Repository navigation
feat: judge open-redirect flows on the destination a browser resolves - #169
Merged
Merged
Conversation
taint.urls states what a URL's constant text proves: a fixed origin, a fixed path, a reference on the current site, and whether a call disables redirects. ssrf now reads it with unchanged verdicts, and open-redirect draws its own: refuted on the current site, a hotspot on a fixed host.
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.
Summary
open-redirectnow judges each flow on the destination a browser resolves from the target's constant text, with its own criteria. The facts it reads are a proof shared withssrf, in a new module,coretrace_python.taint.urls. It states what the constant text a URL starts with proves, and nothing more:Each rule draws its own verdict from it, with no exception coded per CVE.
Closes #168.
open-redirect/then a character that is not a slash, a backslash, a space or a control character ("/profile/" + id,f"/users/{id}/edit"),?,#, or a relative path whose first segment ends in the text without a:("profile/" + id)"https://example.com/" + path)"/" + next(//evil.comis another host),"/\\"or"/\t"before the input (a browser takes/\for//and drops tabs),"https://example.com" + x,"a:b/"or"java"before the input (a scheme may follow), an unknown or imported base, a target built in a helper function.The SSRF criteria do not carry over. A server-side request to a fixed host may still reach its internal paths, so
ssrfkeeps a fixed host as a hotspot and refutes only a fixed destination without redirects. A browser on the current site stays within the project's own code.Design
taint/urls.py.UrlProof(text, origin, path_fixed, relative)andurl_proof(text)state the proof.flow_url(flow, defs, strings)reads the constant text of the value a flow passes (abstract.strings). A flow reaching its sink in a callee proves nothing.redirects_disabled(arguments)holds when the call passesallow_redirects=Falseorfollow_redirects=Falseexplicitly.ssrfmoves onto the shared proof. Its tests, unchanged, pass unchanged.open-redirectrefines its verdict throughTaintDetector.judge, so the refutation's verdict, which other consumers read, does not change. Its manifest now requiresir.ssaandabstract.module_strings, likessrf.Corpus
The regression snapshots, which record each verdict, are unchanged. The three
open-redirectfindings of the corpus redirect to values built without constant text:app/redirect.py:11RedirectResponse(url=target): the whole target is inputhc/accounts/decorators.py:44redirect(request.path): a request path of//evil.comwould leave the sitehc/accounts/views.py:115redirect(redirect_url), validated by the projectVerification
New tests only; no existing test file is modified:
tests/test_url_proof.py: what each constant text proves, and when redirects are disabled;tests/test_open_redirect_destinations.py: each form, module strings, an imported base, a helper function, Django redirects, and the evidence text.The ten tests expecting a refutation or a hotspot failed before the change.
pytest: 1274 passed.pytest -m regression: 19 passed, unchanged.ruff checkandmypy: clean.