T09 · Insecure Skill Coding Practices
- Location
scripts/pr-check.sh:74- Finding
Arbitrary Webhook Destination Enables SSRF and Information Disclosure
- Content
View full analysis
Vulnerability Details
File Location:
scripts/pr-check.sh:74-85
Vulnerability Type: Unrestricted outbound request / SSRF
Risk Level: Highbash payload=$(jq -n \ --arg ts "$ts" \ --arg pr "$PR_URL" \ --arg health "$health_summary" \ '{ embeds: [{ title: ("PR Auto-Check: " + $pr), color: (if ($health | test("✅")) then 3066993 elif ($health | test("⚠️")) then 16776960 else 15158332 end), fields: [ {name: "Health Check", value: $health, inline: false}, {name: "PR", value: $pr, inline: false} ], footer: {text: ("Reported at " + $ts)} }] }') curl -sS -X POST "$DISCORD_WEBHOOK" \ -H 'Content-Type: application/json' \ -d "$payload" > /dev/null 2>&1 || echo "Discord webhook failed"Technical Analysis
The second command-line argument is documented as a Discord webhook, but the script passes it directly to
curlwithout validating its scheme, hostname, port, resolved IP address, or destination type. Consequently, the caller can cause the process to send an HTTP POST request to any URL reachable from the execution environment.The transmitted payload contains the supplied PR URL and health-check information. If the script operates inside a trusted network, the request can target loopback, link-local, private-network, or internal service addresses. Suppression of the response body does not prevent state-changing requests or establish that the destination is safe.
Attack Path
- An attacker or untrusted automation supplies a URL such as an internal service endpoint as the second argument.
- The script performs the PR and health checks and constructs a JSON payload.
curlsends the payload to the attacker-selected endpoint.- The endpoint receives repository and health information, or the POST request triggers an internal operation accessible from the execution environment.
Impact Assessment
...[truncated 395 chars]
- Remediation
View remediation
Remediation Suggestions
- Parse the destination URL before invoking
curl. - Require HTTPS and allow only explicitly approved Discord webhook hostnames and URL paths.
- Resolve the hostname and reject loopback, link-local, private, multicast, and other non-public address ranges, including after DNS resolution.
- Disable redirects with
--max-redirs 0, or independently validate every redirect target. - Store the webhook in trusted configuration or a secret manager instead of accepting it from untrusted task input.
- Apply outbound firewall or proxy allowlisting as defense in depth.
- Avoid including sensitive health details in notifications unless they are required.
- Parse the destination URL before invoking
