Back to skill

Security audit

Workspace Hygiene Publish

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does what it says, but its filesystem handling can let a crafted workspace cause reads or writes outside the selected workspace.

Review before installing. Use it only on workspaces you trust, avoid running it automatically on untrusted or shared workspaces, and do not use --fix until symlink containment, backup, and clearer dry-run behavior are added. There is no evidence here of hidden networking or intentional exfiltration, but the filesystem escape risk is material.

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

Error
Location
scripts/hygiene.py:191
Finding

Workspace Symlink Traversal Allows Reads and Writes Outside the Audited Workspace

Content
View full analysis

Vulnerability Details

File Location: scripts/hygiene.py, lines 191-211 and 440-450
Vulnerability Type: Unrestricted symlink following and insufficient filesystem boundary validation
Risk Level: High

Vulnerable Code

python
def append_memory_content(target_path: Path, source_path: Path) -> bool:
    source_text = load_text(source_path)
    if not source_text.strip():
        return False

    target_text = load_text(target_path)
    if source_text.strip() in target_text:
        return False

    if target_text and not target_text.endswith("\n"):
        target_text += "\n"

    if target_text.strip():
        merged = target_text.rstrip() + "\n\n" + source_text.strip() + "\n"
    else:
        merged = source_text.rstrip() + "\n"

    target_path.parent.mkdir(parents=True, exist_ok=True)
    target_path.write_text(merged, encoding="utf-8")
    return True
python
def write_report(result: AuditResult, report_date: dt.date, report_only: bool) -> Path:
    report_dir = result.workspace / "projects" / "system"
    if not report_dir.exists() and not report_only:
        report_dir.mkdir(parents=True, exist_ok=True)
        result.fixes.append("Created report directory: projects/system")
    elif not report_dir.exists():
        report_dir.mkdir(parents=True, exist_ok=True)

    report_path = report_dir / "hygiene-{0}.md".format(report_date.isoformat())
    report_path.write_text(render_report(result, report_date), encoding="utf-8")
    result.report_path = report_path
    return report_path

Technical Analysis

The skill treats the audited workspace as trusted and performs filesystem reads and writes without verifying whether path components or files are symbolic links. Python operations including Path.exists(), Path.is_file(), Path.read_text(), and Path.write_text() ordinarily follow symlinks.

Two exploitable paths result:

  1. `write_report ...[truncated 2968 chars]
Remediation
View remediation

Remediation Suggestions

  1. Treat every audited workspace and all of its contents as untrusted input.
  2. Resolve each source, destination, and parent directory before access, then verify containment beneath the resolved workspace root:
    python
    workspace_root = result.workspace.resolve(strict=True)
    candidate = target_path.resolve(strict=False)
    candidate.relative_to(workspace_root)
    
    Reject the operation if relative_to() raises ValueError.
  3. Explicitly reject symlinks for memory source files, daily target files, projects, projects/system, and report files. Check every existing path component rather than only the final path.
  4. For write operations, use descriptor-based APIs with no-follow semantics such as os.open() with O_NOFOLLOW where supported. Use safe create or replace behavior to reduce time-of-check/time-of-use races.
  5. Open and validate trusted parent-directory descriptors, then perform relative operations through those descriptors where platform support permits.
  6. Do not overwrite an existing report without an explicit option. Consider exclusive file creation or a securely generated filename.
  7. Before --fix reads a memory file, require that it is a regular, non-symlinked file located directly inside the validated memory directory.
  8. Before writing a daily memory file, reject an existing symlink or non-regular file and verify that the resolved parent remains inside the workspace.
  9. Make --report-only genuinely non-mutating, or rename and document the option to clarify that it still writes a report.
  10. Add regression tests using symlinked source files, target files, and parent directories to confirm that all attempted workspace escapes are rejected.
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 (5)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill explicitly describes reading workspace files, scanning memory and project folders, and writing a hygiene report, yet it declares no tool or permission scope. That mismatch makes the skill harder to sandbox or review and can allow broader file access/modification than users or the platform may expect.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The trigger phrases are broad and generic to normal workspace assistance, which increases the chance the skill is invoked in situations where the user only wanted advice rather than an auditing/modifying workflow. Because the skill can scan and potentially alter files, overbroad activation raises the risk of unintended execution on sensitive or unrelated workspaces.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill documents auto-consolidating memory files and writing reports, but it does not prominently warn that it will modify workspace files. Users may reasonably interpret a hygiene audit as read-only, so silent or insufficiently disclosed writes can lead to unexpected data changes, loss of provenance, or accidental alteration of important memory records.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The help text promises 'Audit and report without applying fixes' and implies no workspace modification, but the program still creates directories and writes a report. This documentation/behavior mismatch is dangerous because operators and higher-level agents may select report-only mode expecting safe inspection, yet the tool still performs state-changing actions.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The implementation writes a report file even when --report-only is set, so a mode advertised as non-modifying still changes the workspace. In an automation or auditing context, this can violate read-only expectations, alter evidence, trigger downstream workflows, or fail in environments where the workspace must not be mutated.

Content

No source excerpt is available for this finding.