T09 · Insecure Skill Coding Practices
- Location
scripts/topology.js:20- Finding
Symlink-Based Filesystem Containment Bypass
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This skill transparently records short local conversation summaries as decision trees, with no evidence of network exfiltration or deceptive behavior, but it does persist data automatically.
Install only if you are comfortable with the agent automatically saving short conversation-derived topics, summaries, rejection reasons, and concepts to local JSON and Markdown files. Use a private storage directory, avoid directories writable by other users, disable always-on mode if you want explicit invocation only, and periodically review or delete stored trees if conversations may be sensitive.
scripts/topology.js:20Symlink-Based Filesystem Containment Bypass
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).
Clean up after testing:
rm -rf /tmp/test-trees
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).
Clean up after testing:
rm -rf /tmp/test-trees
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
**Path containment is enforced at runtime.** At startup, the trees directory is canonicalized via `fs.realpathSync()`. Every user-supplied `file` argument is stripped to its basename with `path.basename()` and then resolved inside the canonical trees directory. This means:
- Absolute paths like `/etc/passwd` are rejected (basename extraction produces `passwd`, which won't exist in trees dir).
- Relative traversal like `../../etc/shadow` is rejected (basename extraction produces `shadow`).
- Symlinks are resolved by `realpathSync` — a symlink inside the trees directory that points outside it would need to already exist on disk, which the script never creates.
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
**Path containment is enforced at runtime.** At startup, the trees directory is canonicalized via `fs.realpathSync()`. Every user-supplied `file` argument is stripped to its basename with `path.basename()` and then resolved inside the canonical trees directory. This means:
- Absolute paths like `/etc/passwd` are rejected (basename extraction produces `passwd`, which won't exist in trees dir).
- Relative traversal like `../../etc/shadow` is rejected (basename extraction produces `shadow`).
- Symlinks are resolved by `realpathSync` — a symlink inside the trees directory that points outside it would need to already exist on disk, which the script never creates.
The `resolveSafePath()` function additionally verifies the resolved path starts with the canonical trees directory prefix before any read or write occurs. If the check fails, the script exits with an error.
The declared purpose emphasizes local JSON tree recording, but the documented behavior also includes cross-tree search, global analysis, concept indexing, and regeneration of companion Markdown files across all trees. This mismatch matters because users may consent to lightweight note persistence without realizing the skill performs broader aggregation and secondary artifact creation, which expands the privacy and data-retention footprint.
The README states that the skill persistently records conversation structure to local JSON files and companion markdown files, but it does not provide a clear user-facing warning about retention, sensitivity of captured content, or consent. Even without network access, always-on local logging can expose sensitive prompts, decisions, and internal reasoning to other local users, backups, indexing tools, or later unintended access.
Documenting the skill as active during every conversation by default materially increases privacy risk because users may not realize continuous recording is occurring across unrelated chats. In this skill's context, the danger is elevated by persistent storage, cross-tree analysis, and companion .md generation, which increase the amount of retained conversational metadata and the chance of secondary exposure.
The skill uses environment-controlled behavior (TOPOLOGY_TREES_DIR) and runtime file operations but does not declare an explicit tool scope or permissions boundary. That makes the actual capability surface broader than what a reviewer can infer from metadata, increasing the risk of unintended file persistence or storage in sensitive locations if the host environment exposes permissive defaults.
The activation criteria are broad enough to trigger during many normal brainstorming or problem-solving conversations, and the skill is marked always: true. In context, that means conversation-derived summaries may be persisted automatically and repeatedly without a narrowly bounded trigger, creating an over-collection/privacy risk even if content length is truncated.
The skill persistently stores conversation-derived data in the background, yet the user-facing description does not prominently warn that summaries, reasons, and concepts are auto-written to disk during ordinary use. This is a meaningful transparency failure because users may reveal sensitive information assuming a normal chat flow, not silent local logging and indexing.
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.
- Score 0.25-0.4 — ambiguous. Ask the user naturally: "This feels related to [topic] we explored on [date]. Continuing that thread, or fresh start?"
- Score < 0.25 — new tree
Never ask the user to pick a tree by ID. If you need to disambiguate, ask naturally in conversation.
## Setup
No suspicious patterns detected.