T09 · Insecure Skill Coding Practices
- Location
scripts/trigger-compile.sh:356- Finding
Untrusted Installed Skills Can Register Persistent Automatic Script Execution
- Content
View full analysis
Vulnerability Details
File Location:
scripts/trigger-compile.sh, lines 17–21, 40–55, 356–360, 394–400, 417, and 469
Vulnerability Type: Persistent execution of third-party Skill scripts without a trust allowlist
Risk Level: HighComplete Code Snippet
bash SKILLS_DIRS=() if [[ -d "$REPO_ROOT/skills" ]]; then SKILLS_DIRS+=("$REPO_ROOT/skills") fi SKILLS_DIRS+=("$HOME/.claude/skills") for _mp in "$HOME/.claude/plugins/marketplaces"/*/skills; do [[ -d "$_mp" ]] && SKILLS_DIRS+=("$_mp") donebash validate_trigger() { local skill_dir="$1" skill="$2" action="$3" scriptpath="$4" if [[ "$action" == "script" ]]; then if [[ -z "$scriptpath" ]]; then echo "ERROR: scriptpath is empty for skill '$skill'" >&2 exit 1 fi if [[ "$scriptpath" == /* ]] || [[ "$scriptpath" == *..* ]]; then echo "ERROR: scriptpath '$scriptpath' contains absolute path or path traversal (..) for skill '$skill'" >&2 exit 1 fi local local_script_path="$skill_dir/$scriptpath" if [[ ! -f "$local_script_path" ]]; then echo "ERROR: script file '$local_script_path' not found for skill '$skill'" >&2 exit 1 fi fi }bash script+="// dispatch to skill-owned resource script; pass hook stdin through { const { spawnSync } = require('child_process'); const scriptPath = path.join($(js_str "$skilldir"), $(js_str "$scriptpath")); const __interpreter = scriptPath.endsWith('.js') ? 'node' : 'bash'; const __t0 = Date.now(); const result = spawnSync(__interpreter, [scriptPath], { input: INPUT, encoding: 'utf8', timeout: 8000 });bash echo "$script" > "$output_file" chmod +x "$output_file" local hook_cmd="node ~/.claude/hooks/trigger-${event}.js" ... cp "$tmp_settings" "$SETTINGS"Technical Analysis
The compiler scans not only repository-controlled Skills, but also all Skills under the user's installation directory and every discovered marketplace. A Skill declaring
action: script...[truncated 2387 chars]- Remediation
View remediation
Remediation Suggestions
- Do not enable
action: scriptfor marketplace or globally installed Skills by default. - Maintain an explicit allowlist of trusted Skill directories, publishers, or immutable package identities.
- Before registration, show each script's resolved path, owning Skill, event, and cryptographic hash.
- Require explicit confirmation for every newly discovered or changed script trigger.
- Persist approvals against a content hash so an updated script cannot inherit approval for an older version.
- Resolve the script with
realpathand verify that the final target remains inside the approved Skill directory, including when symlinks are present. - Provide a mode that only compiles declarative
suggestandblockactions while rejecting executable script actions. - Record provenance in the generated dispatcher and refuse conflicting or duplicate Skill identities from different sources.
- Prefer a constrained subprocess environment with a minimal environment-variable allowlist and restricted filesystem/network access where platform sandboxing is available.
- Do not enable
