Back to skill

Security audit

Discord Roster

Security checks for vulnerabilities and agentic risk

Overview

The skill has a legitimate Discord roster purpose, but its script can execute unintended local code when given crafted arguments or environment values.

Review this skill before installing. Use it only with trusted invocations, a least-privilege Discord bot token, authorized guilds, and a trusted proxy configuration. The script should be fixed to pass inputs to Python as data and validate allowed filters before routine use.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/discord-roster.sh:53
Finding
Arbitrary Python Code Execution Through the Members Filter Argument## Vulnerability Details **File Location**: `scripts/discord-roster.sh`, lines 53–66 **Vulnerability Type**: Unsanitized argument interpolation into dynamically constructed Python source **Risk Level**: High **Vulnerable Code**: ```bash cmd_members() { local guild_id="${1:?Usage: members <guild_id> [--bots|--humans]}" local filter="${2:-all}" local raw raw=$(api "/guilds/${guild_id}/members?limit=1000") python3 -c " import json, sys data = json.loads('''$( echo "$raw" | python3 -c "import sys; print(sys.stdin.read().replace(\"'\",\"\\\\'\"))" )''') if isinstance(data, dict) and 'message' in data: print(f\"API Error: {data['message']}\", file=sys.stderr) sys.exit(1) filt = '$filter' ``` ### Technical Analysis The second positional argument to the `members` command is stored in `filter` and inserted directly into the source string supplied to `python3 -c`. It is not validated or passed as inert data through `sys.argv`, standard input, or an environment variable. An attacker who can control this argument can include a single quote to terminate the Python string assigned to `filt`, append Python statements, and comment out the remaining source on that line. The injected Python can invoke operating-system commands through modules such as `os` or `subprocess`. Shell metacharacters introduced through ordinary parameter expansion are not re-evaluated as shell syntax, but this does not prevent exploitation: the resulting text is subsequently parsed as Python source by `python3 -c`. Exploitation requires the preceding Discord API request to complete successfully because the vulnerable Python invocation occurs after `raw=$(api ...)`. ### Attack Path 1. The attacker gains control over, or influences, the arguments used to invoke the Skill. 2. The attacker invokes `members` with a valid or accessible guild ID so the Discord API request succeeds. 3. The second argument is crafted to cl ...[truncated 1212 chars]
Remediation
## Remediation Suggestions Do not embed command-line arguments in Python source. Pass the filter as a positional argument and validate it against an explicit allowlist before use. A hardened approach is: ```bash local filter="${2:-all}" case "$filter" in all|--bots|--humans) ;; *) die "Invalid member filter: $filter" ;; esac printf '%s' "$raw" | python3 -c ' import json import sys filt = sys.argv[1] data = json.load(sys.stdin) # Process data without constructing source from input. ' "$filter" ``` Additionally: - Pass API responses through standard input rather than embedding them into Python source. - Reject unexpected extra arguments. - Add regression tests containing quotes, newlines, backslashes, semicolons, and Python syntax. - Treat every command-line argument as untrusted, even when expected to come from an agent-generated command.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/discord-roster.sh:8
Finding
Arbitrary Python Code Execution Through OPENCLAW_CONFIG## Vulnerability Details **File Location**: `scripts/discord-roster.sh`, lines 8–21 **Vulnerability Type**: Unsanitized configuration path interpolation into dynamically constructed Python source **Risk Level**: Medium **Vulnerable Code**: ```bash CONFIG_FILE="${OPENCLAW_CONFIG:-$HOME/.openclaw/openclaw.json}" die() { echo "ERROR: $*" >&2; exit 1; } read_config() { [[ -f "$CONFIG_FILE" ]] || die "Config not found: $CONFIG_FILE" python3 -c " import json, sys with open('$CONFIG_FILE') as f: c = json.load(f) token = c.get('channels',{}).get('discord',{}).get('token','') proxy = c.get('channels',{}).get('discord',{}).get('proxy','') print(token) print(proxy) " } ``` ### Technical Analysis `CONFIG_FILE` can be supplied through the `OPENCLAW_CONFIG` environment variable. Its value is interpolated directly inside a single-quoted Python string literal in source passed to `python3 -c`. A path containing a quote and suitable Python syntax can terminate the intended string and alter the generated program. Newline characters in an environment variable can also be used to introduce additional Python statements. The preliminary `[[ -f "$CONFIG_FILE" ]]` check reduces exploitability because the exact attacker-controlled path must resolve to an existing regular file, but it does not make source-code interpolation safe. An attacker able to create a file with a crafted name, or otherwise satisfy the file check, can reach the vulnerable interpreter call. ### Attack Path 1. The attacker can control the `OPENCLAW_CONFIG` environment variable inherited by the Skill process. 2. The attacker creates or identifies a regular file whose path contains the characters required to alter the generated Python source. 3. The crafted path passes the `[[ -f "$CONFIG_FILE" ]]` check. 4. `read_config` expands the path directly into the program supplied to `python3 -c`. 5. Python parses attacker-controlled path content ...[truncated 823 chars]
Remediation
## Remediation Suggestions Pass the configuration path as data through `sys.argv` rather than inserting it into Python source: ```bash read_config() { [[ -f "$CONFIG_FILE" ]] || die "Config not found: $CONFIG_FILE" python3 -c ' import json import sys with open(sys.argv[1], encoding="utf-8") as f: c = json.load(f) discord = c.get("channels", {}).get("discord", {}) print(discord.get("token", "")) print(discord.get("proxy", "")) ' "$CONFIG_FILE" } ``` Further hardening should include: - Validate that an overridden configuration path is within an approved directory when arbitrary paths are unnecessary. - Reject paths containing control characters. - Ensure the configuration file is owned by the expected user and is not writable by untrusted accounts. - Apply restrictive file permissions because the file contains a Discord credential. - Add tests using paths containing quotes, spaces, backslashes, Unicode characters, and newlines.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (3)

Lp1

High
Category
MCP Least Privilege
Confidence
75% confidence
Finding
The skill uses 'shell' capability that is not listed in its permissions. This may indicate deceptive intent or missing permission declarations.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The skill advertises read-only Discord roster queries but does not warn users that it transmits bot credentials and retrieves potentially sensitive guild, member, role, and channel metadata over the network. This can lead operators to run it in contexts where external network use or disclosure of server/member information is unexpected, increasing the risk of inadvertent data exposure or policy violations.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The script automatically reads a Discord bot token from ~/.openclaw/openclaw.json and sends authenticated requests to Discord, optionally through a configured proxy, without any user confirmation or visible warning. In an agent-skill context, silent credential use and network transmission increase the risk of unintended access, auditing blind spots, and token exposure via an untrusted proxy or unexpected invocation.

Static analysis

No suspicious patterns detected.