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

Content
View full analysis

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
View 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

Content
View full analysis

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
View 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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.