Back to skill

Security audit

doc-search

Security checks for vulnerabilities and agentic risk

Overview

This local document search skill is not overtly malicious, but it needs review because it recursively indexes local files, writes persistent index data, and uses unsafe command/path handling.

Install only if you are comfortable letting the agent recursively scan a chosen local document folder and store a persistent local index. Use it on trusted directories, avoid shared or attacker-writable indexes, and prefer a revised version that quotes or avoids shell commands, validates returned paths stay under the docs directory, and clearly documents one index location.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:21
Finding
Shell Command Injection Through Untrusted Workflow Parameters<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:21-58` **Vulnerability Type**: Shell command injection caused by unsafe interpolation of user-controlled values **Risk Level**: High ### Vulnerable Code ```bash ls <docs_dir>/.cache/index.json ``` ```bash python3 ~/.claude/skills/doc-search/scripts/build_index.py <docs_dir> ``` ```bash python3 ~/.claude/skills/doc-search/scripts/search.py "<expanded query>" \ --docs-dir <docs_dir> --topk 5 ``` ```bash grep -ni -e "keyword1" -e "keyword2" /path/to/doc.md ``` ### Technical Analysis The Skill instructs the Agent to construct shell commands by substituting document directories, expanded queries, original keywords, and result paths directly into command templates. The directory and result-path placeholders are unquoted. Consequently, whitespace, command separators, redirection operators, pipelines, and other shell metacharacters in these values can alter the command's structure. Although the expanded query and grep patterns are shown inside double quotes, double quotes do not neutralize shell command substitution. Constructs such as `$(command)` and backtick substitution can still be evaluated by a shell when the Agent builds and executes a command string. Embedded quotation marks may also terminate the intended quoting context if values are interpolated without robust argument-level escaping. The risk applies both to explicitly supplied user input and to filenames originating from a document collection that may not be fully trusted. The Python scripts themselves do not invoke a shell, but the Skill workflow directs the Agent to do so. ### Attack Path 1. An attacker supplies a document directory, search query, keyword, or indexed filename containing shell syntax. 2. The Agent replaces a placeholder in one of the documented Bash templates with that value. 3. The resulting command is passed to a shell rather than executed as a fixed argument array. 4. The shell interprets command substituti ...[truncated 956 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not build shell command strings by interpolating user-controlled values. 2. Invoke the Python scripts through an API that accepts an argument array, for example: ```python subprocess.run( ["python3", script_path, expanded_query, "--docs-dir", docs_dir, "--topk", "5"], shell=False, check=True, ) ``` 3. Use a dedicated file-search API instead of generating a `grep` shell command. If `grep` must be used, pass every pattern and file path as a separate process argument with `shell=False`. 4. Place `--` before file operands so paths beginning with a hyphen cannot be interpreted as options: ```python subprocess.run( ["grep", "-ni", "-e", keyword1, "-e", keyword2, "--", file_path], shell=False, check=False, ) ``` 5. Resolve and validate the document directory before use. Reject paths containing null bytes and ensure that result paths remain within the approved document root. 6. Do not rely on model-generated quoting or escaping. Safe process invocation must be enforced structurally by the implementation. 7. Update `SKILL.md` to explicitly prohibit shell-string execution and require argument-array invocation for all untrusted values. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/search.py:100
Finding
Untrusted Index Metadata Can Redirect File Reads Outside the Document Root<![CDATA[ ## Vulnerability Details **File Location**: `scripts/search.py:100-117`; related workflow at `SKILL.md:53-61` **Vulnerability Type**: Path trust-boundary violation and arbitrary local file read redirection **Risk Level**: Medium ### Vulnerable Code ```python with open(index_file) as f: index_data = json.load(f) query_terms = bigrams(query) results = search(query_terms, index_data, topk) meta = index_data["meta"] output = [] for rel_path, score in results: m = meta.get(rel_path, {}) abs_path = m.get("path", rel_path) output.append({ "path": abs_path, "rel": rel_path, "score": round(score, 3), "title": m.get("title", ""), "summary": m.get("summary", ""), }) ``` The returned path is consumed according to the following Skill instruction: ```text For each result file, grep with the original keywords: ```bash grep -ni -e "keyword1" -e "keyword2" /path/to/doc.md ``` Report file path + matching line numbers to the user. ``` ### Technical Analysis The search implementation parses the selected JSON index without schema validation, integrity verification, ownership checks, or validation that indexed paths belong to the selected document directory. The `meta[*].path` field is copied directly into the search output as an absolute path. The Skill subsequently instructs the Agent to run `grep` on each returned path. Therefore, the index acts as an untrusted source controlling which local files the Agent reads. An attacker who can provide or modify an index can create a valid posting for a chosen query term and associate the matching metadata entry with an absolute path such as a sensitive file outside the approved document root. The search script does not reconstruct the path from a validated relative path and does not call `resolve()` followed by a document-root containment check. The `--index` option also permits selection of a custom index file, increasing the importance of treating all loa ...[truncated 1628 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not trust or return absolute paths stored in index metadata. 2. Require a validated document root when searching, including when a custom index is supplied. 3. Reconstruct each result path exclusively from the indexed relative path: ```python root = Path(docs_dir).expanduser().resolve() candidate = (root / rel_path).resolve() try: candidate.relative_to(root) except ValueError: raise ValueError("Indexed path escapes the document root") ``` 4. Reject absolute `rel_path` values, parent-directory traversal, null bytes, malformed types, and paths that resolve outside the approved root. 5. Decide on and enforce a symlink policy. If symlinks are permitted, containment must be checked after resolution; otherwise, reject symlinked result files. 6. Validate the complete JSON schema before searching. Confirm the expected types and consistency of `meta`, `inv`, `doc_lengths`, `total_docs`, and `avg_length`. 7. Treat malformed or inconsistent indexes as invalid and rebuild them from the document directory rather than continuing with partially trusted content. 8. Consider checking index ownership and permissions when an index resides in a shared or attacker-writable directory. 9. Pass only validated result paths to the precise-location step, and execute `grep` through a non-shell argument array. ]]>
Vulnerability Patterns
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (7)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
Claiming LLM query expansion and grep-based precise matching when the skill actually searches only a prebuilt index creates deceptive expectations about accuracy, privacy, and execution flow. Users may believe the agent is only performing transient search when it may depend on stale indexed data and previous disk writes.

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding
Claiming LLM query expansion and grep-based precise matching when the skill actually searches only a prebuilt index creates deceptive expectations about accuracy, privacy, and execution flow. Users may believe the agent is only performing transient search when it may depend on stale indexed data and previous disk writes.

Natural-Language Policy Violations

Medium
Confidence
92% confidence
Finding
The skill documentation is written in Chinese and does not indicate that users may choose another language or that the skill is intended only for a Chinese-speaking or region-specific audience. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation.

Lp3

Medium
Category
MCP Least Privilege
Confidence
83% confidence
Finding
The skill instructs the agent to read from arbitrary user-specified directories and to write an index file into that directory, but it declares no explicit tool scope or permissions boundary. In an agent environment, missing scope declarations increases the chance of unintended file-system access beyond what users expect, especially because the workflow includes both recursive reads and persistent writes.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The trigger phrases are broad enough to activate on ordinary requests about finding information, which can cause the skill to run in situations where the user did not clearly intend recursive local document scanning. Because this skill can read large directory trees and write an index, overbroad activation increases the risk of unintended privacy exposure and unexpected filesystem modification.

Intent-Code Divergence

Low
Confidence
90% confidence
Finding
The documentation says Step 2 checks for an index in .cache/index.json, while Step 3 says the index is saved to .doc-search/index.json. This inconsistency can cause the agent to rebuild indexes unnecessarily or write data to an unexpected location, which is risky when operating on user directories.

Missing User Warnings

Low
Confidence
88% confidence
Finding
The workflow omits a clear warning that running the skill builds and stores a persistent local search index inside the chosen document directory. This reduces transparency and informed consent, which matters because the skill is not purely read-only and may leave artifacts in sensitive note or wiki repositories.

Static analysis

No suspicious patterns detected.