T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/_lib.sh:58
- Finding
- Unencoded User Input Permits Cloudflare API Query-Parameter Injection<![CDATA[ ## Vulnerability Details **File Location**: `scripts/_lib.sh:58-61`; additional affected call sites include `scripts/dns/delete.sh:54-59`, `scripts/dns/list.sh:52-57`, `scripts/dns/update.sh:59-64`, `scripts/tunnels/configure.sh:49-51`, `scripts/tunnels/delete.sh:46-48`, `scripts/tunnels/list.sh:40-43`, `scripts/tunnels/token.sh:44-47`, and `scripts/zones/get.sh:40` **Vulnerability Type**: API query-parameter injection and unsafe resource selection **Risk Level**: Medium ### Vulnerable Code ```bash # scripts/_lib.sh:58-61 get_zone_id() { local domain="$1" local response=$(cf_get "/zones?name=$domain") echo "$response" | jq -r '.result[0].id // empty' } ``` ```bash # scripts/dns/delete.sh:54-59 # Find the record FULL_NAME="$NAME.$DOMAIN" [ "$NAME" = "@" ] && FULL_NAME="$DOMAIN" RECORDS=$(cf_get "/zones/$ZONE_ID/dns_records?name=$FULL_NAME&type=$TYPE") RECORD_ID=$(echo "$RECORDS" | jq -r '.result[0].id // empty') ``` ```bash # scripts/dns/update.sh:59-64 # Find the record FULL_NAME="$NAME.$DOMAIN" [ "$NAME" = "@" ] && FULL_NAME="$DOMAIN" RECORDS=$(cf_get "/zones/$ZONE_ID/dns_records?name=$FULL_NAME&type=$TYPE") RECORD_ID=$(echo "$RECORDS" | jq -r '.result[0].id // empty') ``` ```bash # scripts/tunnels/delete.sh:46-48 # Get tunnel ID from name TUNNELS=$(cf_get "/accounts/$ACCOUNT_ID/cfd_tunnel?name=$TUNNEL_NAME") TUNNEL_ID=$(echo "$TUNNELS" | jq -r '.result[0].id // empty') ``` ### Technical Analysis User-controlled domains, DNS record names, record types, and tunnel names are interpolated directly into Cloudflare API URLs without URL encoding. Characters such as `&`, `=`, `?`, or encoded delimiters can alter the query string and introduce additional API parameters. The affected mutation operations select `.result[0]` without subsequently verifying that the returned resource exactly matches the requested domain, name, and type. If injected parameters broaden or otherwise change the result set, the first result may not be the intende ...[truncated 1470 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. URL-encode every user-controlled query value before constructing endpoints. For example: ```bash urlencode() { jq -rn --arg value "$1" '$value | @uri' } encoded_domain=$(urlencode "$domain") response=$(cf_get "/zones?name=$encoded_domain") ``` Alternatively, redesign the request helper to use `curl --get --data-urlencode` for query parameters. 2. Apply encoding consistently to: - Zone names. - DNS record names and types. - Tunnel names. - Any future filter, pagination, or search parameters. 3. Validate inputs before making requests: - Require syntactically valid domain and hostname values. - Restrict DNS record types to an explicit allowlist. - Reject control characters and unexpected query delimiters. - Enforce documented tunnel-name constraints. 4. Never rely only on `.result[0]`. Require exactly one result and verify its fields: ```bash MATCH_COUNT=$(echo "$RECORDS" | jq '.result | length') [ "$MATCH_COUNT" -eq 1 ] || { echo "Expected exactly one matching record" >&2 exit 1 } RETURNED_NAME=$(echo "$RECORDS" | jq -r '.result[0].name') RETURNED_TYPE=$(echo "$RECORDS" | jq -r '.result[0].type') [ "$RETURNED_NAME" = "$FULL_NAME" ] && [ "$RETURNED_TYPE" = "$TYPE" ] || { echo "API result does not exactly match the requested record" >&2 exit 1 } ``` 5. Prefer immutable resource IDs for destructive operations when practical, and display all verified identifying fields before requesting confirmation. ]]>
