Back to skill

Security audit

Github Release Watcher

Security checks for vulnerabilities and agentic risk

Overview

This release-watcher skill has a coherent purpose, but its bundled script can turn untrusted GitHub release data into local code execution.

Review before installing. Use this only with repositories you trust, avoid unattended scheduling until the Python interpolation is fixed, and run it with a least-privileged GitHub/session environment because a malicious release tag could execute commands locally.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/check_releases.sh:36
Finding
Arbitrary Python Code Execution Through Unsafe String Interpolation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/check_releases.sh`, lines 36 and 42–46 **Vulnerability Type**: Python code injection **Risk Level**: High ### Vulnerable Code ```bash last_seen=$(python3 -c "import json; d=json.load(open('$STATE_FILE')); print(d.get('$repo',''))" 2>/dev/null) ``` ```bash python3 -c " import json with open('$STATE_FILE','r') as f: d=json.load(f) d['$repo']='$tag' with open('$STATE_FILE','w') as f: json.dump(d,f,indent=2) " ``` ### Technical Analysis The script directly interpolates shell variables into Python source passed to `python3 -c`. The values are placed inside single-quoted Python string literals without escaping or argument binding. The affected values include: - `$tag`, obtained from release metadata returned by GitHub. - `$repo`, obtained from the repository configuration. - `$STATE_FILE`, which can be supplied through an environment variable. An input containing a single quote can terminate the intended Python string literal and append arbitrary Python statements. For example, a release tag shaped like: ```text ';__import__("os").system("id");# ``` would produce Python source equivalent to: ```python d['owner/repository']='';__import__("os").system("id");#' ``` The injected `os.system` call would consequently execute on the local host. Shell quoting around the earlier `gh api` command does not prevent this issue because the injection occurs when dynamically constructing Python code. A malicious release tag is the most direct externally controlled exploitation vector. Repository maintainers or attackers who compromise release-publishing permissions can control tag names returned by the GitHub API. The environment-controlled state-file path is an additional unsafe interpolation boundary. ### Attack Path 1. A victim adds an attacker-controlled repository to `repos.txt`, or already monitors a repository whose release-publishing account is later compromised. 2. The attacker publishes a GitHub ...[truncated 1256 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Do not interpolate untrusted values into Python source code. Pass values as positional arguments and retrieve them through `sys.argv`. For example, the lookup can be implemented as: ```bash last_seen=$( python3 - "$STATE_FILE" "$repo" <<'PY' import json import sys state_file, repo = sys.argv[1], sys.argv[2] with open(state_file, encoding="utf-8") as f: state = json.load(f) print(state.get(repo, "")) PY ) ``` The update can be implemented similarly: ```bash python3 - "$STATE_FILE" "$repo" "$tag" <<'PY' import json import sys state_file, repo, tag = sys.argv[1:4] with open(state_file, encoding="utf-8") as f: state = json.load(f) state[repo] = tag with open(state_file, "w", encoding="utf-8") as f: json.dump(state, f, indent=2) PY ``` Additional hardening should include: 1. Validate repository entries against the expected `owner/repository` format before invoking `gh`. 2. Continue treating all GitHub release fields, including tags, names, and release bodies, as untrusted input. 3. Validate that `STATE_FILE` points to an intended regular file and reject unexpected locations where appropriate. 4. Write state changes to a securely created temporary file and atomically rename it to reduce corruption risks. 5. Run scheduled checks using a dedicated, least-privileged account with minimal filesystem and credential access. 6. Add regression tests containing quotes, semicolons, newlines, and Python syntax in release tags and file paths to verify that values remain data rather than executable code. ]]>
Vulnerability Patterns
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • 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
Findings (1)

Tool Parameter Abuse

High
Category
Tool Misuse
Content
bash scripts/check_releases.sh /path/to/repos.txt

# Dry run (show all latest releases regardless of state)
rm -f scripts/.last_seen.json && bash scripts/check_releases.sh
```

## Integration
Confidence
85% 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).

Static analysis

No suspicious patterns detected.