T09 · Insecure Skill Coding Practices
- Location
scripts/navigate.sh:39- Finding
Arbitrary Python Code Execution Through Unsanitized Node Argument
- Content
View full analysis
Vulnerability Details
File Location:
scripts/navigate.sh, lines 39-52
Vulnerability Type: Python source-code injection
Risk Level: HighVulnerable 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
codecommand passes its second command-line argument toget_node_code, where it is assigned to the shell variablenode. That variable is then interpolated directly into source code supplied topython3 -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_FILEis 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-controllednodevalue.Attack Path
-
An attacker constructs a node argument containing Python syntax that closes the single-quoted
nodestring. -
The attacker directly invokes, or persuades a user or agent to invoke, the following command pattern:
bash ./scripts/navigate.sh code "ATTACKER_CONTROLLED_VALUE" -
The main command dispatcher forwards the value as
$2:bash get_node_code "$2" -
get_node_codeexpands that value ...[truncated 1036 chars]
-
- Remediation
View 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:
- Validate node identifiers against the keys loaded from
flow-rules.json; do not attempt to sanitize input for safe source-code interpolation. - Pass all dynamic values through
sys.argv, standard input, or environment variables rather than embedding them in executable code. - Return a nonzero status for invalid nodes and data-loading failures.
- Run the script with the minimum filesystem, credential, and network permissions required.
- Add regression tests using node values containing quotes, semicolons, newlines, command-substitution syntax, and Python metacharacters, verifying that all such values remain inert data.
- Apply the same code-versus-data separation rule to future shell integrations with Python or other interpreters.
- Validate node identifiers against the keys loaded from
