Back to skill

Security audit

Deep Research Agent

Security checks for vulnerabilities and agentic risk

Overview

This is mostly a coherent deep-research helper, but its local index script can be tricked into reading Markdown files outside the intended research folder.

Review this before installing if you expect research output to stay purely in chat. Avoid using untrusted report slugs, and prefer a patched version that validates slugs and confines all report file access to ~/research.

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/research.py:153
Finding

Path Traversal Permits Out-of-Scope Markdown File Access

Content
View full analysis

Vulnerability Details

File Location: scripts/research.py, lines 153-162; related unsafe path construction at lines 61-77
Vulnerability Type: Path traversal and insufficient input validation
Risk Level: Medium

Vulnerable Code

python
def cmd_cite(args):
    report_path = RESEARCH_DIR / f"{args.slug}.md"
    if not report_path.exists():
        print(f"Report file not found: {report_path}")
        sys.exit(1)

    content = report_path.read_text(encoding="utf-8")

    # Extract citation lines (lines starting with [n])
    citations = re.findall(r"^\[(\d+)\]\s+(.+)$", content, re.MULTILINE)

The same unvalidated path construction is used by the add command:

python
def cmd_add(args):
    index = load_index()
    now = datetime.now(timezone.utc).isoformat()
    slug = args.slug

    report_path = RESEARCH_DIR / f"{slug}.md"
    entry = {
        "title": args.title,
        "slug": slug,
        "depth": args.depth or "standard",
        "confidence": args.confidence or "MEDIUM",
        "tags": [t.strip() for t in args.tags.split(",")] if args.tags else [],
        "created": now,
        "updated": now,
        "file": str(report_path),
        "sources_count": 0,
        "related": [],
    }

    # Count sources if report file already exists
    if report_path.exists():
        content = report_path.read_text(encoding="utf-8")
        sources = re.findall(r"^\[(\d+)\]", content, re.MULTILINE)
        entry["sources_count"] = len(sources)

Technical Analysis

The slug argument is accepted directly from the command line and interpolated into a filesystem path without validation. The code assumes that joining the supplied value with RESEARCH_DIR confines access to ~/research, but pathlib does not enforce that boundary.

A slug containing parent-directory components such as ../ can escape the research directory. An absolute slug can ...[truncated 2032 chars]

Remediation
View remediation

Remediation Suggestions

  1. Enforce a strict slug allowlist before performing any filesystem operation. For example:

    python
    SLUG_PATTERN = re.compile(r"^[A-Za-z0-9][A-Za-z0-9_-]*$")
    
    def validate_slug(slug: str) -> str:
        if not SLUG_PATTERN.fullmatch(slug):
            raise ValueError(
                "Invalid slug: use only letters, digits, underscores, and hyphens"
            )
        return slug
    
  2. Resolve and verify the final path defensively:

    python
    def report_path_for(slug: str) -> Path:
        slug = validate_slug(slug)
        base = RESEARCH_DIR.resolve()
        candidate = (base / f"{slug}.md").resolve()
    
        if candidate.parent != base:
            raise ValueError("Report path escapes the research directory")
    
        return candidate
    
  3. Use the centralized helper in both cmd_cite and cmd_add, as well as in any future command that derives a path from a slug.

  4. Explicitly reject absolute paths, path separators, . components, and .. components even if an allowlist is introduced.

  5. Handle invalid paths and decoding failures with controlled error messages rather than exposing resolved external paths or Python tracebacks.

  6. Consider limiting input file size before calling read_text() to reduce denial-of-service risk from unexpectedly large files.

  7. Add regression tests covering:

    • Valid alphanumeric, hyphenated, and underscored slugs.
    • Parent traversal using ../.
    • Absolute paths.
    • Nested path separators.
    • Symbolic-link boundary cases.
    • Files outside RESEARCH_DIR.
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • 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 (3)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

There is a substantial mismatch between the advertised behavior of a multi-source web research agent and the detected behavior centered on local filesystem/index management. This kind of misrepresentation is dangerous because users and orchestration layers may grant trust, invoke the skill for network research, or overlook local state mutation that is unrelated to the declared purpose, creating opportunities for covert data storage or unintended file operations.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill appears to invoke local file read/write behavior but does not declare any explicit tool scope or permission boundary. In a research-oriented skill, undeclared filesystem capabilities increase the risk of silent persistence, unintended access to user data, or writes outside the user's expectations, especially because the instructions later direct saving reports and maintaining an index under the home directory.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The instructions to save reports to ~/research and use a local research.py index script introduce persistence and local state management that are not justified by a research-only skill description. Even if the path is nominally benign, this broadens the skill's authority from answering questions to creating and mutating local files, which can surprise users and create confidentiality, integrity, and auditability issues.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.