T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/find-duplicates.sh:9
- Finding
- User-Controlled Property Name Is Interpolated into Python Source Code<![CDATA[ ## Vulnerability Details **File Location**: `scripts/find-duplicates.sh`, lines 9 and 26–32 **Vulnerability Type**: Python source injection leading to arbitrary local command execution **Risk Level**: High ### Vulnerable Code ```bash PROPERTY="${2:?Usage: find-duplicates.sh <object-type> <property>}" ``` ```bash RESULTS=$(echo "$RESP" | python3 -c " import sys, json data = json.load(sys.stdin) for r in data.get('results', []): val = r.get('properties', {}).get('$PROPERTY', '') if val: print(f\"{r['id']}|{val}\") " 2>/dev/null) ``` ### Technical Analysis The `PROPERTY` command-line argument is inserted directly into a Python program supplied to `python3 -c`. Although the shell variable is expanded inside a double-quoted shell string, it becomes part of executable Python source code. An attacker-controlled value containing Python string delimiters and additional Python statements can terminate the intended string expression and inject new statements. The injected Python executes with the same operating-system privileges and environment as the shell script. This is not required for duplicate detection. The property name should be passed as data through a positional argument or environment variable rather than used to generate Python source. The same value is also included in a HubSpot API URL without explicit validation or URL encoding. That can alter query semantics, although the fixed HTTPS origin prevents it from independently redirecting the bearer token to another host. ### Attack Path 1. An attacker influences the property argument passed to `find-duplicates.sh`, such as through an automated Agent task or an untrusted user request. 2. The script assigns the value to `PROPERTY` without validating it. 3. The value is interpolated into the Python expression: `get('$PROPERTY', '')`. 4. A malicious value closes the intended Python string and introduces additional Python statements, such as importing an operating-system exec ...[truncated 917 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions Do not interpolate command-line values into Python source. Pass the property as a separate argument: ```bash RESULTS=$(printf '%s' "$RESP" | python3 -c ' import json import sys prop = sys.argv[1] data = json.load(sys.stdin) for record in data.get("results", []): value = record.get("properties", {}).get(prop, "") if value: print(f"{record['id']}|{value}") ' "$PROPERTY") ``` Apply strict validation before using the object type or property: ```bash case "$OBJECT_TYPE" in contacts|companies|deals|tickets) ;; *) echo "Unsupported object type" >&2 exit 1 ;; esac if [[ ! "$PROPERTY" =~ ^[A-Za-z0-9_]+$ ]]; then echo "Invalid property name" >&2 exit 1 fi ``` Additional hardening should include: 1. Construct query strings with URL encoding instead of direct concatenation. 2. Avoid suppressing all Python errors with `2>/dev/null`, because doing so hides malformed input and exploitation attempts. 3. Validate API responses and fail explicitly when HubSpot returns an error object. 4. Run shell static analysis and add tests using quotes, newlines, separators, and other hostile property values. ]]>
