Back to skill

Security audit

Markdown Docs Full-Text Search

Security checks for vulnerabilities and agentic risk

Overview

This is a local Markdown documentation search/indexing skill whose content retrieval behavior is disclosed and purpose-aligned, with no hidden network, credential, persistence, or destructive behavior found.

Install only if you want an agent to index and search Markdown documentation you choose. Do not point it at directories containing secrets or private notes unless you are comfortable with snippets, file paths, and optional full content being returned in search results; avoid regex mode with untrusted patterns.

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 (1)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/search_docs.py:95
Finding
Regular Expression Denial of Service Through Unrestricted Search Patterns<![CDATA[ ## Vulnerability Details **File Location**: `scripts/search_docs.py`, lines 95–108 **Vulnerability Type**: Regular Expression Denial of Service (ReDoS) **Risk Level**: Medium ### Vulnerable Code ```python def regex_search(pattern: str, text: str) -> List[Tuple[int, str]]: """Search for regex matches, returning matching lines.""" try: regex = re.compile(pattern, re.IGNORECASE) except re.error as e: print(f"Invalid regex pattern: {e}", file=sys.stderr) return [] lines = text.split('\n') matches = [] for i, line in enumerate(lines): if regex.search(line): matches.append((i + 1, line.strip())) ``` The pattern originates from the positional `query` command-line argument and is passed to this function when the user selects `--mode regex`: ```python search_fn = keyword_search if mode == "keyword" else regex_search ... matches = search_fn(query, article.content) ``` ### Technical Analysis The application compiles and evaluates an unrestricted, user-supplied regular expression using Python's backtracking `re` engine. Although malformed expressions are caught, syntactically valid patterns with nested or ambiguous quantifiers can require exponential processing time on crafted input. For example, a pattern such as: ```text (a+)+$ ``` can cause catastrophic backtracking when evaluated against a sufficiently long documentation line consisting of many `a` characters followed by a nonmatching character. There is no pattern-complexity validation, input-length limit, matching timeout, or process-level resource boundary. The risk applies when the query is controlled by an untrusted caller, including cases where an agent invokes the script using search terms taken from an untrusted prompt. Exploitation also requires a sufficiently adverse line in the searched Markdown corpus, whether naturally present or attacker-controlled. ### Attack Path 1. The attacker supplies a pathological r ...[truncated 1297 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Prefer a non-backtracking regular-expression engine such as RE2 for searches involving untrusted patterns. 2. If regex support is not required, remove `--mode regex` and retain literal keyword searching. 3. If using a regex implementation that supports timeouts, enforce a short per-match timeout and treat timeout failures as rejected searches. 4. Reject patterns containing risky constructs such as nested quantifiers, ambiguous alternation under repetition, and excessive repetition depth. Pattern filtering should be defense in depth rather than the sole control. 5. Impose conservative limits on: - Pattern length. - Number of files and lines searched. - Maximum line length passed to the regex engine. - Total search execution time. 6. Run regex searches in an isolated subprocess with CPU and wall-clock limits. Terminate the subprocess when those limits are exceeded. 7. Ensure agent workflows do not automatically place untrusted prompt content into regex mode. Use keyword mode by default and require explicit trusted-user approval for regex searches. 8. Add regression tests using known pathological patterns, including `(a+)+$`, against long near-matching input to verify that execution is rejected or terminated within the configured time limit. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (17)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The reported mismatch between promised database-backed ranking and actual regex/simple-match behavior is particularly risky in a documentation research context because users may treat results as authoritative and comprehensive. Misrepresented search semantics can hide relevant documents, distort prioritization, and undermine the reliability of citations used in reports or security assessments.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The reported mismatch between promised database-backed ranking and actual regex/simple-match behavior is particularly risky in a documentation research context because users may treat results as authoritative and comprehensive. Misrepresented search semantics can hide relevant documents, distort prioritization, and undermine the reliability of citations used in reports or security assessments.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The reported mismatch between promised database-backed ranking and actual regex/simple-match behavior is particularly risky in a documentation research context because users may treat results as authoritative and comprehensive. Misrepresented search semantics can hide relevant documents, distort prioritization, and undermine the reliability of citations used in reports or security assessments.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The reported mismatch between promised database-backed ranking and actual regex/simple-match behavior is particularly risky in a documentation research context because users may treat results as authoritative and comprehensive. Misrepresented search semantics can hide relevant documents, distort prioritization, and undermine the reliability of citations used in reports or security assessments.

Context Leakage

High
Category
Data Exfiltration
Content
for row in rows:
        title, source_url, content, file_path, relevance = row
        
        # Extract context around first match
        context = extract_context(content, query, context_chars)
        
        results.append({
Confidence
90% confidence
Finding
The function retrieves full article content from the database and derives/snippets it into output context, which can disclose portions of indexed documents to any caller who can issue searches. In a documentation-search skill this is expected functionality, but it still becomes a data-exposure issue if the index contains sensitive, proprietary, or access-restricted Markdown content because the tool performs no authorization or content-sensitivity filtering.

Context Leakage

High
Category
Data Exfiltration
Content
def extract_context(content: str, query: str, max_chars: int) -> str:
    """Extract context snippet around the first query match."""
    # Find first occurrence of any query term
    terms = query.lower().replace('"', '').replace('*', '').replace('AND', '').replace('OR', '').replace('NOT', '').split()
Confidence
75% confidence
Finding
Code or instructions that leak agent conversation context to external services, potentially exposing sensitive user interactions.

Context Leakage

High
Category
Data Exfiltration
Content
def extract_context(content: str, query: str, max_chars: int) -> str:
    """Extract context snippet around the first query match."""
    # Find first occurrence of any query term
    terms = query.lower().replace('"', '').replace('*', '').replace('AND', '').replace('OR', '').replace('NOT', '').split()
Confidence
75% confidence
Finding
Code or instructions that leak agent conversation context to external services, potentially exposing sensitive user interactions.

Context Leakage

High
Category
Data Exfiltration
Content
def get_context(content: str, match_line: int, context_lines: int) -> str:
    """Extract context around a matching line."""
    lines = content.split('\n')
    start = max(0, match_line - context_lines - 1)
    end = min(len(lines), match_line + context_lines)
Confidence
88% confidence
Finding
The context extraction logic intentionally includes surrounding lines for each match, which can disclose adjacent sensitive data unrelated to the user's query. Because this skill searches arbitrary markdown archives, a targeted query could exfiltrate secrets, internal notes, credentials, or regulated data located near a matched term, and the risk increases further with multiple context lines or full-content output.

Lp3

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding
Without declared permissions the skill's intent is opaque and cannot be validated.

Description-Behavior Mismatch

Medium
Confidence
96% confidence
Finding
The manifest describes a full-text search skill using SQLite FTS5 with BM25-ranked results and says indexing is required first, but this code performs direct filesystem traversal over .md files and uses simple keyword or regex matching in memory. It neither uses SQLite nor FTS5 nor BM25 ranking, so the actual behavior is materially different from the capability promised by the skill description.

Intent-Code Divergence

Low
Confidence
80% confidence
Finding
The top-level docstring and argparse description present the tool primarily as an indexing and searching CLI for competitor documentation. However, the code exposes a `get` subcommand that performs article lookup and content display, which is a distinct retrieval capability not reflected in the descriptive text.

Description-Behavior Mismatch

Low
Confidence
76% confidence
Finding
The manifest describes a full-text search skill over indexed Markdown archives, emphasizing BM25-ranked search and citations. This file adds a separate `get` command that locates matching articles and can print full raw content, which is broader than search-oriented retrieval as described in the manifest and module usage text.

Intent-Code Divergence

Low
Confidence
92% confidence
Finding
Lines L039-L041 imply the code is checking whether the database exists and will gracefully report 'Database not found', but sqlite3.connect(db_path) does not serve as a pure existence check and can create or open a database file. This means the inline behavior suggested by the error message/documented intent is inconsistent with what the code actually does.

Description-Behavior Mismatch

Low
Confidence
90% confidence
Finding
The manifest describes a general-purpose Markdown documentation indexing/search skill for structured archives, but this file's module docstring, CLI description, default database name, and examples all explicitly target 'competitor documentation'. That is an intent-level narrowing/mismatch between the declared skill purpose and what the code presents itself as doing.

Intent-Code Divergence

Low
Confidence
98% confidence
Finding
The comment says 'File path lookups use the content column', which contradicts the schema and later DELETE statements that operate directly on the file_path column in the FTS table. This is an active documentation/code contradiction rather than a mere omission.

Intent-Code Divergence

Low
Confidence
91% confidence
Finding
The file docstring and CLI description repeatedly frame the tool as searching 'competitor documentation files,' while the manifest describes a general markdown documentation archive search skill for research and knowledge-base lookup. That intent framing is narrower and different from the manifest, creating a documentation-to-skill mismatch about the skill's actual purpose.

Description-Behavior Mismatch

Low
Confidence
83% confidence
Finding
The function returns full article bodies and local file paths in results, exceeding the citation-oriented search description and potentially exposing more data than intended. In agent contexts, this can leak proprietary document contents and local filesystem structure to downstream consumers, especially when --full or JSON output is used.

Static analysis

No suspicious patterns detected.