Repository navigation
Conversation
Rework the SSRF hardening from #18 so it guards the one request the SDK actually makes, without breaking startup or legitimate blueprint URLs. - Filter resolved addresses in an OkHttp Dns (PublicAddressDns) so the checked address is the one connected to, for every redirect hop. Also covers IPv6 ULA, multicast, 0.0.0.0/8, 198.18/15, 240/4 and IPv4 embedded in IPv6; IP literals are checked explicitly. - Validate the manifest URL lazily instead of resolving DNS while the SDK is constructed. - Follow redirects with HttpUrl.resolve, re-checking https on each hop, capped at 5. - Cap the manifest at 1 MiB of real bytes via Okio instead of the char-counting BoundedReader, and wrap JSON errors consistently. - Drop the SDK-side serverUrl/download-link checks and the duplicated builder check: the serverhost performs the download and must enforce the policy there. - Replace the plain-http test server with an HTTPS MockWebServer and add the redirect, size and address policy tests #18 described. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Check IPv4 embedded in IPv4-mapped (::ffff:0:0/96) and translated (::ffff:0:0:0/96) resolver answers; the JDK only converts mapped literals, so AAAA answers like ::ffff:127.0.0.1 bypassed the policy. Reject local-use NAT64 64:ff9b:1::/48. - Block 192.0.0.0/24 and the TEST-NET ranges again. - Reject ambiguous numeric hosts (127.1, 1.2.3.4.5) that OkHttp would hand to InetAddress.getByName and system DNS, bypassing Dns. - Bound the whole fetch, across redirects, by a 30s deadline so a slow server can't hold the manifest lock indefinitely. - Strip userinfo and query from URLs in error messages; distinguish a missing Location header from an invalid one. - Tests: assert why https enforcement fails rather than relying on a TLS handshake failure, cover resolver-form IPv4-mapped answers, the deadline, redaction, and the production client's Dns wiring. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Follow-up to #18, before
manifest-ssrfis merged intomain. Claude Opus 5.5 and GPT-6.1-Sol each reviewed #18 separately and reached the same conclusions; this PR applies their findings.Problems in #18
ManifestServerUrlResolverTesttests failed (their mock server useshttp://127.0.0.1). The 4 tests listed in the PR description were never added.CloudApiconstruction failed whenever DNS was unavailable, even if inline blueprints were never used.fc00::/7, e.g. AWS IMDSfd00:ec2::254), multicast,0.0.0.0/8and IPv4 embedded in IPv6.BoundedReadercounted characters ×2, so the effective cap was about 512 KiB of ASCII. A body of exactly the limit was rejected, and the overflow error came out as aJsonSyntaxException.serverUrlchecks: checkingserverUrland manifest download links in the SDK protects nothing, because the SDK only forwards them. It also blocks http and internal mirrors.Changes
PublicAddressDns(OkHttpDns): rejects any hostname that resolves to a non-public address. The checked addresses are the ones OkHttp connects to, on every redirect hop. IP literals, which skipDns, are checked explicitly.HttpUrl; https is enforced on every hop. The client never follows redirects automatically, including clients injected in tests.HttpUrl.resolveand capped at 5.source.request(MAX + 1). Works with or withoutContent-Length. JSON and fetch errors are now consistentlyIllegalStateException.CloudApiOptionsbuilder check (duplicated, and skipped for the env var) and theserverUrl/download-link checks inInlineBlueprintSupport.ManifestServerUrlResolverTestnow uses an HTTPSMockWebServer(new test depsmockwebserverandokhttp-tls). It covers https enforcement, private literals and private DNS answers, redirect following, redirect-to-http, redirect-to-metadata IP, the redirect cap, the size limit (exact, over, chunked) and malformed JSON. A newPublicAddressDnsTesttable-tests the address policy without network access.Threat model / scope
This PR only guards the one request the SDK makes itself: fetching
server_versions.json. Download links (explicitserverUrlor from the manifest) are forwarded unchanged, because the serverhost downloads them. That is where scheme, address and redirect checks have to run; that work is tracked as a separate PR in platform-serverhost.Known limitation (documented on
CloudApiOptions.Builder#serverVersionManifestUrl): if a JVM-wide HTTP proxy is configured, the proxy resolves the target host, so address filtering applies to the proxy host instead. A proxy reached through a private hostname will therefore be refused.Second review round
Two fresh reviewers (Claude Opus 5.5 and GPT-6.1-Sol) with no prior context reviewed the first commit. Fixed in c71b889:
::ffff:127.0.0.1) slipped past the check, because the JDK only converts mapped literals to IPv4. Reproduced end to end and fixed. Local-use NAT6464:ff9b:1::/48is now rejected too.192.0.0.0/24and the TEST-NET ranges are blocked again.127.1,2130706433,1.2.3.4.5) are rejected instead of reaching system DNS outsideDns.Locationheader is reported separately from an invalid one.Dnswiring.Tests
./gradlew :api:check :api:javadoc: 140 tests, 0 failures.🤖 Generated with Claude Code