diff --git a/python/flask/security/audit/flask-cors-misconfiguration.py b/python/flask/security/audit/flask-cors-misconfiguration.py new file mode 100644 index 0000000000..1fe28cebc0 --- /dev/null +++ b/python/flask/security/audit/flask-cors-misconfiguration.py @@ -0,0 +1,39 @@ +from flask import Flask, jsonify +from flask_cors import CORS, cross_origin + +app = Flask(__name__) + +# Enable global CORS for all origins and allow credentials +# ruleid: flask-cors-misconfiguration +CORS(app, supports_credentials=True, origins="*") + +# Enable global CORS for all origins and allow credentials using "resources" dictionary +# ruleid: flask-cors-misconfiguration +cors = CORS(app, resources={ + r"/*": {"origins": "*", "supports_credentials": True}}) + + +@app.route('/data', methods=['GET']) +def get_data(): + # This route uses the global CORS configuration + return jsonify({"message": "CORS is enabled for all origins with credentials support (global config)!"}) + + +@app.route('/special-data', methods=['GET']) +# CORS applied only to this route +# ruleid: flask-cors-misconfiguration +@cross_origin(supports_credentials=True, origins="*") +def get_special_data(): + # This route uses the CORS decorator for route-specific CORS settings + return jsonify({"message": "CORS is enabled with credentials (route-specific config)!"}) + + +@app.route('/safe-route', methods=['GET']) +# ok: flask-cors-misconfiguration +@cross_origin(supports_credentials=True, origins=["https://foo.com", "https://bar.com"]) +def safe_route(): + return jsonify({"message": "CORS is enabled only for specific origins!"}) + + +if __name__ == '__main__': + app.run() diff --git a/python/flask/security/audit/flask-cors-misconfiguration.yaml b/python/flask/security/audit/flask-cors-misconfiguration.yaml new file mode 100644 index 0000000000..4dc989a23c --- /dev/null +++ b/python/flask/security/audit/flask-cors-misconfiguration.yaml @@ -0,0 +1,36 @@ +rules: + - id: flask-cors-misconfiguration + message: >- + Setting 'support_credentials=True' together with 'origin="*"' is a CORS + misconfiguration that can allow third party origins to read sensitive + data. Using this configuration, flask_cors will dynamically reflects the + Origin of each request in the Access-Control-Allow-Origin header, allowing + all origins and allowing cookies and credentials to be sent along with + request. It is recommended to specify allowed origins instead of using "*" + when setting 'support_credentials=True'. + languages: + - python + severity: WARNING + patterns: + - pattern-either: + - pattern: | + @cross_origin(..., origins="*", supports_credentials=True, ...) + - pattern: | + CORS(..., supports_credentials=True, origins="*", ...) + - pattern: | + CORS(..., resources={"...": {...,"origins": "*", + "supports_credentials": True,...}}) + metadata: + category: security + subcategory: + - audit + cwe: + - "CWE 942: Permissive Cross-domain Policy with Untrusted Domains" + confidence: HIGH + likelihood: LOW + impact: HIGH + technology: + - flask + references: + - https://pypi.org/project/Flask-Cors/ + - https://flask-cors.readthedocs.io/en/latest/index.html