T09 · Insecure Skill Coding Practices
- Location
scripts/send-message.sh:21- Finding
Unescaped Author Value Permits JSON Payload Injection
- Content
View full analysis
Vulnerability Details
File Location:
scripts/send-message.sh, lines 21–31
Vulnerability Type: JSON injection caused by unsafe string interpolation
Risk Level: MediumVulnerable Code
bash # Build JSON payload with proper escaping PAYLOAD=$(cat <<EOF { "content": $(echo "$MESSAGE" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read().strip()))'), "author": "$AUTHOR", "metadata": { "source": "openclaw", "timestamp": $(date +%s) } } EOF )Technical Analysis
The script safely serializes
MESSAGEwith Python'sjson.dumps(), but directly interpolates the attacker-controlledAUTHORargument into a JSON string. Quotes, backslashes, delimiters, and object members inAUTHORare therefore interpreted as JSON syntax rather than data.A crafted author can terminate the intended
authorstring and inject additional properties. Depending on how the BORT runtime parses duplicate keys, an injected latercontentormetadataproperty may override the script-generated value. Even when duplicate keys are rejected, an attacker can cause malformed requests and denial of service for this operation.This is JSON/data injection, not shell command injection: the author is expanded inside a here-document and is not passed to
evalor otherwise executed as shell code.Attack Path
-
An attacker obtains the ability to control the optional third argument to
send-message.sh. -
The attacker supplies an author containing a closing quote and additional JSON fields, conceptually similar to:
text attacker", "content": "replacement instruction", "extra": " -
The script inserts that value directly into the JSON here-document.
-
The generated payload contains attacker-selected structure or duplicate fields.
-
curlsends the manipulated payload to:text $BORT_RUNTIME_URL/agents/$AGENT_ID/messages -
If the runtime accepts the resulting JSON and resolves duplicate fields in the ...[truncated 687 chars]
-
- Remediation
View remediation
Remediation Suggestions
Construct the entire payload with one JSON serializer rather than interpolating individual values into a here-document. Pass all dynamic values as arguments or environment variables and let the serializer encode them:
bash TIMESTAMP=$(date +%s) PAYLOAD=$(python3 - "$MESSAGE" "$AUTHOR" "$TIMESTAMP" <<'PY' import json import sys message, author, timestamp = sys.argv[1:4] print(json.dumps({ "content": message, "author": author, "metadata": { "source": "openclaw", "timestamp": int(timestamp), }, })) PY )Additionally:
-
Validate
AGENT_IDagainst a strict non-negative integer pattern before inserting it into the URL:bash [[ "$AGENT_ID" =~ ^[0-9]+$ ]] || { echo "Error: agentId must be a non-negative integer" >&2 exit 1 } -
Consider imposing reasonable length limits on
MESSAGEandAUTHOR. -
Use
curl --fail-with-body --show-errorand explicit connection and request timeouts. -
Add regression tests using author values containing quotes, backslashes, newlines, braces, Unicode, and attempted duplicate properties.
-
Ensure the receiving API rejects duplicate JSON keys or validates the parsed request against a strict schema.
-
