Back to skill

Security audit

Skill Lifecycle

Security checks for vulnerabilities and agentic risk

Overview

This skill has a coherent lifecycle purpose, but its scripts can move skill directories and contain input-handling flaws that could let crafted names alter files or run local code.

Review before installing. This skill is not clearly malicious, but it manages installed skills and should not be used until skill-name validation, path containment checks, safe JSON/Python argument passing, exact archive selection, and clearer consent/disclosure for persistent usage logs are added.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (5)

T09 · Insecure Skill Coding Practices

Error
Location
record-usage.sh:28
Finding

Arbitrary Python Code Execution Through Usage Recording

Content
View full analysis
/dev/null || echo "{\"$SKILL_NAME\":\"$TIMESTAMP\"}" > "$LATEST_FILE" ``` ### Technical Analysis `SKILL_NAME` is accepted from the first command-line argument and interpolated directly into a Python program passed to `python3 -c`. Shell quoting does not protect the generated Python source. A skill name containing a single quote and additional Python statements can terminate the intended string literal and introduce arbitrary statements. For example, a name shaped like the following can produce syntactically valid injected Python: ```text x'] = ''; __import__('os').system('COMMAND'); data['y ``` The generated statement becomes equivalent to: ```python data['x'] = '' __import__('os').system('COMMAND') data['y'] = 'TIMESTAMP' ``` The vulnerable branch is reached when `latest-usage.json` already exists. That file is normally created by the same script, so this condition is expected during ordinary repeated use. ### Attack Path 1. Ensure `latest-usage.json` exists, such as by invoking `record-usage.sh` once with an ordinary skill name. 2. Invoke `record-usage.sh` with a crafted skill-name argument containing a single quote and injected Python statements. 3. The script places the argument directly inside the `python3 -c` source. 4. Python parses and executes the injected statements. 5. Any command launched by the injected Python runs with the account privileges of the user executing the lifecycle script. ### Impact Assessment Successful exploitation provides arbitrary local code execution as the invoking user. The attacker can read or alter fil ...[truncated 255 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
check-retirement.sh:27
Finding

Arbitrary Python Code Execution Through Crafted Skill Directory Names

Content
View full analysis
/dev/null) ``` ### Technical Analysis The basename of each skill directory is inserted directly into a single-quoted Python string within dynamically generated source. Unix directory names can contain quotes, semicolons, parentheses, and other characters significant to Python. Consequently, a crafted directory name can terminate the argument to `data.get`, append arbitrary Python statements, and reconstruct valid trailing syntax. The existence check for `SKILL.md` does not make the directory trustworthy. A malicious or compromised skill installation process can create both the crafted directory and that file. The broad `except` clause and redirection of standard error also suppress evidence of malformed input or failed exploitation attempts. ### Attack Path 1. Create or install a directory beneath `SKILLS_DIR` whose basename contains a Python source-injection payload. 2. Add a `SKILL.md` file so the directory passes the script's eligibility check. 3. Run `check-retirement.sh`, either manually or through the documented periodic workflow. 4. The script derives `skill_name` from the attacker-controlled directory basename. 5. The name is inserted into `python3 -c` and parsed as executable Python source. 6. Injected commands execute with the privileges of the account running the retirement check. ### Impact Assessment An attacker who can introduce a skill directory can escalate that ability into arbitrary c ...[truncated 252 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
archive-skill.sh:5
Finding

Path Traversal Allows Archiving Directories Outside the Skills Root

Content
View full analysis
" && exit 1 SKILL_PATH="$SKILLS_DIR/$SKILL_NAME" ARCHIVE_PATH="$ARCHIVE_DIR/${SKILL_NAME}_${TIMESTAMP}" [ ! -d "$SKILL_PATH" ] && echo "❌ 技能不存在: $SKILL_PATH" && exit 1 mv "$SKILL_PATH" "$ARCHIVE_PATH" && { ``` ### Technical Analysis The skill name is appended directly to source and destination directory paths. Although the variables are shell-quoted, quoting only prevents word splitting and glob expansion; it does not prevent filesystem traversal. Values containing `../`, embedded slashes, or absolute-path-like components can cause the normalized source to resolve outside `SKILLS_DIR`. The destination can also escape `ARCHIVE_DIR`. No canonicalization or containment check confirms that either resolved path remains beneath its intended root. The implementation also immediately performs the move and does not enforce the user-confirmation control described in the Skill documentation. ### Attack Path 1. Identify a directory adjacent to or otherwise reachable from `SKILLS_DIR` using relative traversal. 2. Invoke the script with a crafted argument such as `../target`. 3. `SKILL_PATH` resolves to a directory outside the configured skills root. 4. The directory existence check succeeds because it checks the traversed path. 5. `mv` relocates the unintended directory to the computed destination. 6. The original application or workspace may lose access to the moved directory, causing integrity and availability damage. ### Impact Assessment The script can move directories outside the legitimate Skill tree when ...[truncated 365 chars]
Remediation
View remediation
&2 exit 1 ;; esac ``` - Canonicalize the configured roots and candidate paths with `realpath`. - Verify containment before moving data. A resolved source must begin with the canonical `SKILLS_DIR/`, and its immediate parent should equal the canonical skills root. - Construct the destination only after validation and verify that its parent is exactly `ARCHIVE_DIR`. - Refuse symlinked source directories where symlink behavior is not explicitly intended. - Create the archive root safely before moving and reject existing destination paths. - Add an explicit interactive confirmation or a deliberate `--yes` flag for automation. - Use `set -euo pipefail` and return a nonzero exit code when `mv` or metadata creation fails. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
record-usage.sh:24
Finding

Unescaped Input Corrupts JSON Usage Logs and State Files

Content
View full analysis
> "$USAGE_FILE" # 更新最新使用时间 if [ -f "$LATEST_FILE" ]; then python3 -c " import json with open('$LATEST_FILE', 'r') as f: data = json.load(f) data['$SKILL_NAME'] = '$TIMESTAMP' with open('$LATEST_FILE', 'w') as f: json.dump(data, f, indent=2) " 2>/dev/null || echo "{\"$SKILL_NAME\":\"$TIMESTAMP\"}" > "$LATEST_FILE" else echo "{\"$SKILL_NAME\":\"$TIMESTAMP\"}" > "$LATEST_FILE" fi ``` ### Technical Analysis `SKILL_NAME` and `ACTION` are concatenated into JSON text without JSON encoding. Inputs containing double quotes, backslashes, newlines, or control characters can terminate fields, inject additional properties or records, or make the document invalid. The documented action values are not enforced, so arbitrary attacker-controlled action text reaches the log. If the Python update fails, the fallback writes attacker-controlled JSON using `echo` and truncates `latest-usage.json`. This can erase all prior usage state while still producing malformed content. Concurrent invocations can also interleave append operations or overwrite each other's state because no locking or atomic update mechanism is used. ### Attack Path 1. Invoke `record-usage.sh` with a skill name or action containing JSON delimiters or newline characters. 2. The script inserts the value verbatim into `usage.jsonl`. 3. The attacker creates forged fields, extra apparent records, or invalid JSON lines. 4. If the latest-state update fails, the fallback truncates `latest-usage.json` and replaces it with an unescaped attacker-controlled document. 5. Subsequent retirement checks may lose valid usage history, misclassify skills, or silently treat the state as ...[truncated 421 chars]
Remediation
View remediation
&2 exit 1 ;; esac ``` - Write each JSONL record using `json.dumps`. - Update `latest-usage.json` through a temporary file followed by atomic `os.replace`. - Use a file lock around read-modify-write operations if concurrent invocations are possible. - On malformed existing JSON, preserve the original file and fail clearly rather than truncating the complete state. - Apply restrictive permissions, such as `umask 077`, before creating lifecycle state files. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
restore-skill.sh:11
Finding

Ambiguous Prefix Matching Can Restore the Wrong Archived Skill

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • 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 (14)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

声明描述的是一个较完整的技能退休机制,核心能力应包括使用频次记录、基于低频阈值进行退休标记,以及归档/恢复支持。但给出的代码片段只接受一个技能名参数,将对应目录移动到归档目录,并生成一个归档元数据文件。代码中没有任何频次统计、阈值判断、低频识别或退休标记逻辑;归档原因还是写死为 retirement_threshold_exceeded,而非经过实际判定得出。此外,恢复仅体现在元数据里的命令提示,当前代码并未实现恢复流程。因此,实际行为只是“手动归档单个技能”的子功能,未覆盖声明中的主要能力,属于实质性描述不符。

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
89% confidence
Finding

声明描述包含三个核心能力:记录使用频次、标记低频/退休技能、支持归档和恢复。代码只覆盖了其中“标记低频/退休技能”这一部分,方法是读取已有 usage JSON、根据 100/300 天阈值分类并输出 Markdown 报告。它没有更新或写入使用频次数据(仅在文件不存在时初始化为空 JSON),也没有执行任何归档/恢复操作;ARCHIVE_DIR 只是被创建但未使用。因此描述对功能范围有实质性夸大,属于描述与实际行为不一致。

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding

描述声明的是一个完整的“技能退休机制”,包括基于使用频次进行标记、退休判断,以及归档和恢复支持。但实际代码只是一个使用记录脚本:创建本地目录与文件、追加事件日志、更新最近使用时间。它没有任何统计频次、分析低频、标记退休、执行归档或恢复的逻辑。虽然记录使用情况可以作为退休机制的基础支撑,但就该代码块本身而言,实际行为明显小于声明范围,且“支持恢复”在代码中仅体现为可记录 restored 动作,并不真正恢复技能。因此描述与代码行为存在实质性不匹配。

Content

No source excerpt is available for this finding.

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
95% confidence
Finding

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).

Content

Scanner excerpt · restore-skill.sh (reported line 20)May include surrounding context.

sh
mv "$ARCHIVE_PATH" "$RESTORE_PATH" && {
    echo "✅ 已恢复: $SKILL_NAME"
    bash "$SKILLS_DIR/skill-lifecycle/record-usage.sh" "$SKILL_NAME" "restored"
    rm -f "$RESTORE_PATH/_archive_meta.json"
} || echo "❌ 恢复失败"

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The file forces a specific language for the skill's description and operating instructions, which can violate language/locale policy when no user opt-in or alternative is provided. There is no indication that this skill is intentionally region-specific or that users may choose another language.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

This shell script includes natural-language comments and status/error messages in Chinese only, such as the title, usage description, and success/failure output. Under the language/locale policy rule, forcing a specific language without user opt-in or a documented regional justification is a 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

The script's human-facing comments and generated report/output strings are written only in Chinese, with no mechanism for user language selection or documented locale constraint. This is a natural-language policy issue because it imposes a specific language on users without opt-in.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The natural-language comments and user-visible success output are written in Chinese, which imposes a specific language context without any opt-in or alternative. Under the language/locale policy, hard-coding a single language is a policy concern unless the skill clearly documents that it is intentionally region- or language-specific.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The script deletes _archive_meta.json using rm -f immediately after restore, but there is no prior confirmation prompt or warning that a file will be removed as part of the operation. Although the script prints success/failure messages, it does not disclose this deletion before performing it.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
93% confidence
Finding

This shell script creates directories and writes to files such as latest-usage.json and retirement-report.md, but it provides no prior user-facing warning beyond a usage comment. For a code file, these file-modifying operations lack confirmation prompts, runtime disclosure, or explicit warning comments near the actions.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
92% confidence
Finding

The script persistently records skill names, actions, and timestamps to files under the user's home directory without any user-facing disclosure, consent, retention policy, or access control handling. While the collected data is limited, it creates an activity log that can reveal behavior patterns and installed skill usage, which is a real privacy/security concern if the files are later accessed by other local processes or included in backups.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

Comments and user-visible status messages are written in Chinese, and the script does not offer any language or locale selection. This can violate language/locale policy when skills are expected to avoid forcing a specific language without user opt-in.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
96% confidence
Finding

The visible runtime messages such as failure and success notices are emitted only in Chinese, with no mechanism for the user to choose a preferred language. This is a natural-language policy concern because it enforces a locale choice implicitly.

Content

No source excerpt is available for this finding.