Repository navigation
Fix high and medium scan safety issues - #2
Merged
Merged
Conversation
Stop cross-host and private redirects, cap response bodies, recover unverified TLS certificates, and parse IPv6 and explicit ports before DNS or socket connect. Pin runtime dependencies and audit them in CI. Co-authored-by: Kamal Choubey <Phantom9869@users.noreply.github.com>
Validate each redirect against the URL requests will connect to, dial the addresses that check vetted, resolve names with the system resolver under one deadline, and follow same-host redirects for private literals. Co-authored-by: Kamal Choubey <Phantom9869@users.noreply.github.com>
The pinned session kept trust_env off, so REQUESTS_CA_BUNDLE was ignored and a certificate failure was retried as plain HTTP. Apply the env CA bundle explicitly, and surface TLS verification errors instead of downgrading. Follow same-host redirects for names that resolve only to non-public addresses, still blocking a public-to-private pivot. Check proxies per URL, limit redirect ports to the same port or 80/443, return the 3xx when a followed hop fails, and resolve a port scan's host once. Co-authored-by: Kamal Choubey <Phantom9869@users.noreply.github.com>
Fix redirect SSRF parsing, DNS rebinding, and private-target redirects
Phantom9869
marked this pull request as ready for review
October 21, 2026 07:37
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.
Fixes the High and Medium findings from the security review. Low findings are unchanged.
python -m pytest -q— 155 passed.Fixes
High
gambit.http.requestno longer usesallow_redirects=True. A redirect is followed only when the next URL ishttp/https, the hostname matches, and every resolved address is globally routable (ipaddress.is_global). Chains stop at 5 hops. A followed redirect records the final URL on the finding. The first request to a user-specified private target is still allowed.test_cross_host_redirect_is_not_followed,test_same_host_redirect_to_a_private_address_is_not_followed,test_same_host_redirect_followed_when_address_is_public,test_redirect_chain_is_capped,test_run_records_final_url_after_a_same_host_redirect.Medium
Report injection. Text reports collapse control characters so evidence cannot forge a new heading. Markdown additionally HTML-escapes
&,<,>and backslash-escapes markdown specials. JSON stays raw.test_text_and_markdown_strip_injected_markup_json_keeps_it.TLS re-check dropping the cert. After
CERT_NONE, an emptygetpeercert()is recovered fromgetpeercert(binary_form=True)via a temporary PEM andssl._ssl._test_decode_cert, so expiry is still graded.test_getpeercert_empty_dict_is_recovered_from_der,test_handshake_reads_binary_cert_when_getpeercert_returns_empty.IPv6 host:port parsing. Bare IPv6 is bracketed in
normalize_host.split_host_portreturns the unbracketed address plus an in-range port. URLs re-bracket only real IPv6 literals.example.com:notaportis unchanged.test_split_host_port_ipv6_and_out_of_range_ports,test_ipv6_literal_is_bracketed_in_the_url.Preflight port vs URL port. An open non-default port (8443, 8080, …) is remembered and used on later URLs. 443 and 80 stay scheme defaults. An explicit port on the target always wins.
test_preflight_open_alt_port_is_used_in_the_url.Explicit port sent to DNS, and port wrap. DNS, crt.sh, and CNAME queries use the hostname with the port stripped. Ports outside 1–65535 are rejected before
getaddrinfocan wrap them (99999 → 34463). A valid explicit port is probed in addition to the standard list.test_dns_and_crtsh_drop_an_explicit_port,test_out_of_range_port_does_not_connect,test_explicit_port_is_probed_and_out_of_range_port_is_not,test_connect_refuses_a_port_that_would_wrap.Unbounded response bodies. Non-streaming responses are read through
iter_contentand capped at 1 MiB (max_bytesfor callers that want less). Callerstream=Trueis left unread. crt.sh fetches do not follow redirects.test_response_body_is_capped.Takeover fingerprint false positives. A fingerprint match counts only when status is missing or ≥ 400, so a live HTTP 200 is not HIGH. NXDOMAIN is HIGH only for services marked
claimable. Generic IIS, GitLab, and WordPress strings are no longer fingerprints. Existing GitHub NXDOMAIN and “status omitted” cases stay HIGH.test_fingerprint_on_http_200_is_not_high_and_404_still_is,test_unclaimable_nxdomain_and_generic_iis_text_are_not_high.Dependency pins and pip-audit.
pyproject.tomlandrequirements.txtpindnspython==2.8.0,requests==2.34.2, andurllib3==2.8.0. CI installspip-auditand auditsrequirements.txt.test_runtime_dependencies_are_pinned_and_ci_audits_them.Socket connect DNS timeouts.
gambit.net.connectresolves with dnspythonlifetimeand dials the IP, sosocket.create_connectiondoes not callgetaddrinfoon a hostname before the timeout starts. Used by the liveness preflight, the port scan, and the TLS handshake.test_connect_resolves_with_a_deadline_then_dials_the_ip,test_public_address_check_rejects_metadata_and_private_ranges.