Back to skill

Security audit

Clawdbot Release Check

Security checks for vulnerabilities and agentic risk

Overview

The skill is a disclosed release notifier, but its scheduled checker handles external release data unsafely enough to require review before installation.

Review or patch this skill before enabling the scheduled job. At minimum, validate release tags as numeric versions before Bash arithmetic, replace fixed /tmp files with mktemp or state-directory temp files, and ensure release notes are forwarded as quoted untrusted data. Manual checks are lower risk than the daily agent-run notifier.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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
scripts/check.sh:163
Finding

Remote GitHub release tags can reach unsafe Bash arithmetic evaluation

Content
View full analysis
/dev/null) ``` ```bash # Get latest release info latest_tag=$(echo "$releases_json" | jq -r '.[0].tag_name // empty') release_url=$(echo "$releases_json" | jq -r '.[0].html_url // empty') if [[ -z "$latest_tag" ]]; then exit 0 fi latest_version="${latest_tag#v}" ``` ```bash # Compare versions (semver-style: major.minor.patch) version_gt() { # Returns 0 (true) if $1 > $2 local IFS='.' read -ra v1 <<< "$1" read -ra v2 <<< "$2" for i in 0 1 2; do local n1=${v1[i]:-0} local n2=${v2[i]:-0} if (( n1 > n2 )); then return 0; fi if (( n1 < n2 )); then return 1; fi done return 1 # Equal } ``` ```bash if version_gt "$current_version" "$latest_version"; then exit 0 fi ``` ### Technical Analysis The script downloads release metadata from the GitHub API and treats the first release's `tag_name` as a semantic version. The resulting value is split into components and assigned to `n2`, but the components are not validated as decimal integers before they are used in Bash arithmetic expressions. Bash arithmetic evaluation does not treat variable contents as inert strings. Values can be recursively interpreted as arithmetic expressions, including constructs such as array subscripts that may contain command substitutions. Consequently, specially crafted release metadata may cause shell commands embedded in an arithmetic expression to execute when the following statements are evaluated: ```bash (( n1 > n2 )) (( n1 < n2 )) ``` Although exploitation requires control over or compromise of release metadata returned for the configured repository, this is a meaningful trust-boundary violation because ...[truncated 1362 chars]
Remediation
View remediation
&2 exit 1 fi ``` 2. Apply equivalent validation to `current_version`, even though it originates locally. 3. Do not place untrusted strings directly or indirectly into Bash arithmetic expressions. 4. Convert validated components to canonical base-10 integers before comparison, accounting for leading-zero behavior. 5. Prefer a dedicated semantic-version comparison implementation that treats version input strictly as data. 6. Fail closed when the GitHub response has an unexpected schema or invalid tag instead of silently continuing. 7. Add tests using malformed tags, arithmetic operators, array syntax, command-substitution-like input, prerelease tags, and unusually long values. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/check.sh:141
Finding

Predictable shared temporary files allow symlink-based file clobbering

Content
View full analysis
/tmp/cache-update.json mv /tmp/cache-update.json "$CACHE_FILE" ``` From the uninstall path in `scripts/setup.sh`: ```bash jq --arg name "$JOB_NAME" '.jobs = [.jobs[] | select(.name != $name)]' "$CRON_FILE" > /tmp/cron-updated.json mv /tmp/cron-updated.json "$CRON_FILE" ``` From the installation path in `scripts/setup.sh`: ```bash jq --arg id "$JOB_ID" \ --arg name "$JOB_NAME" \ --arg hour "$CRON_HOUR" \ --arg channel "$CHANNEL" \ --arg to "$TELEGRAM_TO" \ --arg script "$SCRIPT_DIR/check.sh" \ --argjson now "$NOW_MS" \ '.jobs += [{ "id": $id, "name": $name, "enabled": true, "createdAtMs": $now, "updatedAtMs": $now, "schedule": { "kind": "cron", "expr": ("0 " + $hour + " * * *") }, "sessionTarget": "isolated", "wakeMode": "now", "payload": { "kind": "agentTurn", "message": ("UPDATE_CHECK: Run " + $script + " and if there is output, send it to the user."), "deliver": true, "channel": $channel, "to": $to }, "isolation": { "postToMainPrefix": "System" }, "state": {} }]' "$CRON_FILE" > /tmp/cron-updated.json mv /tmp/cron-updated.json "$CRON_FILE" ``` ### Technical Analysis The scripts use fixed filenames in the globally shared `/tmp` directory. Shell redirection opens these paths before `jq` runs and does not request exclusive creation or verify that the destination is a regular file owned by the current user. A local attacker can pre-create `/tmp/cache-update.json` or `/tmp/cron-updated.json` as a symbolic link to another file writable ...[truncated 1331 chars]
Remediation
View remediation
"$tmp_file"; then mv -f -- "$tmp_file" "$CACHE_FILE" fi ``` 5. Register cleanup traps for interruption and failure paths. 6. Apply the same correction to both cache updates and cron configuration installation/removal. 7. Avoid running these scripts as root or another privileged account. ]]>

T01 · Skill Instruction Hijacking

Warning
Location
scripts/check.sh:221
Finding

Untrusted release notes are passed into an AI agent notification turn

Content
View full analysis
/dev/null) if [[ -n "$cached" ]]; then echo "$cached" return fi fi # Extract highlights local hl="" if echo "$body" | grep -qi "Highlights"; then hl=$(echo "$body" | sed -n '/[Hh]ighlights/,/^##[^#]/p' | head -8 | sed '/^##[^#]/d' | sed 's/^### //' | sed 's/^## //') else hl=$(echo "$body" | grep -v "^$" | head -6 | sed 's/^### //' | sed 's/^## //') fi # Save to cache if [[ -n "$hl" && -f "$CACHE_FILE" ]]; then local escaped_hl escaped_hl=$(echo "$hl" | jq -Rs .) jq --arg tag "$tag" --argjson hl "$escaped_hl" '.highlights[$tag] = $hl' "$CACHE_FILE" > /tmp/cache-update.json mv /tmp/cache-update.json "$CACHE_FILE" fi echo "$hl" } ``` ```bash release_body=$(echo "$releases_json" | jq -r '.[0].body // empty') if [[ -n "$release_body" ]]; then highlights=$(get_highlights "$latest_tag" "$release_body") if [[ -n "$highlights" ]]; then # Truncate if too long if [[ ${#highlights} -gt 500 ]]; then highlights="${highlights:0:500}..." fi cat <
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Memory PoisoningPersistent Context Injection, Context Window Stuffing, Memory Manipulation
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (6)

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.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

This is a mismatch because the description only covers checking for new releases and notifying once per version, with no triggers declared, but the code also includes a setup script that installs an automated daily job in ~/.clawdbot/cron/jobs.json. That job is a trigger mechanism and enables notifications to external channels. The release-checking logic itself is consistent with the description, but the undeclared scheduling/trigger setup and channel configuration are additional capabilities not represented in the declared purpose.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

The skill's stated purpose is to check for new clawdbot releases and notify once per version. Writing intermediate state to a predictable global path under /tmp introduces cross-process file manipulation behavior that is not necessary for that purpose and expands the skill's operational surface beyond simple local state storage in its declared state directory.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
81% confidence
Finding

The script writes intermediate cache data to /tmp/cache-update.json and then moves it into the user's cache file. Although the script does perform file writes elsewhere as part of its purpose, this temporary-file write is not disclosed to the user via prompt, log, or warning and uses a shared system temp location, which is a safety-relevant filesystem side effect.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/check.sh (reported line 18)May include surrounding context.

sh
STATE_DIR="${HOME}/.clawdbot"
STATE_FILE="${STATE_DIR}/clawdbot-release-check-state.json"
CACHE_FILE="${STATE_DIR}/clawdbot-release-check-cache.json"
RELEASES_URL="https://api.github.com/repos/clawdbot/clawdbot/releases"
CACHE_MAX_AGE_HOURS="${CACHE_MAX_AGE_HOURS:-24}"

usage() {

Memory Manipulation

High
Category
Memory Poisoning
Confidence
80% confidence
Finding

Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Content

Scanner excerpt · SKILL.md (reported line 51)May include surrounding context.

md
# Show highlights from ALL missed releases
{baseDir}/scripts/check.sh --all-highlights

# Clear state (will notify again on next check)
{baseDir}/scripts/check.sh --reset

# Help

Static analysis

No suspicious patterns detected.