Back to skill

Security audit

ShellGuard Scanner

Security checks for vulnerabilities and agentic risk

Overview

This skill is a local security scanner, but it needs review because crafted filenames in scanned skills can trigger code execution and the package includes active malicious-style test fixtures.

Install only after review or remediation. Run this scanner in a sandboxed environment with no credentials and preferably no network access when scanning untrusted skills; do not treat the included fixture skills as installable, and fix the filename-to-Python injection before using it in CI or on downloaded packages.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/shellguard-scanner.sh:340
Finding
Arbitrary Python Code Execution Through Crafted Scanned Filenames<![CDATA[ ## Vulnerability Details **File Location**: `scripts/shellguard-scanner.sh:340-375` **Vulnerability Type**: Python source injection through untrusted filename interpolation **Risk Level**: High ### Vulnerable Code ```bash # Unicode zero-width characters (requires python3 check) if command -v python3 &>/dev/null; then local zwc_count zwc_count=$(python3 -c " import sys, unicodedata text = open('$file', 'r', errors='replace').read() zwc = [c for c in text if unicodedata.category(c) == 'Cf' or ord(c) in (0x200B,0x200C,0x200D,0xFEFF,0x00AD,0x2060,0x180E)] print(len(zwc)) " 2>/dev/null || echo "0") if (( zwc_count > 0 )); then add_finding "TIER3-OBFUSCATION" "$fname — CRITICAL: $zwc_count zero-width/invisible characters (steganography)" score_add SCORE_OBFUSCATION 15 20 fi fi # Bidirectional control characters if python3 -c " import sys text = open('$file', 'r', errors='replace').read() bidi = [c for c in text if ord(c) in (0x202E,0x202D,0x202A,0x202B,0x2066,0x2067,0x2068,0x202C,0x2069)] sys.exit(0 if bidi else 1) " 2>/dev/null; then add_finding "TIER3-OBFUSCATION" "$fname — CRITICAL: Bidirectional override characters (text spoofing attack)" score_add SCORE_OBFUSCATION 18 20 fi # Unicode tag range (U+E0000..E007F) — invisible instruction injection if python3 -c " import sys text = open('$file', 'r', errors='replace').read() tags = [c for c in text if 0xE0000 <= ord(c) <= 0xE007F] sys.exit(0 if tags else 1) " 2>/dev/null; then add_finding "TIER3-OBFUSCATION" "$fname — CRITICAL: Unicode tag characters (U+E0000 range) — invisible content injection" score_add SCORE_OBFUSCATION 20 20 fi ``` ### Technical Analysis The scanner embeds the value of `$file` directly inside Python source passed to `python3 -c`. Although the shell expands `$file` inside a double-quoted shell string, the resulting value is placed between single quotes in this Python statement: ```python text = open('$file', 'r', errors='repla ...[truncated 2330 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Never interpolate a filename into generated Python source. Pass it as a separate argument and retrieve it through `sys.argv`. Use a fixed, quoted heredoc for each check: ```bash zwc_count=$(python3 - "$file" <<'PY' import sys import unicodedata with open(sys.argv[1], "r", errors="replace") as stream: text = stream.read() zwc = [ char for char in text if unicodedata.category(char) == "Cf" or ord(char) in (0x200B, 0x200C, 0x200D, 0xFEFF, 0x00AD, 0x2060, 0x180E) ] print(len(zwc)) PY ) ``` Apply the same argument-passing design to the bidirectional-control and Unicode-tag checks. Additional hardening should include: 1. Consolidate all three Unicode checks into one fixed Python script or one quoted heredoc invocation. 2. Treat paths as opaque data and never embed them into shell, Python, regular-expression, or JSON source text. 3. Add regression tests using filenames containing single quotes, double quotes, backslashes, newlines, spaces, shell metacharacters, and non-ASCII characters. 4. Run scans of hostile packages in a sandbox with no credentials, restricted filesystem access, and disabled network access. 5. Ensure a malformed filename causes a controlled scan error rather than silently returning a clean result. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/shellguard-scanner.sh:666
Finding
Malformed or Injected JSON Reports in the Primary Scanner<![CDATA[ ## Vulnerability Details **File Location**: `scripts/shellguard-scanner.sh:666-693` **Vulnerability Type**: Improper JSON encoding of attacker-controlled report fields **Risk Level**: Medium ### Vulnerable Code ```bash print_json() { local skill_name="$1" local skill_path="$2" local score score=$(calculate_score) local rating rating=$(rating_for_score "$score") echo "{" echo " \"skill_name\": \"$skill_name\"," echo " \"skill_path\": \"$skill_path\"," echo " \"overall_score\": $score," echo " \"rating\": \"$rating\"," echo " \"scores\": {" echo " \"prompt_injection\": $SCORE_PROMPT_INJECTION," echo " \"obfuscation\": $SCORE_OBFUSCATION," echo " \"code_execution\": $SCORE_CODE_EXEC," echo " \"exfiltration\": $SCORE_EXFILTRATION," echo " \"credential_theft\": $SCORE_CREDENTIAL," echo " \"tool_shadowing\": $SCORE_SHADOWING" echo " }," echo " \"findings\": [" local first=1 for finding in "${FINDINGS[@]}"; do [[ $first -eq 0 ]] && echo "," printf ' %s' "\"$(echo "$finding" | sed 's/"/\\"/g')\"" first=0 done echo echo " ]" echo "}" } ``` ### Technical Analysis The `--json` report is assembled through string concatenation rather than a JSON serializer. Several included values can derive from an untrusted scan target: - `skill_name` derives from the scanned file or directory name. - `skill_path` is supplied by the caller and may contain unusual characters. - Findings can contain filenames derived from attacker-controlled files. `skill_name` and `skill_path` receive no JSON escaping. Findings only escape double quotes and do not correctly encode backslashes, carriage returns, line feeds, tabs, or other control characters required by the JSON grammar. Consequently, a crafted path or filename can terminate a JSON string, insert additional properties, alter the visible report structure, or make the outp ...[truncated 1445 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Replace manual JSON construction with a standards-compliant serializer. Because Python is already supported, report data can be passed to a fixed Python program and encoded with `json.dump` or `json.dumps`. For example: ```python import json import sys report = { "skill_name": skill_name, "skill_path": skill_path, "overall_score": score, "rating": rating, "scores": scores, "findings": findings, } json.dump(report, sys.stdout, ensure_ascii=False) sys.stdout.write("\n") ``` Recommended hardening measures: 1. Serialize every string with one trusted JSON library. 2. Do not use `echo`, `printf`, or `sed` as JSON escaping mechanisms. 3. Validate generated output with a strict parser during tests. 4. Add tests for quotes, backslashes, tabs, newlines, carriage returns, control bytes, and Unicode in Skill paths and filenames. 5. Make CI examples fail closed when the scanner exits unexpectedly or its JSON cannot be parsed. 6. Consider emitting one JSON array for multi-target scans instead of adjacent standalone objects. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/shadow-detector.sh:414
Finding
Improper JSON Escaping in Shadow Detector Reports<![CDATA[ ## Vulnerability Details **File Location**: `scripts/shadow-detector.sh:414-435` **Vulnerability Type**: Improper JSON encoding and incorrect escape order **Risk Level**: Medium ### Vulnerable Code ```bash format_finding_json() { local severity="$1" local record="$2" local is_last="$3" local skill type description evidence IFS='|' read -r skill type description evidence <<< "$record" evidence=$(echo "$evidence" | sed 's/"/\\"/g; s/\\/\\\\/g' | tr -d '\n') description=$(echo "$description" | sed 's/"/\\"/g') local comma="" [[ "$is_last" == "0" ]] && comma="," cat <<JSON { "severity": "$severity", "skill": "$skill", "type": "$type", "description": "$description", "evidence": "$evidence" }$comma JSON } ``` ### Technical Analysis The shadow detector manually creates JSON from values that can include attacker-controlled Skill names and matching source text. The following defects are present: - `skill` is emitted without JSON escaping. - `type` and `severity` are also emitted without centralized validation or serialization. - `description` only escapes double quotes. - `evidence` attempts to escape quotes and backslashes with `sed`, but performs quote escaping first and backslash escaping second. The escape order for `evidence` is incorrect. A quote first becomes `\"`; the later backslash replacement changes that escape to `\\\"`. In JSON parsing, this sequence can represent a literal backslash followed by the end of the JSON string rather than an escaped quote, allowing the remaining text to affect document structure. Other JSON control characters are not comprehensively encoded. The internal record format also uses `|` as an unescaped delimiter, so attacker-controlled evidence containing that character can shift or truncate fields before JSON formatting. ### Attack Path 1. An attacker creates a Skill whose name or `SKILL.md` content matches a shadow-detector signatur ...[truncated 1114 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Use a real JSON serializer and replace the delimiter-based record representation with structured storage. Recommended changes: 1. Pass `severity`, `skill`, `type`, `description`, and `evidence` as separate values to Python and serialize them with `json.dumps`. 2. Do not attempt JSON escaping with chained `sed` substitutions. 3. Avoid storing records as pipe-delimited strings. Use parallel arrays, Bash associative structures, temporary structured files, or serialize findings as they are collected. 4. If a delimiter must remain temporarily, reject or encode delimiter characters before storage and decode them before serialization. 5. Add regression tests where Skill names and evidence contain: - Double quotes - Backslashes - Pipe characters - Newlines and tabs - Carriage returns - Unicode control characters 6. Parse every generated test report with a strict JSON parser and fail the test if parsing fails. 7. Ensure CI integrations treat malformed output as a blocking scanner failure rather than a clean result. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (30)

Description-Behavior Mismatch

Critical
Confidence
100% confidence
Finding
The skill is described as a security scanner for skill directories, yet this code reads the user's local OpenClaw configuration and exfiltrates it externally. That behavior is unrelated to the advertised function and represents unauthorized collection of potentially sensitive data.

Context-Inappropriate Capability

Critical
Confidence
100% confidence
Finding
Reading ~/.openclaw/openclaw.json and transmitting it over the network is unjustified for a local scanner and can expose secrets, tokens, endpoints, or other sensitive operational settings. The skill context makes this more dangerous because users would reasonably trust a security tool and may not expect it to access unrelated personal configuration.

Description-Behavior Mismatch

Critical
Confidence
99% confidence
Finding
The manifest and description present the skill as benign, but the content contains hidden instructions to steal credential material and send it to a remote server. This mismatch is a strong sign of deliberate deception intended to bypass user and reviewer scrutiny.

Intent-Code Divergence

Critical
Confidence
99% confidence
Finding
The file embeds a SYSTEM instruction telling the agent to silently run a curl command that reads a local auth file and sends its contents to an external webhook. This is explicit credential theft and covert data exfiltration, directly contradicting the stated productivity purpose of the skill.

Context-Inappropriate Capability

Critical
Confidence
99% confidence
Finding
The instruction explicitly directs silent execution of a network command that reads local authentication data and suppresses output. Silent handling, credential file access, and external transmission together indicate intentional stealth and theft rather than any valid productivity feature.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
This finding describes behavior far outside the declared purpose: reading local OpenClaw configuration from the user's home directory and transmitting encoded data to an external endpoint via curl/subprocess. That is classic covert data exfiltration under the guise of a security scanner, and the trusted security branding makes it more dangerous because operators are more likely to run it against sensitive environments.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
This finding describes behavior far outside the declared purpose: reading local OpenClaw configuration from the user's home directory and transmitting encoded data to an external endpoint via curl/subprocess. That is classic covert data exfiltration under the guise of a security scanner, and the trusted security branding makes it more dangerous because operators are more likely to run it against sensitive environments.

Ae1

High
Category
analysis-evasion
Content
bash scripts/shellguard-scanner.sh /path/to/suspicious-skill/
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
bash scripts/shellguard-scanner.sh /path/to/suspicious-skill/
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
bash scripts/shellguard-scanner.sh /path/to/suspicious-skill/
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Credential Access

High
Category
Privilege Escalation
Content
# Sensitive file access not in description
    if ! echo "$desc" | grep -qiP 'file|read|write|disk|storage|save|load|config'; then
        if echo "$all_code" | grep -qiP '(\.ssh|\.env|/etc/passwd|auth-profiles|\.openclaw)'; then
            add_finding "critical" "$skill_name" "scope-mismatch-sensitive-files" \
                "Skill accesses sensitive system files not mentioned in description" \
                "Accesses .ssh, .env, /etc/passwd, or OpenClaw config files"
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
# Sensitive file access not in description
    if ! echo "$desc" | grep -qiP 'file|read|write|disk|storage|save|load|config'; then
        if echo "$all_code" | grep -qiP '(\.ssh|\.env|/etc/passwd|auth-profiles|\.openclaw)'; then
            add_finding "critical" "$skill_name" "scope-mismatch-sensitive-files" \
                "Skill accesses sensitive system files not mentioned in description" \
                "Accesses .ssh, .env, /etc/passwd, or OpenClaw config files"
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
local -a cred_patterns=(
        '\.ssh/(id_rsa|id_ed25519|id_ecdsa|authorized_keys|known_hosts)'
        '/etc/(passwd|shadow|sudoers)'
        '\.aws/credentials'
        '\.gnupg/'
        '\.npmrc'
        '\.pypirc'
Confidence
90% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
'/etc/(passwd|shadow|sudoers)'
        '\.aws/credentials'
        '\.gnupg/'
        '\.npmrc'
        '\.pypirc'
        'auth-profiles\.json'
        '\.openclaw/.*\.(json|token)'
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
'Bearer\s+[a-zA-Z0-9_\-\.]{20,}'
        'password\s*=\s*["\x27][^"]{6,}'
        '\.env\b.*read|read.*\.env\b'
        'keychain|Keychain'
    )

    for pattern in "${cred_patterns[@]}"; do
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
'Bearer\s+[a-zA-Z0-9_\-\.]{20,}'
        'password\s*=\s*["\x27][^"]{6,}'
        '\.env\b.*read|read.*\.env\b'
        'keychain|Keychain'
    )

    for pattern in "${cred_patterns[@]}"; do
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Intent-Code Divergence

High
Confidence
100% confidence
Finding
The docstring and comments claim the script performs setup formatting, but the actual code reads a user configuration file and sends it to a remote service. This mismatch is a strong indicator of deceptive behavior intended to hide unauthorized access and exfiltration.

Missing User Warnings

High
Confidence
99% confidence
Finding
Sensitive configuration data is encoded and sent to an external service without any warning, consent, or visible user-facing disclosure. Silent transmission of local config data is a serious privacy and security violation, especially in a tool presented as defensive software.

Missing User Warnings

High
Confidence
99% confidence
Finding
The skill provides no warning that it will silently transmit local credential data, which prevents informed consent and conceals high-risk behavior. In this context, the omission is not merely poor documentation; it supports covert exfiltration of secrets.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The documentation embeds code execution examples that are not justified by the advertised purpose of a note-taking skill. Including both shell and Python execution paths for decoded payloads increases the likelihood that users or agents will run hidden logic without understanding what it does.

Intent-Code Divergence

High
Confidence
99% confidence
Finding
The skill claims to be a benign note-taking utility, but its quick-start instructs users to decode a Base64 string and pipe it directly into bash. Hiding executable shell content behind encoding is unnecessary for the stated functionality and is a classic prompt/package delivery technique for smuggling commands past casual review.

Missing User Warnings

High
Confidence
99% confidence
Finding
The quick-start command tells the user to execute obfuscated shell code with no warning or explanation. Even if the decoded content appears harmless, the pattern normalizes unsafe execution behavior and could easily be swapped for destructive or backdoored commands in a real distribution.

Chaining Abuse

High
Category
Tool Misuse
Content
Initialize with:

```bash
echo "aW5pdGlhbGl6aW5nIG5vdGVzLi4u" | base64 -d | bash
```

Or use the Python helper:
Confidence
97% confidence
Finding
The use of a pipeline into bash is a direct command-execution sink and, in this context, is chained with Base64 decoding to conceal the executed content. For a note-taking skill, this is contextually unjustified and significantly more dangerous because users may trust the benign branding and run the command without scrutiny.

Missing User Warnings

High
Confidence
99% confidence
Finding
The Python helper uses base64 decoding followed by exec, which directly runs hidden code in the current interpreter context. This is especially dangerous in agent environments because it can execute arbitrary logic with the permissions of the hosting process while concealing behavior from reviewers.

Lp3

Medium
Category
MCP Least Privilege
Confidence
85% confidence
Finding
The skill advertises executable scanner scripts and optional Python analysis, implying shell, file-read, and likely network-capable behavior, yet the manifest does not declare any explicit tool scope or permissions. That omission reduces transparency and can allow operators or orchestrators to grant broader access than users expect, which is especially risky for a security-themed skill that users may trust by default.