# PYTHON-FLASK-AUDIT-004: Flask CORS Wildcard Origin

> **Severity:** MEDIUM | **CWE:** CWE-942 | **OWASP:** A05:2021

- **Language:** Python
- **Category:** Flask
- **URL:** https://codepathfinder.dev/registry/python/flask/PYTHON-FLASK-AUDIT-004
- **Detection:** `pathfinder scan --ruleset python/PYTHON-FLASK-AUDIT-004 --project .`

## Description

This rule detects Flask applications that initialize Flask-CORS with a wildcard origin
(origins="*"). The Access-Control-Allow-Origin: * header instructs browsers to allow any
web page -- on any domain, including attacker-controlled sites -- to make cross-origin
requests to the application and read the responses.

The critical risk arises when wildcard CORS is combined with credentials (cookies, HTTP
auth, or client-side certificates). While browsers prevent credentials from being sent with
a pure wildcard response, the misconfiguration often evolves: developers add
supports_credentials=True and then change origins="*" to origins="null" or reflect the
Origin header, inadvertently creating a credentialed CORS bypass. Auditing wildcard CORS
early prevents this class of vulnerability from appearing in the first place.

The detection uses FlaskCORS.method("CORS").where("origins", "*") -- a QueryType-based
precise match. FlaskCORS declares fqns=["flask_cors"], so only objects imported from the
flask_cors package are matched. The .where("origins", "*") filter means only calls where
the origins keyword argument is exactly the string "*" are flagged. Calls with a specific
domain list, a regex, or a variable are not flagged. This precision delivers zero false
positives on correctly configured applications.


## Vulnerable Code

```python
from flask import Flask
from flask_cors import CORS

app = Flask(__name__)
CORS(app, origins="*")
```

## Secure Code

```python
from flask import Flask
from flask_cors import CORS

app = Flask(__name__)

# SAFE: Restrict CORS to specific trusted origins
CORS(app, origins=["https://app.example.com", "https://admin.example.com"])

# For APIs that must be public, restrict to specific HTTP methods
# and do not enable supports_credentials:
CORS(app, origins=["https://trusted-partner.com"], methods=["GET"])

# For truly public read-only APIs, wildcard is acceptable only
# if no authentication is used and no sensitive data is returned:
# CORS(app, origins="*", supports_credentials=False)  # document this decision

```

## Detection Rule (Python SDK)

```python
from rules.python_decorators import python_rule
from codepathfinder import calls, Or, QueryType

class FlaskCORS(QueryType):
    fqns = ["flask_cors"]


@python_rule(
    id="PYTHON-FLASK-AUDIT-004",
    name="Flask CORS Wildcard Origin",
    severity="MEDIUM",
    category="flask",
    cwe="CWE-942",
    tags="python,flask,cors,wildcard,CWE-942",
    message="CORS configured with wildcard origin '*'. Restrict to specific domains.",
    owasp="A05:2021",
)
def detect_flask_cors_wildcard():
    """Detects CORS(app, origins='*')."""
    return FlaskCORS.method("CORS").where("origins", "*")
```

## How to Fix

- Replace origins="*" with an explicit list of trusted origins -- for example, ["https://app.example.com", "https://admin.example.com"].
- If you need to allow multiple environments (dev, staging, prod), maintain an allow-list per environment and inject it via configuration rather than hardcoding "*".
- Never combine origins="*" with supports_credentials=True -- browsers block this and developers tend to work around it in insecure ways.
- Restrict CORS to the minimum required HTTP methods (methods=["GET", "POST"]) rather than allowing all methods.
- Audit CORS configuration changes in code review as a security-sensitive change, similar to changes to authentication middleware.

## Security Implications

- **Cross-Origin Data Theft from Authenticated Users:** With wildcard CORS, any malicious web page can make JavaScript fetch() calls to the
application's API and read the JSON responses. If the user visiting the malicious page
is already authenticated (via cookie or session), the request carries their credentials
and the attacker's page can silently exfiltrate account data, private API responses,
or internal state.

- **Cross-Site Request Forgery Amplification:** Wildcard CORS allows attackers to use JavaScript XMLHttpRequest with full access to
response bodies, not just fire-and-forget form submissions. This upgrades CSRF from
blind state-changing attacks to data-reading attacks where the attacker can actually
see what the server returns.

- **Credential-Based Bypass When supports_credentials Is Added Later:** Applications often start with origins="*" and later add supports_credentials=True as
authentication is added. At that point, browsers block the combination. Developers then
change to reflecting the Origin header or using origins="null", both of which are
exploitable. Fixing the wildcard origin early prevents this progression.

- **Internal API Exposure:** Wildcard CORS on internal APIs (admin panels, management endpoints, metrics) allows
any external website visited by a privileged user to make requests to those endpoints.
Even if authentication is required, the attacker's page executes in the authenticated
user's browser context.


## FAQ

**Q: Is wildcard CORS always unsafe, or are there legitimate use cases?**

Wildcard CORS (origins="*") is safe only for fully public, unauthenticated APIs that
return no sensitive data -- for example, a public CDN or a read-only open data API with
no authentication. For any API that uses cookies, sessions, API keys, or returns
user-specific data, wildcard CORS is a vulnerability.


**Q: How does the .where() filter make this rule precise?**

Without .where(), the rule would flag every CORS() call regardless of the origins value,
producing false positives on applications with proper origin allow-lists. The
.where("origins", "*") constraint means only the literal string "*" triggers a finding.
CORS(app, origins=["https://example.com"]) is not flagged.


**Q: The MDN docs say browsers block credentials with wildcard CORS. Why is this still a finding?**

Browsers do block Access-Control-Allow-Credentials: true with Access-Control-Allow-Origin: *.
However, many APIs do not use cookies and instead use tokens in headers -- those requests
work fine with wildcard CORS. Additionally, developers who hit the credential-blocking
error often switch to reflecting the Origin header (effectively any-origin) as a workaround,
which is exploitable. Flagging wildcard early prevents the whole pattern.


**Q: How do I handle CORS for multiple environments (dev, staging, prod)?**

Maintain a per-environment allow-list and inject it through configuration:
  allowed_origins = os.getenv("CORS_ORIGINS", "https://app.example.com").split(",")
  CORS(app, origins=allowed_origins)
This way, no environment has wildcard origins hardcoded in source code.


**Q: How do I run this rule in CI/CD?**

Run: pathfinder ci --ruleset python/flask/PYTHON-FLASK-AUDIT-004 --project .
The rule outputs SARIF, JSON, or CSV and can post inline pull request comments on GitHub.


**Q: Does this rule also detect per-route CORS decorators with wildcard origins?**

No. This rule matches the CORS() constructor call pattern. Per-route decorators such as
@cross_origin(origins="*") are a separate pattern not covered here. A dedicated rule
for the @cross_origin decorator would complement this one.


**Q: What should I do if I truly need public CORS for a subset of routes?**

Use Flask-CORS's resource-specific configuration to restrict wildcard CORS to specific
URL patterns: CORS(app, resources={r"/public/*": {"origins": "*"}}). This limits the
wildcard to routes that genuinely require it rather than applying it globally.


## References

- [CWE-942: Permissive Cross-domain Policy with Untrusted Domains](https://cwe.mitre.org/data/definitions/942.html)
- [OWASP CORS Configuration Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html)
- [Flask-CORS Documentation](https://flask-cors.readthedocs.io/)
- [MDN Web Docs -- Cross-Origin Resource Sharing (CORS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)
- [OWASP A05:2021 Security Misconfiguration](https://owasp.org/Top10/A05_2021-Security_Misconfiguration/)
- [PortSwigger CORS Exploitation](https://portswigger.net/web-security/cors)

---

Source: https://codepathfinder.dev/registry/python/flask/PYTHON-FLASK-AUDIT-004
Code Pathfinder — Open source, type-aware SAST with cross-file dataflow analysis
