Back to skill

Security audit

double6-deep-research

Security checks for vulnerabilities and agentic risk

Overview

This research skill is mostly coherent, but its validator has a real path-handling issue that can read or overwrite files outside the intended bundle if run on a crafted directory.

Install only if you are comfortable with a research helper that reads authorized materials and writes a report bundle. Do not run its validator on untrusted or preexisting bundle directories, do not run it with elevated privileges, and check that bundle files such as request_snapshot.md, final_report.md, research_record.json, independent_review.json, and validation_receipt.json are not symlinks before validation.

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/validate_research_bundle.py:1163
Finding

Bundle Symlinks Permit Out-of-Scope File Reads and Arbitrary File Overwrite

Content
View full analysis

Vulnerability Details

File Location: scripts/validate_research_bundle.py:1163-1173, 1372-1379; scripts/research_review_validation.py:397-405
Vulnerability Type: Symlink following and incomplete bundle-boundary enforcement
Risk Level: Medium

Vulnerable Code

python
for filename in REQUIRED_FILES:
    if not (bundle / filename).is_file():
        errors.append(f"missing_required_file:{filename}")
if errors:
    return {
        "status": "error",
        "research_status": "unknown",
        "review_status": "not_reviewed",
        "errors": errors,
        "warnings": warnings,
        "checks": checks,
    }

request = read_text(bundle / "request_snapshot.md", errors)
report = read_text(bundle / "final_report.md", errors)
record = read_json(bundle / "research_record.json", errors)
python
def validate_independent_review(
    bundle: Path,
    question_ids: set[str],
    claim_ids: set[str],
    current_hashes: dict[str, str],
    errors: list[str],
) -> str:
    review_path = bundle / "independent_review.json"
    if not review_path.is_file():
        return "not_reviewed"
    review = read_json(review_path, errors)
python
bundle_dir = args.bundle_dir.resolve()
result = validate_bundle(bundle_dir)
if bundle_dir.is_dir():
    try:
        (bundle_dir / "validation_receipt.json").write_text(
            json.dumps(result, ensure_ascii=False, indent=2) + "\n",
            encoding="utf-8",
        )

Technical Analysis

The validator treats the bundle directory as a filesystem security boundary, but it does not apply that boundary consistently. Python's Path.is_file(), Path.read_text(), file opening, and Path.write_text() follow symbolic links by default.

Consequently, attacker-controlled symlinks can be used for:

  1. Required candidate files such as request_snapshot.md, final_report.md, and research_record.json.
  2. The optional independent_review.json.
  3. The output file validation_receipt.json.

...[truncated 2190 chars]

Remediation
View remediation

Remediation Suggestions

Apply the bundle-boundary policy uniformly to every input and output:

  1. Reject symbolic links for all fixed candidate files

    • Use lstat() or Path.is_symlink() before reading.
    • Require each candidate to be a regular file rather than merely accepting is_file().
  2. Resolve and verify every input path

    • Resolve the bundle root once.
    • Resolve each candidate and review path.
    • Require every resolved path to satisfy resolved_path.is_relative_to(bundle_root).
    • Reject paths that escape the bundle.
  3. Secure the receipt destination

    • Reject an existing validation_receipt.json if it is a symbolic link or non-regular file.
    • Open the destination with no-follow semantics where supported, such as os.open() with O_NOFOLLOW.
    • Write to a newly created temporary regular file inside the verified bundle directory, flush and synchronize it if required, and atomically replace the receipt.
    • Verify the destination again immediately before replacement to reduce time-of-check/time-of-use risk.
  4. Use a shared safe-path helper

    • Centralize regular-file, symlink, resolution, and containment checks.
    • Apply it to required files, independent_review.json, source snapshots, candidate hashing, and receipt output.
  5. Add regression tests

    • Required-file symlink escaping the bundle.
    • Review-file symlink escaping the bundle.
    • Receipt symlink targeting an external file.
    • Nested symlink traversal.
    • Replacement races where practical.

A safe input check should follow this general pattern:

python
def safe_regular_file(bundle_root: Path, relative_name: str) -> Path:
    path = bundle_root / relative_name
    if path.is_symlink():
        raise ValueError("symbolic links are not allowed")
    resolved = path.resolve(strict=True)
    if not resolved.is_relative_to(bundle_root):
        raise ValueError("path escapes bundle")
    if not resolved.is_file():
        raise ValueEr
...[truncated 163 chars]
Vulnerability Patterns
  • 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
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (8)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The description portrays an end-to-end research skill that gathers information from multiple sources and writes outputs to disk. The supplied code chunk instead implements internal validation logic for an existing research bundle: checking local files, validating JSON fields, verifying candidate file hashes, and assessing independent review completeness/consistency. This is materially narrower and different in primary purpose. While validation is mentioned in the description, the declared behavior centers on performing research and generating deliverables, none of which this code does. Therefore the description does not accurately represent this code chunk.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

该描述与代码存在明显的目的和能力错配。代码核心函数 validate_bundle 对研究交付包进行静态校验,检查必需文件、JSON schema、问题/来源/关键声明映射、引用完整性、研究轮次顺序、覆盖要求、快照 SHA-256,以及独立审阅结果;main 仅将验证结果输出并写入 validation_receipt.json。虽然描述提到“运行 Python validator”,这一点与代码部分吻合,但描述的主体能力是‘使用搜索、网页和本地材料完成多来源研究,并写入报告和记录’,而这段代码完全没有实现这些研究或内容生成行为。因此其主要目的与实际行为 materially different,应判定为 mismatch。

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
85% confidence
Finding

The skill explicitly instructs use of local file reads, file writes, and web/search access, but it does not declare an explicit tool scope such as permissions or allowed-tools. This creates an authorization ambiguity: a host may grant broader capabilities than intended, or reviewers may be unable to verify whether the requested access is appropriately constrained to the research bundle and user-authorized sources.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The title and the entire policy text are written exclusively in Chinese, establishing a language constraint for the skill content with no indication that users may choose another language. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

This markdown file is written as prescriptive skill guidance entirely in Chinese and specifies exact Chinese section titles and wording conventions for required report artifacts. Because the document does not offer user opt-in or an alternative language path, it creates a language/locale constraint that can violate organizational policy on user language choice.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The top-level natural-language description is entirely in Chinese and does not offer any language choice or explain a region-specific requirement. Under the policy, forcing a specific language without user opt-in is a locale/language policy violation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This code file contains a top-level docstring entirely in Chinese, establishing a Chinese-only natural-language interface for the skill. Under the policy rule, forcing a specific language without offering user choice or documenting a justified locale constraint is a natural-language policy violation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The returned result includes a user-facing scope_note string written only in Chinese, with no mechanism for the user to select another language. This imposes a locale choice on all users and matches the rule's language-policy violation criteria.

Content

No source excerpt is available for this finding.