T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/neuro/neuro-check.sh:163
- Finding
- Unescaped User-Controlled Data Written to Persistent JSON State## Vulnerability Details **File Location**: `scripts/neuro/neuro-check.sh`, lines 163–172 **Vulnerability Type**: JSON injection and persistent state corruption **Risk Level**: Medium ### Vulnerable Code ```bash cat > "$STATE_FILE" << EOF { "task": "$task", "task_type": "$task_type", "route": "$final_route", "circadian": "$circadian", "is_efficient": $is_efficient, "decided_at": "$(date -Iseconds)" } EOF ``` ### Technical Analysis The script directly interpolates user-controlled task text into a JSON document without applying JSON string escaping. The task originates from the first command-line argument: ```bash local task="${1:-}" ``` Consequently, double quotes, backslashes, control characters, and newlines in the task can terminate or alter the intended JSON string. An attacker can produce malformed JSON or inject additional object properties. The value of `circadian` is also interpolated without escaping after being read from a separate persistent state file. The generated content is stored at the fixed path `/root/.openclaw/workspace/.neuro/route-state.json`. Because this is persistent shared state, downstream workspace components may consume corrupted or attacker-influenced routing information. This is data-format injection rather than shell command injection: the here-document performs parameter expansion, but shell syntax contained inside an expanded variable is not reparsed as executable shell code. ### Attack Path 1. An attacker supplies a crafted task containing JSON syntax, such as: ```text x", "route": "attacker-controlled", "injected": "true ``` 2. `main` accepts the entire value as `task`, and the classification functions process it as ordinary text. 3. The here-document inserts the value directly between JSON quotation marks without serialization or escaping. 4. The resulting `route-state.json` is malformed or contains attacker-influenced properties. 5. A downstream component that reads and trusts thi ...[truncated 934 chars]
- Remediation
- ## Remediation Suggestions 1. Generate the state file with a genuine JSON serializer rather than a shell here-document. For example, pass each value to Python and use `json.dump`: ```bash python3 - "$STATE_FILE" "$task" "$task_type" "$final_route" \ "$circadian" "$is_efficient" "$(date -Iseconds)" <<'PY' import json import os import sys import tempfile destination, task, task_type, route, circadian, efficient, decided_at = sys.argv[1:] state = { "task": task, "task_type": task_type, "route": route, "circadian": circadian, "is_efficient": int(efficient), "decided_at": decided_at, } directory = os.path.dirname(destination) fd, temporary = tempfile.mkstemp(dir=directory, prefix=".route-state-", text=True) try: with os.fdopen(fd, "w", encoding="utf-8") as stream: json.dump(state, stream, ensure_ascii=False) stream.write("\n") stream.flush() os.fsync(stream.fileno()) os.chmod(temporary, 0o600) os.replace(temporary, destination) except Exception: try: os.unlink(temporary) except FileNotFoundError: pass raise PY ``` 2. Use atomic replacement so concurrent readers never observe a partially written document. 3. Apply restrictive permissions to the state directory and file, such as directory mode `0700` and file mode `0600`. 4. Prefer a caller-scoped or application-scoped state directory over a fixed shared root-workspace path when multiple trust domains may invoke the script. 5. Validate the circadian state schema and require `name` to be a string of an acceptable length before propagating it. 6. Add regression tests with quotation marks, backslashes, newlines, Unicode characters, and duplicate-key injection payloads, then verify that the resulting file always parses to the intended object.
