Back to skill

Security audit

DEPRECATED - Bobo Context Cleanup

Security checks for vulnerabilities and agentic risk

Overview

The skill has a coherent cleanup purpose, but its script contains real unsafe JSON-generation and archive-overwrite risks that can affect workspace files or execute code in crafted environments.

Install only after the script is fixed to safely serialize JSON data, remove suppressed JSON errors, declare node if JSON output remains supported, and prevent archive filename collisions. Until then, avoid --json and avoid running archive on workspaces containing untrusted or duplicate-named memory files.

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

T09 · Insecure Skill Coding Practices

Error
Location
cleanup.sh:75
Finding
Arbitrary JavaScript Execution Through an Unescaped Workspace Path<![CDATA[ ## Vulnerability Details **File Location**: `cleanup.sh:75-77` **Vulnerability Type**: JavaScript source injection **Risk Level**: High ### Vulnerable Code ```bash if [[ "$JSON_OUT" -eq 1 ]]; then node -e "console.log(JSON.stringify({mode:'analyze',workspace:'$WORKSPACE',memory:{files:$file_count,lines:$total_lines,archived:$archive_count},agentsLines:$agents_lines,specsCount:$specs_count},null,2))" return fi ``` ### Technical Analysis The script interpolates the attacker-influenced `WORKSPACE` environment variable directly into JavaScript source passed to `node -e`. Although the shell quotes the overall argument, it does not encode the value for use inside a JavaScript single-quoted string. A workspace path containing a single quote and additional JavaScript syntax can terminate the intended string and inject statements into the generated program. Node.js then evaluates the resulting source rather than treating the workspace path exclusively as data. This is a source-code injection issue, not ordinary shell argument injection. Shell quoting around the `node -e` argument does not prevent the expanded value from changing the JavaScript program. ### Attack Path 1. An attacker supplies or influences the `WORKSPACE` environment variable, such as through an execution wrapper, automation configuration, or attacker-controlled workspace path. 2. The corresponding `memory` directory and expected files are prepared so that the analysis reaches its JSON output branch. 3. The victim or agent invokes: ```bash ./cleanup.sh analyze --json ``` 4. The malicious workspace value is inserted between JavaScript single quotes without escaping. 5. The injected JavaScript is evaluated by Node.js. 6. The payload can invoke APIs such as `child_process` to execute operating-system commands. ### Impact Assessment Successful exploitation provides arbitrary code execution with the privileges of the user running the skill. The payload could read or modify ...[truncated 295 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Never interpolate workspace paths into executable JavaScript source. Pass the workspace as a data argument or environment variable and read it from Node.js: ```bash WORKSPACE_VALUE="$WORKSPACE" \ FILE_COUNT="$file_count" \ TOTAL_LINES="$total_lines" \ ARCHIVE_COUNT="$archive_count" \ AGENTS_LINES="$agents_lines" \ SPECS_COUNT="$specs_count" \ node -e ' const result = { mode: "analyze", workspace: process.env.WORKSPACE_VALUE, memory: { files: Number(process.env.FILE_COUNT), lines: Number(process.env.TOTAL_LINES), archived: Number(process.env.ARCHIVE_COUNT) }, agentsLines: Number(process.env.AGENTS_LINES), specsCount: Number(process.env.SPECS_COUNT) }; console.log(JSON.stringify(result, null, 2)); ' ``` Alternatively, use `jq` with `--arg` and `--argjson`, which safely distinguishes strings from numeric values. Add regression tests covering paths containing quotes, backslashes, newlines, Unicode characters, and JavaScript metacharacters. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
cleanup.sh:106
Finding
Arbitrary JavaScript Execution Through Unescaped Memory Filenames<![CDATA[ ## Vulnerability Details **File Location**: `cleanup.sh:106-108` **Vulnerability Type**: JavaScript source injection **Risk Level**: High ### Vulnerable Code ```bash if [[ "$JSON_OUT" -eq 1 ]]; then node -e "console.log(JSON.stringify({mode:'plan',cutoff:'$CUTOFF_DATE',lowValue:${#low_list[@]},archiveCandidates:${#medium_list[@]},lowFiles:${#low_list[@]}?${low_list[@]/#/'"'}:[],archiveFiles:${#medium_list[@]}?${medium_list[@]/#/'"'}:[]},null,2))" 2>/dev/null || true fi ``` ### Technical Analysis Paths collected from `$WORKSPACE/memory` are expanded directly into source code passed to `node -e`. The `${low_list[@]}` and `${medium_list[@]}` values are not serialized as JSON strings and are not escaped for JavaScript syntax. A crafted Markdown filename containing quotes, delimiters, or JavaScript expressions can alter the generated program. Because Node.js evaluates the generated string as source, a malicious filename can potentially execute arbitrary JavaScript. The array-generation expression is also structurally unreliable for ordinary paths, particularly when multiple filenames or filenames containing whitespace and special characters are present. Redirecting standard error to `/dev/null` and appending `|| true` suppresses syntax and runtime failures, making attacks and malformed output harder to detect. ### Attack Path 1. An attacker gains the ability to place or rename a Markdown file beneath the workspace's `memory` directory. 2. The filename is selected so that the file is classified as low-value or as an archive candidate and contains JavaScript syntax that escapes the intended generated expression. 3. The victim or agent invokes: ```bash ./cleanup.sh plan --json ``` 4. `collect_files` returns the attacker-controlled path. 5. The path is stored in `low_list` or `medium_list`. 6. The path is interpolated directly into the `node -e` source. 7. Node.js evaluates the injected statements, potentially including operating-system c ...[truncated 605 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Do not construct JavaScript or JSON source through shell interpolation. Serialize each path as data using a tool that performs correct escaping. A hardened design should: 1. Pass filenames through standard input as NUL-delimited records or through a safely generated JSON document. 2. Parse those records inside a fixed Node.js program whose source contains no interpolated filenames. 3. Use `JSON.stringify` only after paths have been received as data. 4. Remove `2>/dev/null || true`; JSON-generation failures should produce a nonzero exit status and a visible diagnostic. 5. Add tests for filenames containing spaces, quotes, backslashes, newlines, shell metacharacters, and JavaScript syntax. For example, the shell can emit NUL-delimited arrays to a fixed helper program, or the implementation can use `jq --args`/`jq --rawfile` to build arrays without evaluating path contents as code. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
cleanup.sh:167
Finding
Archive Filename Collisions Can Overwrite Existing Records<![CDATA[ ## Vulnerability Details **File Location**: `cleanup.sh:167-170` **Vulnerability Type**: Unsafe archive destination handling and data overwrite **Risk Level**: Medium ### Vulnerable Code ```bash local moved=0 for file in "${candidates[@]}"; do mv "$file" "$ARCHIVE_DIR/" moved=$((moved+1)) done ``` ### Technical Analysis Every candidate is moved into the same flat archive directory using only its basename. The implementation neither preserves the source file's relative directory structure nor checks whether the destination already exists. Consequently, the following collision cases are possible: - Two eligible files in different subdirectories have the same basename. - A newly archived file has the same basename as a file already present in `memory/archive`. On common Unix implementations, `mv` replaces an existing writable destination file without requesting confirmation. The archive operation can therefore destroy a previous record even though the documented behavior presents archiving as a non-destructive alternative to deletion. Quoting `"$file"` correctly prevents word splitting, but it does not prevent destination collisions. ### Attack Path 1. An attacker or ordinary workspace operation creates two eligible Markdown files in different memory subdirectories with the same basename, or creates a candidate whose basename matches an existing archived record. 2. The user reviews the candidate list and confirms the archive operation, or invokes it with `--yes`. 3. The first file is moved into `memory/archive`. 4. A later `mv` targets the same destination pathname. 5. The existing archived file is replaced by the later source file. 6. The overwritten content is no longer recoverable through this archive mechanism. ### Impact Assessment Exploitation or accidental triggering can cause permanent loss of memory records accessible to the invoking user. The effect is limited to colliding files moved into the configured archive directory, but ...[truncated 204 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Preserve each file's path relative to `MEMORY_DIR` and create matching subdirectories beneath `ARCHIVE_DIR`. Before moving any file, calculate all destination paths and abort the entire operation if a duplicate or pre-existing destination is detected. At minimum, use no-clobber behavior where supported: ```bash mv --no-clobber -- "$file" "$destination" ``` For portability and stronger guarantees: 1. Derive and validate the relative source path. 2. Reject relative paths that escape the intended directory. 3. Create the corresponding archive subdirectory. 4. Test whether the destination already exists. 5. Abort rather than overwrite when a collision is found. 6. Display all source-to-destination mappings before confirmation. 7. Update the moved counter only after confirming that the move succeeded. 8. Consider adding a unique timestamp or content-derived suffix when preserving the relative hierarchy is not feasible. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (2)

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
The skill body is written in Chinese and does not indicate that users may request another language or that the skill is intended only for a Chinese-speaking or region-specific context. This can violate language/locale policy because it implicitly constrains interaction language without opt-in.

Natural-Language Policy Violations

Low
Confidence
95% confidence
Finding
This shell script emits user-facing status messages entirely in Chinese, including the main analysis report headings and labels. The file does not indicate that the skill is region-specific or provide any user opt-in for language selection, which is a natural-language locale policy concern.

Static analysis

No suspicious patterns detected.