You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
get-image-metadata (public MCP tool) accepted destinations the guard did not cover. This PR:
Adds an IP classifier (code/src/utils/ssrf.ts) rejecting loopback, RFC 1918, link-local, CGNAT, ULA (fc00::/7), fe80::/10, multicast/reserved, NAT64 and IPv4-mapped/compatible IPv6.
Makes assertSafeUrl use it, so IPv6 literals are covered everywhere it is used.
Adds safeFetchBuffer: resolves the host, rejects if any address is blocked, and connects only to a validated address (single lookup, no rebinding window). No redirects, 10 MB cap, 10 s timeout. No new dependency.
Blocked destinations return the same generic error, without the resolved IP.
Local harnesses: mapped/ULA/link-local/metadata/localhost literals and hostnames resolving to private or mixed addresses are rejected and the local server is never hit; a public image still returns metadata; redirect, oversize and timeout fail; DNS pinning does a single lookup.
Residual risk
transformMediaUrl (canvas/weave.ts) is literal-only, since the canvas runtime performs that fetch and cannot be validated here.
Reject IPv6 private, link-local, ULA and IPv4-mapped destinations, and
validate the IPs a hostname resolves to. Fetch connects only to the
validated address, without following redirects.
Refs #136
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
alexmontesg
added
internal
Internal changes out of the scope for changelog files
and removed
internal
Internal changes out of the scope for changelog files
labels
Oct 5, 2026
Block site-local, local-use NAT64, IPv4-translated, 6to4 and Teredo
ranges, reject every IPv4-mapped address after canonicalizing it, destroy
the request on early rejections and enforce a total fetch deadline.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Unlike the previous fetch implementation, http.request does not decode HTTP content encodings. A successful PNG response with Content-Encoding: gzip returns compressed bytes here, which are passed directly to sharp and fail image detection. Decode supported content encodings before returning the image, enforcing maxBytes on the decoded stream as well to prevent decompression bombs.
Allow only global unicast IPv6 minus special-purpose ranges, reject protocol
upgrades, decode gzip/deflate/br with the size cap on decoded bytes and
reject (not resolve) when the body exceeds the cap.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Reply to the 'previously missed' note (Content-Encoding): valid. Node's http.request does not decode, unlike fetch. Fixed in 1e59e12: gzip/deflate/br are decoded, unknown encodings are rejected, and maxBytes applies to the decoded stream (a 50 MB gzip bomb is rejected at the cap). While testing this I also found that exceeding the cap while streaming resolved with an empty buffer instead of rejecting; that is fixed too.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #136.
get-image-metadata(public MCP tool) accepted destinations the guard did not cover. This PR:code/src/utils/ssrf.ts) rejecting loopback, RFC 1918, link-local, CGNAT, ULA (fc00::/7),fe80::/10, multicast/reserved, NAT64 and IPv4-mapped/compatible IPv6.assertSafeUrluse it, so IPv6 literals are covered everywhere it is used.safeFetchBuffer: resolves the host, rejects if any address is blocked, and connects only to a validated address (single lookup, no rebinding window). No redirects, 10 MB cap, 10 s timeout. No new dependency.Independent of the MCP authorization PR (#137).
Verification
npm run lintandnpm run buildpass.Residual risk
transformMediaUrl(canvas/weave.ts) is literal-only, since the canvas runtime performs that fetch and cannot be validated here.