Back to skill

Security audit

Semantic Cache

Security checks for vulnerabilities and agentic risk

Overview

The skill does what it says, but its shared Redis cache design can expose or overwrite other users' cached prompts and can delete broad cache data without strong safeguards.

Use this only with a dedicated Redis database, isolated namespace, and non-sensitive prompts unless the cache is tenant-aware and access-controlled. Do not run the clear command or stress test against shared or production Redis, and review dependency updates before installation.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (2)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/cache.js:72
Finding

Global Semantic Cache Permits Cross-User Data Disclosure and Cache Poisoning

Content
View full analysis
0) { await redis.expire(key, CACHE_TTL); } console.log(`Cached: "${query.substring(0, 60)}..." -> ${key}`); return key; } ``` ```js async function lookup(query) { const redis = await getRedis(); await ensureIndex(); const vector = await embed(query); const startTime = Date.now(); const results = await redis.ft.search(INDEX_NAME, `*=>[KNN 1 @embedding $BLOB AS score]`, { PARAMS: { BLOB: float32Buffer(vector) }, SORTBY: "score", LIMIT: { from: 0, size: 1 }, RETURN: ["query_text", "response_text", "score", "hit_count", "model", "created_at"], DIALECT: 2, }); const lookupMs = Date.now() - startTime; if (results.total === 0) { return { hit: false, lookupMs }; } const doc = results.documents[0]; const similarity = 1 - parseFloat(doc.value.score); if (similarity < SIMILARITY_THRESHOLD) { return { hit: false, lookupMs, closestSimilarity: similarity, closestQuery: doc.value.query_text, }; } // Increment hit count await redis.hIncrBy(doc.id, "hit_count", 1); return { hit: true, response: doc.value.response_text, similarity, originalQuery: doc.value.query_text, model: doc.value.mo ...[truncated 3017 chars]
Remediation
View remediation
::`. - Store the tenant identifier as a Redis TAG field. - Require a tenant filter in every vector query. 3. Enforce authorization before returning a cached response. The caller must be authorized to read the owner namespace of the selected entry. 4. Restrict cache writes to trusted code paths. Do not expose unrestricted `store` functionality to untrusted callers. 5. Separate caches for workflows with different confidentiality or trust levels. 6. Do not cache secrets, credentials, regulated data, or highly sensitive responses. Add configurable exclusion and redaction rules. 7. Consider encrypting sensitive cached values at rest with tenant-specific keys. 8. Record provenance, owner, model, policy version, and creation context for each entry. 9. Validate cached-response provenance and trust level before using it in downstream automation. 10. Add tests proving that one tenant cannot retrieve or influence another tenant's entries. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/cache.js:187
Finding

Broad Cleanup Commands Can Delete Unrelated Redis Data and Shared Search Indexes

Content
View full analysis
0) { await redis.del(keys); } try { await redis.ft.dropIndex(INDEX_NAME); } catch {} console.log(`Cleared ${keys.length} cached entries and dropped index`); } ``` The stress test performs destructive cleanup automatically at startup: ```js // Clean start const oldKeys = await redis.keys(KEY_PREFIX + "*"); if (oldKeys.length > 0) await redis.del(oldKeys); try { await redis.ft.dropIndex(INDEX_NAME); } catch {} ``` It repeats the operation when the test completes: ```js // Cleanup const allKeys = await redis.keys(KEY_PREFIX + "*"); if (allKeys.length > 0) await redis.del(allKeys); try { await redis.ft.dropIndex(INDEX_NAME); } catch {} await redis.quit(); ``` ### Technical Analysis The application uses the generic prefix `cache:` and the fixed index name `idx:semantic_cache`. The `clear` command retrieves every Redis key matching `cache:*` and deletes all of them. It then drops the fixed search index. Neither operation verifies that the matched keys or index belong exclusively to this project. If the configured Redis database is shared, unrelated applications may legitimately use the same generic prefix or index name. Those applications' data can therefore be deleted. The stress-test script is more hazardous because it performs the same destructive operations automatically both before and after testing. It has no confirmation prompt, test-environment assertion, dedicated database requirement, or production safety guard. The use of Redis `KEYS` also performs a blocking full keyspace scan. On a large production database, this can cause latenc ...[truncated 1787 chars]
Remediation
View remediation
:` - A namespace containing application, environment, and tenant identifiers. 2. Generate a deployment-specific index name instead of using the global fixed name `idx:semantic_cache`. 3. Track resources created by the application and delete only resources whose ownership has been verified. 4. Replace blocking `KEYS` calls with cursor-based `SCAN` operations and bounded batches. 5. Require explicit confirmation before destructive CLI operations. For automation, require a deliberate flag such as `--confirm-clear`. 6. Require the stress test to use a dedicated Redis database or disposable Redis instance. 7. Add a mandatory test-only guard, such as `SEMANTIC_CACHE_TEST_MODE=true`, and refuse to run if the Redis URL appears to target an approved production environment. 8. Use Redis ACLs to restrict the application account to its dedicated key prefix and index operations where supported. 9. Avoid silently swallowing index-deletion errors; report failures so operators can understand the resulting state. 10. Document that `clear` and `stress-test.js` are destructive, and add integration tests confirming that unrelated prefixes and indexes remain untouched. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (20)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 41)May include surrounding context.

md
node scripts/cache.js store "What is the capital of France?" "The capital of France is Paris."

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 46)May include surrounding context.

md
node scripts/cache.js store "What is the capital of France?" "The capital of France is Paris."

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 51)May include surrounding context.

md
node scripts/cache.js store "What is the capital of France?" "The capital of France is Paris."

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 56)May include surrounding context.

md
node scripts/cache.js store "What is the capital of France?" "The capital of France is Paris."

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 61)May include surrounding context.

md
node scripts/cache.js store "What is the capital of France?" "The capital of France is Paris."

Known Vulnerable Dependency: form-data==4.0.5 — 1 advisory(ies): CVE-2026-12143 (form-data: CRLF injection in form-data via unescaped multipart field names and f)

High
Category
Supply Chain
Confidence
95% confidence
Finding

The lockfile pins form-data to 4.0.5, and the supplied advisory indicates a HIGH-severity CRLF injection issue in multipart field names. This is a real supply-chain risk if any code path constructs multipart requests using untrusted field names or filenames through this dependency chain; in this skill, form-data is transitive via the OpenAI SDK, so exploitability depends on whether attacker-controlled multipart metadata is ever passed through, making the package presence a true vulnerability but with context-dependent reachability.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill declares required environment variables and executable capabilities but does not define an explicit tool scope such as permissions or allowed-tools. That creates ambiguity about what the skill is allowed to access at runtime and can lead to over-broad execution or unintended access to sensitive resources like REDIS_URL and OPENAI_API_KEY.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The activation text is broad enough to match many normal requests involving AI responses, caching, cost reduction, or performance. Over-broad activation can cause the skill to run unexpectedly in contexts the user did not intend, increasing the chance of unnecessary data transmission, cache pollution, or destructive operations being suggested or invoked.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The skill states that user queries are sent to OpenAI for embeddings and, on cache miss, for LLM completion, but it does not clearly warn users that their prompts may leave the local environment. This is dangerous because sensitive prompts, internal data, or regulated information could be disclosed to an external provider without informed consent.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The documented clear command deletes all cached entries but is presented without any warning, confirmation step, or scope limitation. This is dangerous because a user or agent could trigger irreversible cache loss, causing denial of service for the cache layer, loss of operational data, and unnecessary downstream API cost spikes.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The script sends raw user prompts to OpenAI for embeddings and completions and stores prompts and responses in Redis without any built-in consent notice, redaction, or sensitivity checks. In environments where prompts may contain secrets, personal data, or proprietary content, this creates a real data exposure risk to third-party services and persistent storage.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The manifest describes a semantic cache for LLM API calls that checks cache state and stores results for future reuse. However, the query function does not just support caching: it directly sends user prompts to OpenAI chat completions on a miss and returns generated answers, making this script an LLM client as well as a cache layer.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The 'clear' command deletes all cache entries matching the prefix and drops the search index immediately, with no confirmation, dry-run, or safety interlock. An accidental invocation, script misuse, or automation mistake can cause complete loss of cached data and service disruption.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The embed function sends input text to OpenAI's embeddings API, which transmits query data off-box to an external service. The file has no user-facing warning, disclosure, or prompt indicating that provided text will be sent to a third-party API.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The script performs unconditional destructive cleanup by deleting all Redis keys with the cache prefix and dropping the shared search index on startup. In a non-isolated or misconfigured Redis instance, this can erase legitimate cache data or break other applications using the same prefix/index, causing denial of service or data loss.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
91% confidence
Finding

The dependency uses a caret range, which allows automatic installation of newer minor and patch versions of the Redis client. That increases supply-chain risk and can also introduce unexpected behavior or vulnerable releases without explicit review, even though this file itself does not contain a direct exploit.

Content

Scanner excerpt · package.json (reported line 11)May include surrounding context.

json
"test": "node scripts/cache.js test"
  },
  "dependencies": {
    "redis": "^4.7.0",
    "openai": "^4.80.0"
  }
}

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
91% confidence
Finding

The OpenAI package is specified with a caret range, permitting unreviewed minor and patch updates at install time. In a security-sensitive skill that brokers LLM requests and cached responses, this creates avoidable supply-chain exposure and reproducibility issues if an upstream release is compromised or breaking.

Content

Scanner excerpt · package.json (reported line 12)May include surrounding context.

json
},
  "dependencies": {
    "redis": "^4.7.0",
    "openai": "^4.80.0"
  }
}

Context-Inappropriate Capability

Low
Category
Not specified by scanner
Confidence
83% confidence
Finding

The manifest focuses on checking cache before expensive LLM calls and storing responses for reuse. The clear command adds a bulk deletion/admin capability that removes all cached entries and drops the search index, which is not part of the stated end-user purpose of semantic caching in workflows.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
83% confidence
Finding

The script reads REDIS_URL and OPENAI_API_KEY from environment variables, including an API credential used for outbound requests. There is no visible user-facing notice that the script depends on and consumes these sensitive environment values.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
97% confidence
Finding

The section labeled 'Test 7: Hit count tracking' suggests the code is validating hit-count updates, but lookup() does not modify Redis records and the test only reads existing hit_count values. The inline note at L144 acknowledges this, making the section's stated intent inconsistent with the code's actual behavior.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.