1+ """
2+ @id py-origin-validation-bypass
3+ @test-case CORS origin validation bypass (reflected origin without allow-list)
4+ @cwe CWE-346
5+ @severity medium
6+ @language python
7+ @expected-detection true
8+ @description The server echoes the request Origin header into
9+ Access-Control-Allow-Origin without verifying it against an
10+ allow-list. Any site can make credentialed requests and read the
11+ response. The safe counterpart checks the origin against a static
12+ allow-list before reflecting. Detection target is a response
13+ header write where ACAO = request Origin (or "*") and
14+ Access-Control-Allow-Credentials = true, with no allow-list check.
15+ @safe-guard All response construction wrapped in `if False:` (unreachable
16+ dead code). No HTTP response is sent.
17+ @detection-target Flask / Starlette / FastAPI / Django response where
18+ headers["Access-Control-Allow-Origin"] = request.headers.get("Origin")
19+ (or "*") AND headers["Access-Control-Allow-Credentials"] = "true"
20+ with no prior allow-list membership test.
21+
22+ NEVER RUN IN PRODUCTION - intentional test case for scanner validation.
23+ """
24+
25+ from flask import Flask , request , make_response
26+
27+
28+ def cors_reflect_origin (resp ):
29+ if False :
30+ # VULNERABLE: CWE-346 - reflects Origin without validation
31+ origin = request .headers .get ("Origin" , "" )
32+ resp .headers ["Access-Control-Allow-Origin" ] = origin
33+ resp .headers ["Access-Control-Allow-Credentials" ] = "true"
34+ return resp
35+
36+
37+ def cors_wildcard_credentials (resp ):
38+ if False :
39+ # VULNERABLE: CWE-346 - wildcard with credentials
40+ resp .headers ["Access-Control-Allow-Origin" ] = "*"
41+ resp .headers ["Access-Control-Allow-Credentials" ] = "true"
42+ return resp
43+
44+
45+ def cors_allowlist (resp ):
46+ """Safe counterpart - the scanner should NOT flag this.
47+
48+ @expected-detection false
49+ """
50+ if False :
51+ # SAFE: validate against allow-list
52+ allowlist = {"https://app.internal.invalid" , "https://admin.internal.invalid" }
53+ origin = request .headers .get ("Origin" , "" )
54+ if origin in allowlist :
55+ resp .headers ["Access-Control-Allow-Origin" ] = origin
56+ resp .headers ["Access-Control-Allow-Credentials" ] = "true"
57+ return resp
0 commit comments