T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/render.sh:19
- Finding
- Unrestricted Card Image URL Enables Server-Side Request Forgery<![CDATA[ ## Vulnerability Details **File Location**: `scripts/render.sh:19-31`, `scripts/render.sh:216-229`, `scripts/render.sh:393-401`; related sink in `templates/card.html:70` **Vulnerability Type**: Server-Side Request Forgery (SSRF) through an insufficiently validated image URL **Risk Level**: Medium ### Vulnerable Code ```bash # HTML entity escaping for user-supplied text escape_html() { echo "$1" | sed 's/&/\&/g; s/</\</g; s/>/\>/g; s/"/\"/g; s/'"'"'/\'/g' } # Validate image URL (block dangerous protocols) validate_image_url() { local url="$1" # Block file://, javascript:, data: (except data:image), and vbscript: if [[ "$url" =~ ^(file:|javascript:|vbscript:) ]]; then echo "" return fi if [[ "$url" =~ ^data: ]] && [[ ! "$url" =~ ^data:image/ ]]; then echo "" return fi echo "$url" } ``` ```bash generate_card() { local data="$1" TITLE=$(echo "$data" | jq -r '.title // ""') SUBTITLE=$(echo "$data" | jq -r '.subtitle // ""') BODY=$(echo "$data" | jq -r '.body // ""') STATUS=$(echo "$data" | jq -r '.status // ""') IMAGE=$(echo "$data" | jq -r '.image // ""') # Escape all user-supplied text TITLE=$(escape_html "$TITLE") SUBTITLE=$(escape_html "$SUBTITLE") BODY=$(escape_html "$BODY") IMAGE_HTML="" if [[ -n "$IMAGE" && "$IMAGE" != "null" ]]; then # Validate image URL (block dangerous protocols) IMAGE=$(validate_image_url "$IMAGE") if [[ -n "$IMAGE" ]]; then IMAGE=$(escape_html "$IMAGE") IMAGE_HTML="<img src=\"$IMAGE\" class=\"card-image\" />" fi fi ``` ```html <body> <div class="card"> {{IMAGE_HTML}} ``` ```bash # Render with wkhtmltoimage /usr/bin/wkhtmltoimage \ --quiet \ --width "$WIDTH" \ --enable-javascript \ --javascript-delay 500 \ --format png \ "$TEMP_DIR/render.html" \ "$OUTPUT_FILE" ``` ### Technical Analysis The c ...[truncated 3320 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. **Disable remote images by default.** Accept embedded image data only after strict MIME-type, decoding, and size validation, or remove the `image` feature if it is unnecessary. 2. **Use an explicit allowlist if remote images are required.** - Parse the URL with a dedicated URL parser rather than shell regular expressions. - Permit only `https`. - Normalize the scheme and hostname before validation. - Allow only explicitly trusted image hosts. - Reject URLs containing credentials or ambiguous host syntax. 3. **Block internal destinations.** - Resolve the hostname before connecting. - Reject loopback, private, link-local, multicast, unspecified, and reserved IPv4 and IPv6 ranges. - Block cloud metadata endpoints. - Revalidate every redirect target and every DNS resolution result to prevent redirect and DNS-rebinding bypasses. 4. **Fetch images outside the HTML renderer.** Use a hardened downloader in a network-isolated process with: - Strict connection and total timeouts. - Redirect limits. - Response-size limits. - MIME-type and image-decoding validation. - Proxy restrictions. - Egress filtering. - A nonprivileged execution account. Save the validated result to a controlled temporary path and reference only that file during rendering. 5. **Harden `wkhtmltoimage`.** - Replace `--enable-javascript` with `--disable-javascript`. - Explicitly disable local file access where supported. - Run the renderer in a sandbox or container without access to sensitive files or internal networks. - Apply operating-system resource limits. 6. **Document the capability.** If card images remain supported, include the `image` field and its network behavior in `README.md` and `SKILL.md` so operators can make an informed trust decision. 7. **Add security tests** covering uppercase schemes, protocol-relative URLs, redirects, IPv4 and IPv6 loopback addresses, private ranges, link-loca ...[truncated 140 chars]
