Back to skill

Security audit

Milestone

Security checks for vulnerabilities and agentic risk

Overview

Milestone appears to be a local-only logging CLI, but it persistently stores user input in plaintext and has misleading or broken commands that warrant review before installation.

Install only if you specifically want a local activity logger and are comfortable with milestone entries being kept in plaintext under ~/.local/share/milestone. Do not enter passwords, tokens, private customer data, or sensitive prompts. Expect command behavior to be unreliable until the duplicate dispatch and Bash local-scope bugs are fixed, and review file permissions before storing private notes.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/script.sh:6
Finding

Local activity data is created without explicit restrictive permissions

Content
View full analysis
> "$DATA_DIR/history.log"; } ``` Representative entry write: ```bash run) shift if [ $# -eq 0 ]; then echo "Recent run entries:" tail -20 "$DATA_DIR/run.log" 2>/dev/null || echo " No entries yet. Use: milestone run " else local input="$*" local ts=$(date '+%Y-%m-%d %H:%M') echo "$ts|$input" >> "$DATA_DIR/run.log" local total=$(wc -l < "$DATA_DIR/run.log") echo " [Milestone] run: $input" echo " Saved. Total run entries: $total" _log "run" "$input" ``` Equivalent plaintext writes appear in the other entry-command handlers and in export generation. ### Technical Analysis The script creates `~/.local/share/milestone` with `mkdir -p` and creates logs through shell redirection without first setting a restrictive `umask` or explicitly applying secure modes. The resulting permissions therefore depend on the environment from which the script is launched. Milestone inputs are arbitrary user-provided text and may include private report, configuration, analysis, or project information. The same input is written both to a command-specific log and to `history.log`. If the process has a permissive `umask`, these files may be readable by unintended local accounts. The files are plaintext and are not encrypted. This does not grant remote access or elevated privileges. Exploitation requires an attacker who already has local access and sufficient permission to traverse the user's home directory and read the resulting files. ### Attack P ...[truncated 785 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/script.sh:54
Finding

JSON and CSV export fields are emitted without format-specific escaping

Content
View full analysis
"$out" local first=1 for f in "$DATA_DIR"/*.log; do [ -f "$f" ] || continue local name=$(basename "$f" .log) while IFS='|' read -r ts val; do [ $first -eq 1 ] && first=0 || echo "," >> "$out" printf ' {"type":"%s","time":"%s","value":"%s"}' "$name" "$ts" "$val" >> "$out" done < "$f" done echo "\n]" >> "$out" ;; csv) echo "type,time,value" > "$out" for f in "$DATA_DIR"/*.log; do [ -f "$f" ] || continue local name=$(basename "$f" .log) while IFS='|' read -r ts val; do echo "$name,$ts,$val" >> "$out"; done < "$f" done ;; txt) echo "=== Milestone Export ===" > "$out" for f in "$DATA_DIR"/*.log; do [ -f "$f" ] || continue echo "--- $(basename "$f" .log) ---" >> "$out" cat "$f" >> "$out" done ;; *) echo "Formats: json, csv, txt"; return 1 ;; esac echo "Exported to $out ($(wc -c < "$out") bytes)" } ``` ### Technical Analysis The JSON exporter interpolates log-derived values directly inside double-quoted JSON strings. It does not escape quotation marks, backslashes, control characters, or embedded newlines. A crafted entry can therefore make the output invalid or alter its apparent JSON structure. The CSV exporter similarly concatenates fields with commas without RFC 4180 quoting. Commas, quotation marks, and newlines in an entry can cre ...[truncated 1800 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Note
Location
scripts/script.sh:231
Finding

Duplicate command labels make documented export and health-status operations unreachable

Content
View full analysis
/dev/null || echo " No entries yet. Use: milestone export " else local input="$*" local ts=$(date '+%Y-%m-%d %H:%M') echo "$ts|$input" >> "$DATA_DIR/export.log" local total=$(wc -l < "$DATA_DIR/export.log") echo " [Milestone] export: $input" echo " Saved. Total export entries: $total" _log "export" "$input" fi ;; ``` Later dispatch labels attempt to invoke the documented implementations: ```bash stats) _stats ;; export) shift; _export "$@" ;; search) shift; _search "$@" ;; recent) _recent ;; status) _status ;; ``` An earlier `status)` entry handler similarly appears before the later `status) _status ;;` label. ### Technical Analysis A Bash `case` statement executes the first matching pattern. Because an `export)` branch appears before `export) shift; _export "$@" ;;`, the `_export` call cannot be reached through the normal command-line interface. The same problem applies to the health-status implementation: the earlier `status)` branch captures the command before `_status` can execute. As a result, `milestone export json` is treated as an ordinary entry and does not produce the documented backup. A user may see a “Saved” message referring to the logged entry and incorrectly infer that an export was generated. Likewise, `milestone status` displays recent status entries rather than the documented health check. ### Attack Path 1. A user follows the documented instructions and runs `mil ...[truncated 801 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Note
Location
scripts/script.sh:133
Finding

Top-level use of local terminates entry commands before data is saved

Content
View full analysis
/dev/null || echo " No entries yet. Use: milestone run " else local input="$*" local ts=$(date '+%Y-%m-%d %H:%M') echo "$ts|$input" >> "$DATA_DIR/run.log" local total=$(wc -l < "$DATA_DIR/run.log") echo " [Milestone] run: $input" echo " Saved. Total run entries: $total" _log "run" "$input" fi ``` The same top-level `local input`, `local ts`, and `local total` pattern is repeated in the `check`, `convert`, `analyze`, `generate`, `preview`, `batch`, `compare`, `export`, `config`, `status`, and `report` handlers. ### Technical Analysis In Bash, `local` is valid only inside a function. These declarations execute in the script's top-level `case` statement rather than inside a function. When an entry command receives an argument, Bash reports that `local` can only be used in a function. The script enables `set -e` at line 4, so this error terminates execution immediately. The command exits before the intended log write and history update. This is a deterministic denial of core functionality rather than a privilege-escalation issue. ### Attack Path 1. A user invokes an advertised command with input, for example `milestone run "release completed"`. 2. Execution enters the argument-bearing branch. 3. Bash evaluates `local input="$*"` at top level. 4. The `local` builtin returns an error. 5. Because `set -e` is active, the script exits immediately. 6. The entry is not saved, despite the command being documented as a logging operation. An attacker who can influenc ...[truncated 488 chars]
Remediation
View remediation
> "$DATA_DIR/run.log" total=$(wc -l < "$DATA_DIR/run.log") _log "run" "$input" } ``` 2. Alternatively, remove `local` from assignments that intentionally remain at script scope. 3. Separate declaration from command substitution so failures are not masked: ```bash local ts ts=$(date '+%Y-%m-%d %H:%M') ``` 4. Consolidate the repeated handlers into a validated generic logging function to reduce duplicated defects. 5. Add smoke tests that invoke every advertised command both with and without arguments. 6. Verify exit codes and confirm that successful commands actually create the expected records. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (9)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The skill presents itself as a generic utility tool, but the documented behavior includes persistent local logging, history retention, search over stored activity, and export of accumulated data. This is a transparency and consent problem: users may invoke the skill without realizing their inputs and actions are being stored and later retrievable, which can expose sensitive prompts, filenames, or operational context.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The help text claims 'export ' performs data export, but the first matching case label for export instead logs the supplied argument as user input, making the real exporter unreachable. This is dangerous because users may pass filenames, tokens, or structured data expecting export behavior, and the script will silently retain that sensitive input instead.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The help text says 'status' performs a health check, but the earlier duplicate status case logs the provided argument and prevents the actual _status handler from being reached. This misleading behavior can cause users to expose environment details or troubleshooting data that is then written to disk rather than used transiently for status reporting.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The skill advertises automatic history and activity logging without a prominent warning that user actions and possibly user-supplied content will be stored on disk. In a CLI/logging context, this can capture sensitive operational data locally and make it available to other local users, backups, or later exfiltration if the machine is compromised.

Content

No source excerpt is available for this finding.

Ssd 3

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The tool is designed to persist and replay arbitrary user inputs across multiple command logs and export formats, creating a local archive of sensitive material with broad retrieval options. Because the skill description suggests a focused utility rather than a logging/archive system, this behavior is more dangerous: users are less likely to expect that their inputs will be retained and re-exposed.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The script presents itself as a focused Milestone utility but actually implements a general-purpose activity logger with multiple commands and export capabilities. This mismatch is dangerous because users may provide sensitive operational input believing commands perform utility functions, while the tool silently persists that data for later retrieval and export.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

Commands such as convert, analyze, generate, and compare are advertised as functional operations, but the implementation only records raw user input into per-command log files. This deceptive behavior can capture secrets, proprietary text, or sensitive prompts entered by users who reasonably expect processing rather than surveillance-style retention.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The script persistently stores arbitrary user-provided inputs without any warning about retention, later redisplay, or export. In a skill context, users commonly paste task content, identifiers, file paths, or secrets into commands, so undisclosed logging materially increases privacy and data-leak risk.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The skill documents export to JSON, CSV, or text but does not warn that exported files leave the protected default data directory and may be copied, shared, or stored in less secure locations. This increases the chance of accidental disclosure of accumulated logs and reports, especially if users export sensitive activity data without understanding the privacy implications.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.