T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/navigate.sh:39
- Finding
- Arbitrary Python Code Execution Through Unsanitized Node Argument## Vulnerability Details **File Location**: `scripts/navigate.sh`, lines 39-52 **Vulnerability Type**: Python source-code injection **Risk Level**: High ### Vulnerable Code ```bash get_node_code() { local node=$1 if [ -f "$DATA_FILE" ]; then python3 -c " import json with open('$DATA_FILE') as f: data = json.load(f) node = '$node' if node in data['nodes']: n = data['nodes'][node] print(f\"校验:{n.get('check', 'N/A')}\") print(f\"执行:{n.get('exec', 'N/A')}\") print(f\"下一步:{n.get('next', 'N/A')}\") else: print(f'节点 {node} 未找到') " ``` ### Technical Analysis The `code` command passes its second command-line argument to `get_node_code`, where it is assigned to the shell variable `node`. That variable is then interpolated directly into source code supplied to `python3 -c`: ```bash node = '$node' ``` Shell expansion occurs before Python parses the resulting program. Consequently, an attacker-controlled argument containing a single quote and additional Python syntax can terminate the intended string literal and inject arbitrary Python statements. This is not merely malformed-input handling. Injected Python can import operating-system modules and start arbitrary local programs. No validation, escaping, or separation between code and data prevents this behavior. Although `DATA_FILE` is also embedded into the generated Python source, it is derived from the script directory rather than directly from a command-line argument. The confirmed exploit path is the user-controlled `node` value. ### Attack Path 1. An attacker constructs a node argument containing Python syntax that closes the single-quoted `node` string. 2. The attacker directly invokes, or persuades a user or agent to invoke, the following command pattern: ```bash ./scripts/navigate.sh code "ATTACKER_CONTROLLED_VALUE" ``` 3. The main command dispatcher forwards the value as `$2`: ```bash get_node_code "$2" ``` 4. `get_node_code` expands that value ...[truncated 1036 chars]
- Remediation
- ## Remediation Suggestions Do not generate Python source code by interpolating shell arguments. Pass the data file and node value as positional arguments so Python receives them strictly as data: ```bash get_node_code() { local node=$1 if [ -f "$DATA_FILE" ]; then python3 - "$DATA_FILE" "$node" <<'PY' import json import sys data_file = sys.argv[1] node = sys.argv[2] with open(data_file, encoding="utf-8") as f: data = json.load(f) if node in data["nodes"]: entry = data["nodes"][node] print(f"Check: {entry.get('check', 'N/A')}") print(f"Execute: {entry.get('exec', 'N/A')}") print(f"Next: {entry.get('next', 'N/A')}") else: print(f"Node {node} not found") PY else echo -e "${RED}Data file not found${NC}" return 1 fi } ``` Additional hardening measures should include: 1. Validate node identifiers against the keys loaded from `flow-rules.json`; do not attempt to sanitize input for safe source-code interpolation. 2. Pass all dynamic values through `sys.argv`, standard input, or environment variables rather than embedding them in executable code. 3. Return a nonzero status for invalid nodes and data-loading failures. 4. Run the script with the minimum filesystem, credential, and network permissions required. 5. Add regression tests using node values containing quotes, semicolons, newlines, command-substitution syntax, and Python metacharacters, verifying that all such values remain inert data. 6. Apply the same code-versus-data separation rule to future shell integrations with Python or other interpreters.
