diff --git a/tests/samples/codeql/python/vulnerable-code-snippets.json b/tests/samples/codeql/python/vulnerable-code-snippets.json new file mode 100644 index 000000000..8f8926021 --- /dev/null +++ b/tests/samples/codeql/python/vulnerable-code-snippets.json @@ -0,0 +1 @@ +{"runs":[{"artifacts":[{"location":{"index":0,"uri":"Unsafe Deserialization/pickle2.py"}},{"location":{"index":1,"uri":"Command Injection/tainted.py"}},{"location":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"}},{"location":{"index":3,"uri":"Path Traversal/py_ctf.py"}}],"automationDetails":{"id":"/language:python/"},"conversion":{"tool":{"driver":{"name":"GitHub Code Scanning"}}},"properties":{"codeqlConfigSummary":{}},"results":[{"codeFlows":[{"threadFlows":[{"locations":[{"location":{"message":{"text":"ControlFlowNode for Attribute()"},"physicalLocation":{"artifactLocation":{"index":0,"uri":"Unsafe Deserialization/pickle2.py"},"region":{"endColumn":44,"endLine":41,"startColumn":18,"startLine":40}}}},{"location":{"message":{"text":"ControlFlowNode for secret"},"physicalLocation":{"artifactLocation":{"index":0,"uri":"Unsafe Deserialization/pickle2.py"},"region":{"endColumn":15,"endLine":40,"startColumn":9,"startLine":40}}}},{"location":{"message":{"text":"ControlFlowNode for secret"},"physicalLocation":{"artifactLocation":{"index":0,"uri":"Unsafe Deserialization/pickle2.py"},"region":{"endColumn":23,"endLine":42,"startColumn":17,"startLine":42}}}}]}]}],"correlationGuid":"3db4f2bd-6682-4bba-a7d4-5f5d76b6e190","level":"error","locations":[{"physicalLocation":{"artifactLocation":{"index":0,"uri":"Unsafe Deserialization/pickle2.py"},"region":{"endColumn":23,"endLine":42,"startColumn":17,"startLine":42}}}],"message":{"text":"This expression stores [sensitive data (secret)](1) as clear text."},"partialFingerprints":{"primaryLocationLineHash":"7831bb4e0b589e7f:1"},"properties":{"github/alertNumber":2,"github/alertUrl":"https://api.github.com/repos/nahsra/Vulnerable-Code-Snippets/code-scanning/alerts/2"},"relatedLocations":[{"id":1,"message":{"text":"sensitive data (secret)"},"physicalLocation":{"artifactLocation":{"index":0,"uri":"Unsafe Deserialization/pickle2.py"},"region":{"endColumn":44,"endLine":41,"startColumn":18,"startLine":40}}}],"rule":{"id":"py/clear-text-storage-sensitive-data","toolComponent":{"index":0},"index":3},"ruleId":"py/clear-text-storage-sensitive-data"},{"correlationGuid":"119ca813-b276-44bb-809c-be478bc6c216","level":"error","locations":[{"physicalLocation":{"artifactLocation":{"index":1,"uri":"Command Injection/tainted.py"},"region":{"endColumn":21,"endLine":14,"startColumn":2,"startLine":14}}}],"message":{"text":"A Flask app appears to be run in debug mode. This may allow an attacker to run arbitrary code through the debugger."},"partialFingerprints":{"primaryLocationLineHash":"592eb5113a7053ce:1"},"properties":{"github/alertNumber":3,"github/alertUrl":"https://api.github.com/repos/nahsra/Vulnerable-Code-Snippets/code-scanning/alerts/3"},"rule":{"id":"py/flask-debug","toolComponent":{"index":0},"index":8},"ruleId":"py/flask-debug"},{"codeFlows":[{"threadFlows":[{"locations":[{"location":{"message":{"text":"ControlFlowNode for ImportMember"},"physicalLocation":{"artifactLocation":{"index":1,"uri":"Command Injection/tainted.py"},"region":{"endColumn":33,"endLine":2,"startColumn":26,"startLine":2}}}},{"location":{"message":{"text":"ControlFlowNode for request"},"physicalLocation":{"artifactLocation":{"index":1,"uri":"Command Injection/tainted.py"},"region":{"endColumn":33,"endLine":2,"startColumn":26,"startLine":2}}}},{"location":{"message":{"text":"ControlFlowNode for request"},"physicalLocation":{"artifactLocation":{"index":1,"uri":"Command Injection/tainted.py"},"region":{"endColumn":22,"endLine":9,"startColumn":15,"startLine":9}}}},{"location":{"message":{"text":"ControlFlowNode for Attribute"},"physicalLocation":{"artifactLocation":{"index":1,"uri":"Command Injection/tainted.py"},"region":{"endColumn":34,"endLine":9,"startColumn":15,"startLine":9}}}}]}]}],"correlationGuid":"956d88c6-70d3-4f7f-9fd1-00ce1c6059e0","level":"error","locations":[{"physicalLocation":{"artifactLocation":{"index":1,"uri":"Command Injection/tainted.py"},"region":{"endColumn":34,"endLine":9,"startColumn":15,"startLine":9}}}],"message":{"text":"This command line depends on a [user-provided value](1)."},"partialFingerprints":{"primaryLocationLineHash":"d2d7cb64d3a56d5c:1"},"properties":{"github/alertNumber":4,"github/alertUrl":"https://api.github.com/repos/nahsra/Vulnerable-Code-Snippets/code-scanning/alerts/4"},"relatedLocations":[{"id":1,"message":{"text":"user-provided value"},"physicalLocation":{"artifactLocation":{"index":0,"uri":"Command Injection/tainted.py"},"region":{"endColumn":33,"endLine":2,"startColumn":26,"startLine":2}}}],"rule":{"id":"py/command-line-injection","toolComponent":{"index":0},"index":5},"ruleId":"py/command-line-injection"},{"codeFlows":[{"threadFlows":[{"locations":[{"location":{"message":{"text":"ControlFlowNode for ImportMember"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":12,"endLine":6,"startColumn":5,"startLine":6}}}},{"location":{"message":{"text":"ControlFlowNode for request"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":12,"endLine":6,"startColumn":5,"startLine":6}}}},{"location":{"message":{"text":"ControlFlowNode for request"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":15,"endLine":26,"startColumn":8,"startLine":26}}}},{"location":{"message":{"text":"ControlFlowNode for Attribute"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":25,"endLine":29,"startColumn":13,"startLine":29}}}},{"location":{"message":{"text":"ControlFlowNode for Attribute()"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":38,"endLine":29,"startColumn":13,"startLine":29}}}},{"location":{"message":{"text":"ControlFlowNode for golem"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":10,"endLine":29,"startColumn":5,"startLine":29}}}},{"location":{"message":{"text":"ControlFlowNode for golem"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":33,"endLine":36,"startColumn":28,"startLine":36}}}},{"location":{"message":{"text":"[post] ControlFlowNode for session [Dictionary element at key golem]"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":16,"endLine":36,"startColumn":9,"startLine":36}}}},{"location":{"message":{"text":"ControlFlowNode for session [Dictionary element at key golem]"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":16,"endLine":50,"startColumn":9,"startLine":50}}}},{"location":{"message":{"text":"ControlFlowNode for Subscript"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":25,"endLine":50,"startColumn":9,"startLine":50}}}},{"location":{"message":{"text":"ControlFlowNode for template"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":17,"endLine":41,"startColumn":9,"startLine":41}}}},{"location":{"message":{"text":"ControlFlowNode for template"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":43,"endLine":56,"startColumn":35,"startLine":56}}}},{"location":{"message":{"text":"ControlFlowNode for render_template_string()"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":44,"endLine":56,"startColumn":12,"startLine":56}}}}]}]},{"threadFlows":[{"locations":[{"location":{"message":{"text":"ControlFlowNode for ImportMember"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":12,"endLine":6,"startColumn":5,"startLine":6}}}},{"location":{"message":{"text":"ControlFlowNode for request"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":12,"endLine":6,"startColumn":5,"startLine":6}}}},{"location":{"message":{"text":"ControlFlowNode for request"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":20,"endLine":29,"startColumn":13,"startLine":29}}}},{"location":{"message":{"text":"ControlFlowNode for Attribute"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":25,"endLine":29,"startColumn":13,"startLine":29}}}},{"location":{"message":{"text":"ControlFlowNode for Attribute()"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":38,"endLine":29,"startColumn":13,"startLine":29}}}},{"location":{"message":{"text":"ControlFlowNode for golem"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":10,"endLine":29,"startColumn":5,"startLine":29}}}},{"location":{"message":{"text":"ControlFlowNode for golem"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":14,"endLine":32,"startColumn":9,"startLine":32}}}},{"location":{"message":{"text":"ControlFlowNode for golem"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":33,"endLine":36,"startColumn":28,"startLine":36}}}},{"location":{"message":{"text":"[post] ControlFlowNode for session [Dictionary element at key golem]"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":16,"endLine":36,"startColumn":9,"startLine":36}}}},{"location":{"message":{"text":"ControlFlowNode for session [Dictionary element at key golem]"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":16,"endLine":50,"startColumn":9,"startLine":50}}}},{"location":{"message":{"text":"ControlFlowNode for Subscript"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":25,"endLine":50,"startColumn":9,"startLine":50}}}},{"location":{"message":{"text":"ControlFlowNode for template"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":17,"endLine":41,"startColumn":9,"startLine":41}}}},{"location":{"message":{"text":"ControlFlowNode for template"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":43,"endLine":56,"startColumn":35,"startLine":56}}}},{"location":{"message":{"text":"ControlFlowNode for render_template_string()"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":44,"endLine":56,"startColumn":12,"startLine":56}}}}]}]}],"correlationGuid":"5b4f3e50-2b30-4a70-ac57-fa37359f9443","level":"error","locations":[{"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":44,"endLine":56,"startColumn":12,"startLine":56}}}],"message":{"text":"Cross-site scripting vulnerability due to a [user-provided value](1)."},"partialFingerprints":{"primaryLocationLineHash":"f207ef544f2b3e05:1"},"properties":{"github/alertNumber":5,"github/alertUrl":"https://api.github.com/repos/nahsra/Vulnerable-Code-Snippets/code-scanning/alerts/5"},"relatedLocations":[{"id":1,"message":{"text":"user-provided value"},"physicalLocation":{"artifactLocation":{"index":0,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":12,"endLine":6,"startColumn":5,"startLine":6}}}],"rule":{"id":"py/reflective-xss","toolComponent":{"index":0},"index":25},"ruleId":"py/reflective-xss"},{"codeFlows":[{"threadFlows":[{"locations":[{"location":{"message":{"text":"ControlFlowNode for ImportMember"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":12,"endLine":6,"startColumn":5,"startLine":6}}}},{"location":{"message":{"text":"ControlFlowNode for request"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":12,"endLine":6,"startColumn":5,"startLine":6}}}},{"location":{"message":{"text":"ControlFlowNode for request"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":15,"endLine":26,"startColumn":8,"startLine":26}}}},{"location":{"message":{"text":"ControlFlowNode for Attribute"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":25,"endLine":29,"startColumn":13,"startLine":29}}}},{"location":{"message":{"text":"ControlFlowNode for Attribute()"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":38,"endLine":29,"startColumn":13,"startLine":29}}}},{"location":{"message":{"text":"ControlFlowNode for golem"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":10,"endLine":29,"startColumn":5,"startLine":29}}}},{"location":{"message":{"text":"ControlFlowNode for golem"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":33,"endLine":36,"startColumn":28,"startLine":36}}}},{"location":{"message":{"text":"[post] ControlFlowNode for session [Dictionary element at key golem]"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":16,"endLine":36,"startColumn":9,"startLine":36}}}},{"location":{"message":{"text":"ControlFlowNode for session [Dictionary element at key golem]"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":16,"endLine":50,"startColumn":9,"startLine":50}}}},{"location":{"message":{"text":"ControlFlowNode for Subscript"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":25,"endLine":50,"startColumn":9,"startLine":50}}}},{"location":{"message":{"text":"ControlFlowNode for template"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":17,"endLine":41,"startColumn":9,"startLine":41}}}},{"location":{"message":{"text":"ControlFlowNode for template"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":43,"endLine":56,"startColumn":35,"startLine":56}}}},{"location":{"message":{"text":"ControlFlowNode for render_template_string()"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":44,"endLine":56,"startColumn":12,"startLine":56}}}}]}]},{"threadFlows":[{"locations":[{"location":{"message":{"text":"ControlFlowNode for ImportMember"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":12,"endLine":6,"startColumn":5,"startLine":6}}}},{"location":{"message":{"text":"ControlFlowNode for request"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":12,"endLine":6,"startColumn":5,"startLine":6}}}},{"location":{"message":{"text":"ControlFlowNode for request"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":20,"endLine":29,"startColumn":13,"startLine":29}}}},{"location":{"message":{"text":"ControlFlowNode for Attribute"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":25,"endLine":29,"startColumn":13,"startLine":29}}}},{"location":{"message":{"text":"ControlFlowNode for Attribute()"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":38,"endLine":29,"startColumn":13,"startLine":29}}}},{"location":{"message":{"text":"ControlFlowNode for golem"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":10,"endLine":29,"startColumn":5,"startLine":29}}}},{"location":{"message":{"text":"ControlFlowNode for golem"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":14,"endLine":32,"startColumn":9,"startLine":32}}}},{"location":{"message":{"text":"ControlFlowNode for golem"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":33,"endLine":36,"startColumn":28,"startLine":36}}}},{"location":{"message":{"text":"[post] ControlFlowNode for session [Dictionary element at key golem]"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":16,"endLine":36,"startColumn":9,"startLine":36}}}},{"location":{"message":{"text":"ControlFlowNode for session [Dictionary element at key golem]"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":16,"endLine":50,"startColumn":9,"startLine":50}}}},{"location":{"message":{"text":"ControlFlowNode for Subscript"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":25,"endLine":50,"startColumn":9,"startLine":50}}}},{"location":{"message":{"text":"ControlFlowNode for template"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":17,"endLine":41,"startColumn":9,"startLine":41}}}},{"location":{"message":{"text":"ControlFlowNode for template"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":43,"endLine":56,"startColumn":35,"startLine":56}}}},{"location":{"message":{"text":"ControlFlowNode for render_template_string()"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":44,"endLine":56,"startColumn":12,"startLine":56}}}}]}]}],"correlationGuid":"4368aec2-0d5c-451e-a914-42cf18ca62fd","level":"error","locations":[{"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":44,"endLine":56,"startColumn":12,"startLine":56}}}],"message":{"text":"Cross-site scripting vulnerability due to a [user-provided value](1)."},"partialFingerprints":{"primaryLocationLineHash":"f207ef544f2b3e05:1"},"properties":{"github/alertNumber":6,"github/alertUrl":"https://api.github.com/repos/nahsra/Vulnerable-Code-Snippets/code-scanning/alerts/6"},"relatedLocations":[{"id":1,"message":{"text":"user-provided value"},"physicalLocation":{"artifactLocation":{"index":0,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":12,"endLine":6,"startColumn":5,"startLine":6}}}],"rule":{"id":"py/reflective-xss","toolComponent":{"index":0},"index":25},"ruleId":"py/reflective-xss"},{"codeFlows":[{"threadFlows":[{"locations":[{"location":{"message":{"text":"ControlFlowNode for ImportMember"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":12,"endLine":6,"startColumn":5,"startLine":6}}}},{"location":{"message":{"text":"ControlFlowNode for request"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":12,"endLine":6,"startColumn":5,"startLine":6}}}},{"location":{"message":{"text":"ControlFlowNode for request"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":25,"endLine":69,"startColumn":18,"startLine":69}}}},{"location":{"message":{"text":"ControlFlowNode for Attribute"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":28,"endLine":70,"startColumn":16,"startLine":70}}}},{"location":{"message":{"text":"ControlFlowNode for Attribute()"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":40,"endLine":70,"startColumn":16,"startLine":70}}}},{"location":{"message":{"text":"ControlFlowNode for page"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":13,"endLine":70,"startColumn":9,"startLine":70}}}},{"location":{"message":{"text":"ControlFlowNode for Attribute()"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":63,"endLine":78,"startColumn":25,"startLine":78}}}}]}]},{"threadFlows":[{"locations":[{"location":{"message":{"text":"ControlFlowNode for ImportMember"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":12,"endLine":6,"startColumn":5,"startLine":6}}}},{"location":{"message":{"text":"ControlFlowNode for request"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":12,"endLine":6,"startColumn":5,"startLine":6}}}},{"location":{"message":{"text":"ControlFlowNode for request"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":23,"endLine":70,"startColumn":16,"startLine":70}}}},{"location":{"message":{"text":"ControlFlowNode for Attribute"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":28,"endLine":70,"startColumn":16,"startLine":70}}}},{"location":{"message":{"text":"ControlFlowNode for Attribute()"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":40,"endLine":70,"startColumn":16,"startLine":70}}}},{"location":{"message":{"text":"ControlFlowNode for page"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":13,"endLine":70,"startColumn":9,"startLine":70}}}},{"location":{"message":{"text":"ControlFlowNode for Attribute()"},"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":63,"endLine":78,"startColumn":25,"startLine":78}}}}]}]}],"correlationGuid":"4586f203-2a1f-4567-b472-cf139c3171f7","level":"error","locations":[{"physicalLocation":{"artifactLocation":{"index":2,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":63,"endLine":78,"startColumn":25,"startLine":78}}}],"message":{"text":"This path depends on a [user-provided value](1)."},"partialFingerprints":{"primaryLocationLineHash":"49ce9f5ebea5b775:1"},"properties":{"github/alertNumber":7,"github/alertUrl":"https://api.github.com/repos/nahsra/Vulnerable-Code-Snippets/code-scanning/alerts/7"},"relatedLocations":[{"id":1,"message":{"text":"user-provided value"},"physicalLocation":{"artifactLocation":{"index":0,"uri":"Server Side Template Injection/asis_ssti_pt.py"},"region":{"endColumn":12,"endLine":6,"startColumn":5,"startLine":6}}}],"rule":{"id":"py/path-injection","toolComponent":{"index":0},"index":22},"ruleId":"py/path-injection"},{"codeFlows":[{"threadFlows":[{"locations":[{"location":{"message":{"text":"ControlFlowNode for ImportMember"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":12,"endLine":6,"startColumn":5,"startLine":6}}}},{"location":{"message":{"text":"ControlFlowNode for request"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":12,"endLine":6,"startColumn":5,"startLine":6}}}},{"location":{"message":{"text":"ControlFlowNode for request"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":25,"endLine":69,"startColumn":18,"startLine":69}}}},{"location":{"message":{"text":"ControlFlowNode for Attribute"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":28,"endLine":70,"startColumn":16,"startLine":70}}}},{"location":{"message":{"text":"ControlFlowNode for Attribute()"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":40,"endLine":70,"startColumn":16,"startLine":70}}}},{"location":{"message":{"text":"ControlFlowNode for page"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":13,"endLine":70,"startColumn":9,"startLine":70}}}},{"location":{"message":{"text":"ControlFlowNode for Attribute()"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":63,"endLine":78,"startColumn":25,"startLine":78}}}}]}]},{"threadFlows":[{"locations":[{"location":{"message":{"text":"ControlFlowNode for ImportMember"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":12,"endLine":6,"startColumn":5,"startLine":6}}}},{"location":{"message":{"text":"ControlFlowNode for request"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":12,"endLine":6,"startColumn":5,"startLine":6}}}},{"location":{"message":{"text":"ControlFlowNode for request"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":23,"endLine":70,"startColumn":16,"startLine":70}}}},{"location":{"message":{"text":"ControlFlowNode for Attribute"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":28,"endLine":70,"startColumn":16,"startLine":70}}}},{"location":{"message":{"text":"ControlFlowNode for Attribute()"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":40,"endLine":70,"startColumn":16,"startLine":70}}}},{"location":{"message":{"text":"ControlFlowNode for page"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":13,"endLine":70,"startColumn":9,"startLine":70}}}},{"location":{"message":{"text":"ControlFlowNode for Attribute()"},"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":63,"endLine":78,"startColumn":25,"startLine":78}}}}]}]}],"correlationGuid":"13ee947d-8cdc-465b-8edc-b62046310cdd","level":"error","locations":[{"physicalLocation":{"artifactLocation":{"index":3,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":63,"endLine":78,"startColumn":25,"startLine":78}}}],"message":{"text":"This path depends on a [user-provided value](1)."},"partialFingerprints":{"primaryLocationLineHash":"49ce9f5ebea5b775:1"},"properties":{"github/alertNumber":8,"github/alertUrl":"https://api.github.com/repos/nahsra/Vulnerable-Code-Snippets/code-scanning/alerts/8"},"relatedLocations":[{"id":1,"message":{"text":"user-provided value"},"physicalLocation":{"artifactLocation":{"index":0,"uri":"Path Traversal/py_ctf.py"},"region":{"endColumn":12,"endLine":6,"startColumn":5,"startLine":6}}}],"rule":{"id":"py/path-injection","toolComponent":{"index":0},"index":22},"ruleId":"py/path-injection"}],"tool":{"driver":{"name":"CodeQL","semanticVersion":"2.19.3"},"extensions":[{"name":"codeql/python-queries","rules":[{"defaultConfiguration":{"level":"warning"},"fullDescription":{"text":"Matching HTML tags using regular expressions is hard to do right, and can easily lead to security issues."},"help":{"markdown":"# Bad HTML filtering regexp\nIt is possible to match some single HTML tags using regular expressions (parsing general HTML using regular expressions is impossible). However, if the regular expression is not written well it might be possible to circumvent it, which can lead to cross-site scripting or other security issues.\n\nSome of these mistakes are caused by browsers having very forgiving HTML parsers, and will often render invalid HTML containing syntax errors. Regular expressions that attempt to match HTML should also recognize tags containing such syntax errors.\n\n\n## Recommendation\nUse a well-tested sanitization or parser library if at all possible. These libraries are much more likely to handle corner cases correctly than a custom implementation.\n\n\n## Example\nThe following example attempts to filters out all `\u003cscript\u003e` tags.\n\n\n```python\nimport re\n\ndef filterScriptTags(content): \n oldContent = \"\"\n while oldContent != content:\n oldContent = content\n content = re.sub(r'\u003cscript.*?\u003e.*?\u003c/script\u003e', '', content, flags= re.DOTALL | re.IGNORECASE)\n return content\n```\nThe above sanitizer does not filter out all `\u003cscript\u003e` tags. Browsers will not only accept `\u003c/script\u003e` as script end tags, but also tags such as `\u003c/script foo=\"bar\"\u003e` even though it is a parser error. This means that an attack string such as `\u003cscript\u003ealert(1)\u003c/script foo=\"bar\"\u003e` will not be filtered by the function, and `alert(1)` will be executed by a browser if the string is rendered as HTML.\n\nOther corner cases include that HTML comments can end with `--!\u003e`, and that HTML tag names can contain upper case characters.\n\n\n## References\n* Securitum: [The Curious Case of Copy \u0026amp; Paste](https://research.securitum.com/the-curious-case-of-copy-paste/).\n* stackoverflow.com: [You can't parse \\[X\\]HTML with regex](https://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags#answer-1732454).\n* HTML Standard: [Comment end bang state](https://html.spec.whatwg.org/multipage/parsing.html#comment-end-bang-state).\n* stackoverflow.com: [Why aren't browsers strict about HTML?](https://stackoverflow.com/questions/25559999/why-arent-browsers-strict-about-html).\n* Common Weakness Enumeration: [CWE-116](https://cwe.mitre.org/data/definitions/116.html).\n* Common Weakness Enumeration: [CWE-20](https://cwe.mitre.org/data/definitions/20.html).\n* Common Weakness Enumeration: [CWE-185](https://cwe.mitre.org/data/definitions/185.html).\n* Common Weakness Enumeration: [CWE-186](https://cwe.mitre.org/data/definitions/186.html).\n","text":"# Bad HTML filtering regexp\nIt is possible to match some single HTML tags using regular expressions (parsing general HTML using regular expressions is impossible). However, if the regular expression is not written well it might be possible to circumvent it, which can lead to cross-site scripting or other security issues.\n\nSome of these mistakes are caused by browsers having very forgiving HTML parsers, and will often render invalid HTML containing syntax errors. Regular expressions that attempt to match HTML should also recognize tags containing such syntax errors.\n\n\n## Recommendation\nUse a well-tested sanitization or parser library if at all possible. These libraries are much more likely to handle corner cases correctly than a custom implementation.\n\n\n## Example\nThe following example attempts to filters out all `\u003cscript\u003e` tags.\n\n\n```python\nimport re\n\ndef filterScriptTags(content): \n oldContent = \"\"\n while oldContent != content:\n oldContent = content\n content = re.sub(r'\u003cscript.*?\u003e.*?\u003c/script\u003e', '', content, flags= re.DOTALL | re.IGNORECASE)\n return content\n```\nThe above sanitizer does not filter out all `\u003cscript\u003e` tags. Browsers will not only accept `\u003c/script\u003e` as script end tags, but also tags such as `\u003c/script foo=\"bar\"\u003e` even though it is a parser error. This means that an attack string such as `\u003cscript\u003ealert(1)\u003c/script foo=\"bar\"\u003e` will not be filtered by the function, and `alert(1)` will be executed by a browser if the string is rendered as HTML.\n\nOther corner cases include that HTML comments can end with `--!\u003e`, and that HTML tag names can contain upper case characters.\n\n\n## References\n* Securitum: [The Curious Case of Copy \u0026amp; Paste](https://research.securitum.com/the-curious-case-of-copy-paste/).\n* stackoverflow.com: [You can't parse \\[X\\]HTML with regex](https://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags#answer-1732454).\n* HTML Standard: [Comment end bang state](https://html.spec.whatwg.org/multipage/parsing.html#comment-end-bang-state).\n* stackoverflow.com: [Why aren't browsers strict about HTML?](https://stackoverflow.com/questions/25559999/why-arent-browsers-strict-about-html).\n* Common Weakness Enumeration: [CWE-116](https://cwe.mitre.org/data/definitions/116.html).\n* Common Weakness Enumeration: [CWE-20](https://cwe.mitre.org/data/definitions/20.html).\n* Common Weakness Enumeration: [CWE-185](https://cwe.mitre.org/data/definitions/185.html).\n* Common Weakness Enumeration: [CWE-186](https://cwe.mitre.org/data/definitions/186.html).\n"},"id":"py/bad-tag-filter","name":"py/bad-tag-filter","properties":{"precision":"high","queryURI":"https://github.com/github/codeql/blob/39a67b6e2e6490a9bd010db50e148f647765e9f7/python/ql/src/Security/CWE-116/BadTagFilter.ql","security-severity":"7.8","tags":["correctness","external/cwe/cwe-020","external/cwe/cwe-116","external/cwe/cwe-185","external/cwe/cwe-186","security"]},"shortDescription":{"text":"Bad HTML filtering regexp"}},{"defaultConfiguration":{"level":"error"},"fullDescription":{"text":"Binding a socket to all interfaces opens it up to traffic from any IPv4 address and is therefore associated with security risks."},"help":{"markdown":"# Binding a socket to all network interfaces\nSockets can be used to communicate with other machines on a network. You can use the (IP address, port) pair to define the access restrictions for the socket you create. When using the built-in Python `socket` module (for instance, when building a message sender service or an FTP server data transmitter), one has to bind the port to some interface. When you bind the port to all interfaces using `0.0.0.0` as the IP address, you essentially allow it to accept connections from any IPv4 address provided that it can get to the socket via routing. Binding to all interfaces is therefore associated with security risks.\n\n\n## Recommendation\nBind your service incoming traffic only to a dedicated interface. If you need to bind more than one interface using the built-in `socket` module, create multiple sockets (instead of binding to one socket to all interfaces).\n\n\n## Example\nIn this example, two sockets are insecure because they are bound to all interfaces; one through the `0.0.0.0` notation and another one through an empty string `''`.\n\n\n```python\nimport socket\n\n# binds to all interfaces, insecure\ns = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\ns.bind(('0.0.0.0', 31137))\n\n# binds to all interfaces, insecure\ns = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\ns.bind(('', 4040))\n\n# binds only to a dedicated interface, secure\ns = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\ns.bind(('84.68.10.12', 8080))\n\n```\n\n## References\n* Python reference: [ Socket families](https://docs.python.org/3/library/socket.html#socket-families).\n* Python reference: [ Socket Programming HOWTO](https://docs.python.org/3.7/howto/sockets.html).\n* Common Vulnerabilities and Exposures: [ CVE-2018-1281 Detail](https://nvd.nist.gov/vuln/detail/CVE-2018-1281).\n* Common Weakness Enumeration: [CWE-200](https://cwe.mitre.org/data/definitions/200.html).\n","text":"# Binding a socket to all network interfaces\nSockets can be used to communicate with other machines on a network. You can use the (IP address, port) pair to define the access restrictions for the socket you create. When using the built-in Python `socket` module (for instance, when building a message sender service or an FTP server data transmitter), one has to bind the port to some interface. When you bind the port to all interfaces using `0.0.0.0` as the IP address, you essentially allow it to accept connections from any IPv4 address provided that it can get to the socket via routing. Binding to all interfaces is therefore associated with security risks.\n\n\n## Recommendation\nBind your service incoming traffic only to a dedicated interface. If you need to bind more than one interface using the built-in `socket` module, create multiple sockets (instead of binding to one socket to all interfaces).\n\n\n## Example\nIn this example, two sockets are insecure because they are bound to all interfaces; one through the `0.0.0.0` notation and another one through an empty string `''`.\n\n\n```python\nimport socket\n\n# binds to all interfaces, insecure\ns = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\ns.bind(('0.0.0.0', 31137))\n\n# binds to all interfaces, insecure\ns = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\ns.bind(('', 4040))\n\n# binds only to a dedicated interface, secure\ns = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\ns.bind(('84.68.10.12', 8080))\n\n```\n\n## References\n* Python reference: [ Socket families](https://docs.python.org/3/library/socket.html#socket-families).\n* Python reference: [ Socket Programming HOWTO](https://docs.python.org/3.7/howto/sockets.html).\n* Common Vulnerabilities and Exposures: [ CVE-2018-1281 Detail](https://nvd.nist.gov/vuln/detail/CVE-2018-1281).\n* Common Weakness Enumeration: [CWE-200](https://cwe.mitre.org/data/definitions/200.html).\n"},"id":"py/bind-socket-all-network-interfaces","name":"py/bind-socket-all-network-interfaces","properties":{"precision":"high","queryURI":"https://github.com/github/codeql/blob/39a67b6e2e6490a9bd010db50e148f647765e9f7/python/ql/src/Security/CVE-2018-1281/BindToAllInterfaces.ql","security-severity":"6.5","tags":["external/cwe/cwe-200","security"]},"shortDescription":{"text":"Binding a socket to all network interfaces"}},{"defaultConfiguration":{"level":"error"},"fullDescription":{"text":"Logging sensitive information without encryption or hashing can expose it to an attacker."},"help":{"markdown":"# Clear-text logging of sensitive information\nIf sensitive data is written to a log entry it could be exposed to an attacker who gains access to the logs.\n\nPotential attackers can obtain sensitive user data when the log output is displayed. Additionally that data may expose system information such as full path names, system information, and sometimes usernames and passwords.\n\n\n## Recommendation\nSensitive data should not be logged.\n\n\n## Example\nIn the example the entire process environment is logged using \\`print\\`. Regular users of the production deployed application should not have access to this much information about the environment configuration.\n\n\n```python\n# BAD: Logging cleartext sensitive data\nimport os\nprint(f\"[INFO] Environment: {os.environ}\")\n```\nIn the second example the data that is logged is not sensitive.\n\n\n```python\nnot_sensitive_data = {'a': 1, 'b': 2}\n# GOOD: it is fine to log data that is not sensitive\nprint(f\"[INFO] Some object contains: {not_sensitive_data}\")\n```\n\n## References\n* OWASP: [Insertion of Sensitive Information into Log File](https://owasp.org/Top10/A09_2021-Security_Logging_and_Monitoring_Failures/).\n* Common Weakness Enumeration: [CWE-312](https://cwe.mitre.org/data/definitions/312.html).\n* Common Weakness Enumeration: [CWE-359](https://cwe.mitre.org/data/definitions/359.html).\n* Common Weakness Enumeration: [CWE-532](https://cwe.mitre.org/data/definitions/532.html).\n","text":"# Clear-text logging of sensitive information\nIf sensitive data is written to a log entry it could be exposed to an attacker who gains access to the logs.\n\nPotential attackers can obtain sensitive user data when the log output is displayed. Additionally that data may expose system information such as full path names, system information, and sometimes usernames and passwords.\n\n\n## Recommendation\nSensitive data should not be logged.\n\n\n## Example\nIn the example the entire process environment is logged using \\`print\\`. Regular users of the production deployed application should not have access to this much information about the environment configuration.\n\n\n```python\n# BAD: Logging cleartext sensitive data\nimport os\nprint(f\"[INFO] Environment: {os.environ}\")\n```\nIn the second example the data that is logged is not sensitive.\n\n\n```python\nnot_sensitive_data = {'a': 1, 'b': 2}\n# GOOD: it is fine to log data that is not sensitive\nprint(f\"[INFO] Some object contains: {not_sensitive_data}\")\n```\n\n## References\n* OWASP: [Insertion of Sensitive Information into Log File](https://owasp.org/Top10/A09_2021-Security_Logging_and_Monitoring_Failures/).\n* Common Weakness Enumeration: [CWE-312](https://cwe.mitre.org/data/definitions/312.html).\n* Common Weakness Enumeration: [CWE-359](https://cwe.mitre.org/data/definitions/359.html).\n* Common Weakness Enumeration: [CWE-532](https://cwe.mitre.org/data/definitions/532.html).\n"},"id":"py/clear-text-logging-sensitive-data","name":"py/clear-text-logging-sensitive-data","properties":{"precision":"high","queryURI":"https://github.com/github/codeql/blob/39a67b6e2e6490a9bd010db50e148f647765e9f7/python/ql/src/Security/CWE-312/CleartextLogging.ql","security-severity":"7.5","tags":["external/cwe/cwe-312","external/cwe/cwe-359","external/cwe/cwe-532","security"]},"shortDescription":{"text":"Clear-text logging of sensitive information"}},{"defaultConfiguration":{"level":"error"},"fullDescription":{"text":"Sensitive information stored without encryption or hashing can expose it to an attacker."},"help":{"markdown":"# Clear-text storage of sensitive information\nSensitive information that is stored unencrypted is accessible to an attacker who gains access to the storage. This is particularly important for cookies, which are stored on the machine of the end-user.\n\n\n## Recommendation\nEnsure that sensitive information is always encrypted before being stored. If possible, avoid placing sensitive information in cookies altogether. Instead, prefer storing, in the cookie, a key that can be used to look up the sensitive information.\n\nIn general, decrypt sensitive information only at the point where it is necessary for it to be used in cleartext.\n\nBe aware that external processes often store the `standard out` and `standard error` streams of the application, causing logged sensitive information to be stored as well.\n\n\n## Example\nThe following example code stores user credentials (in this case, their password) in a cookie in plain text:\n\n\n```python\nfrom flask import Flask, make_response, request\n\napp = Flask(\"Leak password\")\n\n@app.route('/')\ndef index():\n password = request.args.get(\"password\")\n resp = make_response(render_template(...))\n resp.set_cookie(\"password\", password)\n return resp\n\n```\nInstead, the credentials should be encrypted, for instance by using the `cryptography` module, or not stored at all.\n\n\n## References\n* M. Dowd, J. McDonald and J. Schuhm, *The Art of Software Security Assessment*, 1st Edition, Chapter 2 - 'Common Vulnerabilities of Encryption', p. 43. Addison Wesley, 2006.\n* M. Howard and D. LeBlanc, *Writing Secure Code*, 2nd Edition, Chapter 9 - 'Protecting Secret Data', p. 299. Microsoft, 2002.\n* Common Weakness Enumeration: [CWE-312](https://cwe.mitre.org/data/definitions/312.html).\n* Common Weakness Enumeration: [CWE-315](https://cwe.mitre.org/data/definitions/315.html).\n* Common Weakness Enumeration: [CWE-359](https://cwe.mitre.org/data/definitions/359.html).\n","text":"# Clear-text storage of sensitive information\nSensitive information that is stored unencrypted is accessible to an attacker who gains access to the storage. This is particularly important for cookies, which are stored on the machine of the end-user.\n\n\n## Recommendation\nEnsure that sensitive information is always encrypted before being stored. If possible, avoid placing sensitive information in cookies altogether. Instead, prefer storing, in the cookie, a key that can be used to look up the sensitive information.\n\nIn general, decrypt sensitive information only at the point where it is necessary for it to be used in cleartext.\n\nBe aware that external processes often store the `standard out` and `standard error` streams of the application, causing logged sensitive information to be stored as well.\n\n\n## Example\nThe following example code stores user credentials (in this case, their password) in a cookie in plain text:\n\n\n```python\nfrom flask import Flask, make_response, request\n\napp = Flask(\"Leak password\")\n\n@app.route('/')\ndef index():\n password = request.args.get(\"password\")\n resp = make_response(render_template(...))\n resp.set_cookie(\"password\", password)\n return resp\n\n```\nInstead, the credentials should be encrypted, for instance by using the `cryptography` module, or not stored at all.\n\n\n## References\n* M. Dowd, J. McDonald and J. Schuhm, *The Art of Software Security Assessment*, 1st Edition, Chapter 2 - 'Common Vulnerabilities of Encryption', p. 43. Addison Wesley, 2006.\n* M. Howard and D. LeBlanc, *Writing Secure Code*, 2nd Edition, Chapter 9 - 'Protecting Secret Data', p. 299. Microsoft, 2002.\n* Common Weakness Enumeration: [CWE-312](https://cwe.mitre.org/data/definitions/312.html).\n* Common Weakness Enumeration: [CWE-315](https://cwe.mitre.org/data/definitions/315.html).\n* Common Weakness Enumeration: [CWE-359](https://cwe.mitre.org/data/definitions/359.html).\n"},"id":"py/clear-text-storage-sensitive-data","name":"py/clear-text-storage-sensitive-data","properties":{"precision":"high","queryURI":"https://github.com/github/codeql/blob/39a67b6e2e6490a9bd010db50e148f647765e9f7/python/ql/src/Security/CWE-312/CleartextStorage.ql","security-severity":"7.5","tags":["external/cwe/cwe-312","external/cwe/cwe-315","external/cwe/cwe-359","security"]},"shortDescription":{"text":"Clear-text storage of sensitive information"}},{"defaultConfiguration":{"level":"error"},"fullDescription":{"text":"Interpreting unsanitized user input as code allows a malicious user to perform arbitrary code execution."},"help":{"markdown":"# Code injection\nDirectly evaluating user input (for example, an HTTP request parameter) as code without properly sanitizing the input first allows an attacker arbitrary code execution. This can occur when user input is passed to code that interprets it as an expression to be evaluated, such as `eval` or `exec`.\n\n\n## Recommendation\nAvoid including user input in any expression that may be dynamically evaluated. If user input must be included, use context-specific escaping before including it. It is important that the correct escaping is used for the type of evaluation that will occur.\n\n\n## Example\nThe following example shows two functions setting a name from a request. The first function uses `exec` to execute the `setname` function. This is dangerous as it can allow a malicious user to execute arbitrary code on the server. For example, the user could supply the value `\"' + subprocess.call('rm -rf') + '\"` to destroy the server's file system. The second function calls the `setname` function directly and is thus safe.\n\n\n```python\n\nurlpatterns = [\n # Route to code_execution\n url(r'^code-ex1$', code_execution_bad, name='code-execution-bad'),\n url(r'^code-ex2$', code_execution_good, name='code-execution-good')\n]\n\ndef code_execution(request):\n if request.method == 'POST':\n first_name = base64.decodestring(request.POST.get('first_name', ''))\n #BAD -- Allow user to define code to be run.\n exec(\"setname('%s')\" % first_name)\n\ndef code_execution(request):\n if request.method == 'POST':\n first_name = base64.decodestring(request.POST.get('first_name', ''))\n #GOOD --Call code directly\n setname(first_name)\n\n```\n\n## References\n* OWASP: [Code Injection](https://www.owasp.org/index.php/Code_Injection).\n* Wikipedia: [Code Injection](https://en.wikipedia.org/wiki/Code_injection).\n* Common Weakness Enumeration: [CWE-94](https://cwe.mitre.org/data/definitions/94.html).\n* Common Weakness Enumeration: [CWE-95](https://cwe.mitre.org/data/definitions/95.html).\n* Common Weakness Enumeration: [CWE-116](https://cwe.mitre.org/data/definitions/116.html).\n","text":"# Code injection\nDirectly evaluating user input (for example, an HTTP request parameter) as code without properly sanitizing the input first allows an attacker arbitrary code execution. This can occur when user input is passed to code that interprets it as an expression to be evaluated, such as `eval` or `exec`.\n\n\n## Recommendation\nAvoid including user input in any expression that may be dynamically evaluated. If user input must be included, use context-specific escaping before including it. It is important that the correct escaping is used for the type of evaluation that will occur.\n\n\n## Example\nThe following example shows two functions setting a name from a request. The first function uses `exec` to execute the `setname` function. This is dangerous as it can allow a malicious user to execute arbitrary code on the server. For example, the user could supply the value `\"' + subprocess.call('rm -rf') + '\"` to destroy the server's file system. The second function calls the `setname` function directly and is thus safe.\n\n\n```python\n\nurlpatterns = [\n # Route to code_execution\n url(r'^code-ex1$', code_execution_bad, name='code-execution-bad'),\n url(r'^code-ex2$', code_execution_good, name='code-execution-good')\n]\n\ndef code_execution(request):\n if request.method == 'POST':\n first_name = base64.decodestring(request.POST.get('first_name', ''))\n #BAD -- Allow user to define code to be run.\n exec(\"setname('%s')\" % first_name)\n\ndef code_execution(request):\n if request.method == 'POST':\n first_name = base64.decodestring(request.POST.get('first_name', ''))\n #GOOD --Call code directly\n setname(first_name)\n\n```\n\n## References\n* OWASP: [Code Injection](https://www.owasp.org/index.php/Code_Injection).\n* Wikipedia: [Code Injection](https://en.wikipedia.org/wiki/Code_injection).\n* Common Weakness Enumeration: [CWE-94](https://cwe.mitre.org/data/definitions/94.html).\n* Common Weakness Enumeration: [CWE-95](https://cwe.mitre.org/data/definitions/95.html).\n* Common Weakness Enumeration: [CWE-116](https://cwe.mitre.org/data/definitions/116.html).\n"},"id":"py/code-injection","name":"py/code-injection","properties":{"precision":"high","queryURI":"https://github.com/github/codeql/blob/39a67b6e2e6490a9bd010db50e148f647765e9f7/python/ql/src/Security/CWE-094/CodeInjection.ql","security-severity":"9.3","tags":["external/cwe/cwe-094","external/cwe/cwe-095","external/cwe/cwe-116","security"]},"shortDescription":{"text":"Code injection"}},{"defaultConfiguration":{"level":"error"},"fullDescription":{"text":"Using externally controlled strings in a command line may allow a malicious user to change the meaning of the command."},"help":{"markdown":"# Uncontrolled command line\nCode that passes user input directly to `exec`, `eval`, or some other library routine that executes a command, allows the user to execute malicious code.\n\n\n## Recommendation\nIf possible, use hard-coded string literals to specify the command to run or the library to load. Instead of passing the user input directly to the process or library function, examine the user input and then choose among hard-coded string literals.\n\nIf the applicable libraries or commands cannot be determined at compile time, then add code to verify that the user input string is safe before using it.\n\n\n## Example\nThe following example shows two functions. The first is unsafe as it takes a shell script that can be changed by a user, and passes it straight to `subprocess.call()` without examining it first. The second is safe as it selects the command from a predefined allowlist.\n\n\n```python\n\nurlpatterns = [\n # Route to command_execution\n url(r'^command-ex1$', command_execution_unsafe, name='command-execution-unsafe'),\n url(r'^command-ex2$', command_execution_safe, name='command-execution-safe')\n]\n\nCOMMANDS = {\n \"list\" :\"ls\",\n \"stat\" : \"stat\"\n}\n\ndef command_execution_unsafe(request):\n if request.method == 'POST':\n action = request.POST.get('action', '')\n #BAD -- No sanitizing of input\n subprocess.call([\"application\", action])\n\ndef command_execution_safe(request):\n if request.method == 'POST':\n action = request.POST.get('action', '')\n #GOOD -- Use an allowlist\n subprocess.call([\"application\", COMMANDS[action]])\n\n```\n\n## References\n* OWASP: [Command Injection](https://www.owasp.org/index.php/Command_Injection).\n* Common Weakness Enumeration: [CWE-78](https://cwe.mitre.org/data/definitions/78.html).\n* Common Weakness Enumeration: [CWE-88](https://cwe.mitre.org/data/definitions/88.html).\n","text":"# Uncontrolled command line\nCode that passes user input directly to `exec`, `eval`, or some other library routine that executes a command, allows the user to execute malicious code.\n\n\n## Recommendation\nIf possible, use hard-coded string literals to specify the command to run or the library to load. Instead of passing the user input directly to the process or library function, examine the user input and then choose among hard-coded string literals.\n\nIf the applicable libraries or commands cannot be determined at compile time, then add code to verify that the user input string is safe before using it.\n\n\n## Example\nThe following example shows two functions. The first is unsafe as it takes a shell script that can be changed by a user, and passes it straight to `subprocess.call()` without examining it first. The second is safe as it selects the command from a predefined allowlist.\n\n\n```python\n\nurlpatterns = [\n # Route to command_execution\n url(r'^command-ex1$', command_execution_unsafe, name='command-execution-unsafe'),\n url(r'^command-ex2$', command_execution_safe, name='command-execution-safe')\n]\n\nCOMMANDS = {\n \"list\" :\"ls\",\n \"stat\" : \"stat\"\n}\n\ndef command_execution_unsafe(request):\n if request.method == 'POST':\n action = request.POST.get('action', '')\n #BAD -- No sanitizing of input\n subprocess.call([\"application\", action])\n\ndef command_execution_safe(request):\n if request.method == 'POST':\n action = request.POST.get('action', '')\n #GOOD -- Use an allowlist\n subprocess.call([\"application\", COMMANDS[action]])\n\n```\n\n## References\n* OWASP: [Command Injection](https://www.owasp.org/index.php/Command_Injection).\n* Common Weakness Enumeration: [CWE-78](https://cwe.mitre.org/data/definitions/78.html).\n* Common Weakness Enumeration: [CWE-88](https://cwe.mitre.org/data/definitions/88.html).\n"},"id":"py/command-line-injection","name":"py/command-line-injection","properties":{"precision":"high","queryURI":"https://github.com/github/codeql/blob/39a67b6e2e6490a9bd010db50e148f647765e9f7/python/ql/src/Security/CWE-078/CommandInjection.ql","security-severity":"9.8","tags":["correctness","external/cwe/cwe-078","external/cwe/cwe-088","security"]},"shortDescription":{"text":"Uncontrolled command line"}},{"defaultConfiguration":{"level":"warning"},"fullDescription":{"text":"Constructing cookies from user input may allow an attacker to perform a Cookie Poisoning attack."},"help":{"markdown":"# Construction of a cookie using user-supplied input\nConstructing cookies from user input can allow an attacker to control a user's cookie. This may lead to a session fixation attack. Additionally, client code may not expect a cookie to contain attacker-controlled data, and fail to sanitize it for common vulnerabilities such as Cross Site Scripting (XSS). An attacker manipulating the raw cookie header may additionally be able to set cookie attributes such as `HttpOnly` to insecure values.\n\n\n## Recommendation\nDo not use raw user input to construct cookies.\n\n\n## Example\nIn the following cases, a cookie is constructed for a Flask response using user input. The first uses `set_cookie`, and the second sets a cookie's raw value through the `set-cookie` header.\n\n\n```python\nfrom flask import request, make_response\n\n\n@app.route(\"/1\")\ndef set_cookie():\n resp = make_response()\n resp.set_cookie(request.args[\"name\"], # BAD: User input is used to set the cookie's name and value\n value=request.args[\"name\"])\n return resp\n\n\n@app.route(\"/2\")\ndef set_cookie_header():\n resp = make_response()\n resp.headers['Set-Cookie'] = f\"{request.args['name']}={request.args['name']};\" # BAD: User input is used to set the raw cookie header.\n return resp\n\n```\n\n## References\n* Wikipedia - [Session Fixation](https://en.wikipedia.org/wiki/Session_fixation).\n* Common Weakness Enumeration: [CWE-20](https://cwe.mitre.org/data/definitions/20.html).\n","text":"# Construction of a cookie using user-supplied input\nConstructing cookies from user input can allow an attacker to control a user's cookie. This may lead to a session fixation attack. Additionally, client code may not expect a cookie to contain attacker-controlled data, and fail to sanitize it for common vulnerabilities such as Cross Site Scripting (XSS). An attacker manipulating the raw cookie header may additionally be able to set cookie attributes such as `HttpOnly` to insecure values.\n\n\n## Recommendation\nDo not use raw user input to construct cookies.\n\n\n## Example\nIn the following cases, a cookie is constructed for a Flask response using user input. The first uses `set_cookie`, and the second sets a cookie's raw value through the `set-cookie` header.\n\n\n```python\nfrom flask import request, make_response\n\n\n@app.route(\"/1\")\ndef set_cookie():\n resp = make_response()\n resp.set_cookie(request.args[\"name\"], # BAD: User input is used to set the cookie's name and value\n value=request.args[\"name\"])\n return resp\n\n\n@app.route(\"/2\")\ndef set_cookie_header():\n resp = make_response()\n resp.headers['Set-Cookie'] = f\"{request.args['name']}={request.args['name']};\" # BAD: User input is used to set the raw cookie header.\n return resp\n\n```\n\n## References\n* Wikipedia - [Session Fixation](https://en.wikipedia.org/wiki/Session_fixation).\n* Common Weakness Enumeration: [CWE-20](https://cwe.mitre.org/data/definitions/20.html).\n"},"id":"py/cookie-injection","name":"py/cookie-injection","properties":{"precision":"high","queryURI":"https://github.com/github/codeql/blob/39a67b6e2e6490a9bd010db50e148f647765e9f7/python/ql/src/Security/CWE-020/CookieInjection.ql","security-severity":"5","tags":["external/cwe/cwe-20","security"]},"shortDescription":{"text":"Construction of a cookie using user-supplied input"}},{"defaultConfiguration":{"level":"warning"},"fullDescription":{"text":"Disabling or weakening CSRF protection may make the application vulnerable to a Cross-Site Request Forgery (CSRF) attack."},"help":{"markdown":"# CSRF protection weakened or disabled\nCross-site request forgery (CSRF) is a type of vulnerability in which an attacker is able to force a user to carry out an action that the user did not intend.\n\nThe attacker tricks an authenticated user into submitting a request to the web application. Typically this request will result in a state change on the server, such as changing the user's password. The request can be initiated when the user visits a site controlled by the attacker. If the web application relies only on cookies for authentication, or on other credentials that are automatically included in the request, then this request will appear as legitimate to the server.\n\nA common countermeasure for CSRF is to generate a unique token to be included in the HTML sent from the server to a user. This token can be used as a hidden field to be sent back with requests to the server, where the server can then check that the token is valid and associated with the relevant user session.\n\n\n## Recommendation\nIn many web frameworks, CSRF protection is enabled by default. In these cases, using the default configuration is sufficient to guard against most CSRF attacks.\n\n\n## Example\nThe following example shows a case where CSRF protection is disabled by overriding the default middleware stack and not including the one protecting against CSRF.\n\n\n```python\nMIDDLEWARE = [\n 'django.middleware.security.SecurityMiddleware',\n 'django.contrib.sessions.middleware.SessionMiddleware',\n 'django.middleware.common.CommonMiddleware',\n # 'django.middleware.csrf.CsrfViewMiddleware',\n 'django.contrib.auth.middleware.AuthenticationMiddleware',\n 'django.contrib.messages.middleware.MessageMiddleware',\n 'django.middleware.clickjacking.XFrameOptionsMiddleware',\n]\n\n```\nThe protecting middleware was probably commented out during a testing phase, when server-side token generation was not set up. Simply commenting it back in will enable CSRF protection.\n\n\n## References\n* Wikipedia: [Cross-site request forgery](https://en.wikipedia.org/wiki/Cross-site_request_forgery)\n* OWASP: [Cross-site request forgery](https://owasp.org/www-community/attacks/csrf)\n* Common Weakness Enumeration: [CWE-352](https://cwe.mitre.org/data/definitions/352.html).\n","text":"# CSRF protection weakened or disabled\nCross-site request forgery (CSRF) is a type of vulnerability in which an attacker is able to force a user to carry out an action that the user did not intend.\n\nThe attacker tricks an authenticated user into submitting a request to the web application. Typically this request will result in a state change on the server, such as changing the user's password. The request can be initiated when the user visits a site controlled by the attacker. If the web application relies only on cookies for authentication, or on other credentials that are automatically included in the request, then this request will appear as legitimate to the server.\n\nA common countermeasure for CSRF is to generate a unique token to be included in the HTML sent from the server to a user. This token can be used as a hidden field to be sent back with requests to the server, where the server can then check that the token is valid and associated with the relevant user session.\n\n\n## Recommendation\nIn many web frameworks, CSRF protection is enabled by default. In these cases, using the default configuration is sufficient to guard against most CSRF attacks.\n\n\n## Example\nThe following example shows a case where CSRF protection is disabled by overriding the default middleware stack and not including the one protecting against CSRF.\n\n\n```python\nMIDDLEWARE = [\n 'django.middleware.security.SecurityMiddleware',\n 'django.contrib.sessions.middleware.SessionMiddleware',\n 'django.middleware.common.CommonMiddleware',\n # 'django.middleware.csrf.CsrfViewMiddleware',\n 'django.contrib.auth.middleware.AuthenticationMiddleware',\n 'django.contrib.messages.middleware.MessageMiddleware',\n 'django.middleware.clickjacking.XFrameOptionsMiddleware',\n]\n\n```\nThe protecting middleware was probably commented out during a testing phase, when server-side token generation was not set up. Simply commenting it back in will enable CSRF protection.\n\n\n## References\n* Wikipedia: [Cross-site request forgery](https://en.wikipedia.org/wiki/Cross-site_request_forgery)\n* OWASP: [Cross-site request forgery](https://owasp.org/www-community/attacks/csrf)\n* Common Weakness Enumeration: [CWE-352](https://cwe.mitre.org/data/definitions/352.html).\n"},"id":"py/csrf-protection-disabled","name":"py/csrf-protection-disabled","properties":{"precision":"high","queryURI":"https://github.com/github/codeql/blob/39a67b6e2e6490a9bd010db50e148f647765e9f7/python/ql/src/Security/CWE-352/CSRFProtectionDisabled.ql","security-severity":"8.8","tags":["external/cwe/cwe-352","security"]},"shortDescription":{"text":"CSRF protection weakened or disabled"}},{"defaultConfiguration":{"level":"error"},"fullDescription":{"text":"Running a Flask app in debug mode may allow an attacker to run arbitrary code through the Werkzeug debugger."},"help":{"markdown":"# Flask app is run in debug mode\nRunning a Flask application with debug mode enabled may allow an attacker to gain access through the Werkzeug debugger.\n\n\n## Recommendation\nEnsure that Flask applications that are run in a production environment have debugging disabled.\n\n\n## Example\nRunning the following code starts a Flask webserver that has debugging enabled. By visiting `/crash`, it is possible to gain access to the debugger, and run arbitrary code through the interactive debugger.\n\n\n```python\nfrom flask import Flask\n\napp = Flask(__name__)\n\n@app.route('/crash')\ndef main():\n raise Exception()\n\napp.run(debug=True)\n\n```\n\n## References\n* Flask Quickstart Documentation: [Debug Mode](http://flask.pocoo.org/docs/1.0/quickstart/#debug-mode).\n* Werkzeug Documentation: [Debugging Applications](http://werkzeug.pocoo.org/docs/0.14/debug/).\n* Common Weakness Enumeration: [CWE-215](https://cwe.mitre.org/data/definitions/215.html).\n* Common Weakness Enumeration: [CWE-489](https://cwe.mitre.org/data/definitions/489.html).\n","text":"# Flask app is run in debug mode\nRunning a Flask application with debug mode enabled may allow an attacker to gain access through the Werkzeug debugger.\n\n\n## Recommendation\nEnsure that Flask applications that are run in a production environment have debugging disabled.\n\n\n## Example\nRunning the following code starts a Flask webserver that has debugging enabled. By visiting `/crash`, it is possible to gain access to the debugger, and run arbitrary code through the interactive debugger.\n\n\n```python\nfrom flask import Flask\n\napp = Flask(__name__)\n\n@app.route('/crash')\ndef main():\n raise Exception()\n\napp.run(debug=True)\n\n```\n\n## References\n* Flask Quickstart Documentation: [Debug Mode](http://flask.pocoo.org/docs/1.0/quickstart/#debug-mode).\n* Werkzeug Documentation: [Debugging Applications](http://werkzeug.pocoo.org/docs/0.14/debug/).\n* Common Weakness Enumeration: [CWE-215](https://cwe.mitre.org/data/definitions/215.html).\n* Common Weakness Enumeration: [CWE-489](https://cwe.mitre.org/data/definitions/489.html).\n"},"id":"py/flask-debug","name":"py/flask-debug","properties":{"precision":"high","queryURI":"https://github.com/github/codeql/blob/39a67b6e2e6490a9bd010db50e148f647765e9f7/python/ql/src/Security/CWE-215/FlaskDebug.ql","security-severity":"7.5","tags":["external/cwe/cwe-215","external/cwe/cwe-489","security"]},"shortDescription":{"text":"Flask app is run in debug mode"}},{"defaultConfiguration":{"level":"error"},"fullDescription":{"text":"Making a network request to a URL that is fully user-controlled allows for request forgery attacks."},"help":{"markdown":"# Full server-side request forgery\nDirectly incorporating user input into an HTTP request without validating the input can facilitate server-side request forgery (SSRF) attacks. In these attacks, the request may be changed, directed at a different server, or via a different protocol. This can allow the attacker to obtain sensitive information or perform actions with escalated privilege.\n\nWe make a distinctions between how much of the URL an attacker can control:\n\n* **Full SSRF**: where the full URL can be controlled.\n* **Partial SSRF**: where only part of the URL can be controlled, such as the path component of a URL to a hardcoded domain.\n\n\nPartial control of a URL is often much harder to exploit. Therefore we have created a separate query for each of these.\n\nThis query covers full SSRF, to find partial SSRF use the `py/partial-ssrf` query.\n\n\n## Recommendation\nTo guard against SSRF attacks you should avoid putting user-provided input directly into a request URL. Instead, either maintain a list of authorized URLs on the server and choose from that list based on the input provided, or perform proper validation of the input.\n\n\n## Example\nThe following example shows code vulnerable to a full SSRF attack, because it uses untrusted input (HTTP request parameter) directly to construct a URL. By using `evil.com#` as the `target` value, the requested URL will be `https://evil.com#.example.com/data/`. It also shows how to remedy the problem by using the user input select a known fixed string.\n\n\n```python\nimport requests\nfrom flask import Flask, request\n\napp = Flask(__name__)\n\n@app.route(\"/full_ssrf\")\ndef full_ssrf():\n target = request.args[\"target\"]\n\n # BAD: user has full control of URL\n resp = requests.get(\"https://\" + target + \".example.com/data/\")\n\n # GOOD: `subdomain` is controlled by the server.\n subdomain = \"europe\" if target == \"EU\" else \"world\"\n resp = requests.get(\"https://\" + subdomain + \".example.com/data/\")\n\n```\n\n## Example\nThe following example shows code vulnerable to a partial SSRF attack, because it uses untrusted input (HTTP request parameter) directly to construct a URL. By using `../transfer-funds-to/123?amount=456` as the `user_id` value, the requested URL will be `https://api.example.com/transfer-funds-to/123?amount=456`. It also shows how to remedy the problem by validating the input.\n\n\n```python\nimport requests\nfrom flask import Flask, request\n\napp = Flask(__name__)\n\n@app.route(\"/partial_ssrf\")\ndef partial_ssrf():\n user_id = request.args[\"user_id\"]\n\n # BAD: user can fully control the path component of the URL\n resp = requests.get(\"https://api.example.com/user_info/\" + user_id)\n\n if user_id.isalnum():\n # GOOD: user_id is restricted to be alpha-numeric, and cannot alter path component of URL\n resp = requests.get(\"https://api.example.com/user_info/\" + user_id)\n\n```\n\n## References\n* [OWASP SSRF article](https://owasp.org/www-community/attacks/Server_Side_Request_Forgery)\n* [PortSwigger SSRF article](https://portswigger.net/web-security/ssrf)\n* Common Weakness Enumeration: [CWE-918](https://cwe.mitre.org/data/definitions/918.html).\n","text":"# Full server-side request forgery\nDirectly incorporating user input into an HTTP request without validating the input can facilitate server-side request forgery (SSRF) attacks. In these attacks, the request may be changed, directed at a different server, or via a different protocol. This can allow the attacker to obtain sensitive information or perform actions with escalated privilege.\n\nWe make a distinctions between how much of the URL an attacker can control:\n\n* **Full SSRF**: where the full URL can be controlled.\n* **Partial SSRF**: where only part of the URL can be controlled, such as the path component of a URL to a hardcoded domain.\n\n\nPartial control of a URL is often much harder to exploit. Therefore we have created a separate query for each of these.\n\nThis query covers full SSRF, to find partial SSRF use the `py/partial-ssrf` query.\n\n\n## Recommendation\nTo guard against SSRF attacks you should avoid putting user-provided input directly into a request URL. Instead, either maintain a list of authorized URLs on the server and choose from that list based on the input provided, or perform proper validation of the input.\n\n\n## Example\nThe following example shows code vulnerable to a full SSRF attack, because it uses untrusted input (HTTP request parameter) directly to construct a URL. By using `evil.com#` as the `target` value, the requested URL will be `https://evil.com#.example.com/data/`. It also shows how to remedy the problem by using the user input select a known fixed string.\n\n\n```python\nimport requests\nfrom flask import Flask, request\n\napp = Flask(__name__)\n\n@app.route(\"/full_ssrf\")\ndef full_ssrf():\n target = request.args[\"target\"]\n\n # BAD: user has full control of URL\n resp = requests.get(\"https://\" + target + \".example.com/data/\")\n\n # GOOD: `subdomain` is controlled by the server.\n subdomain = \"europe\" if target == \"EU\" else \"world\"\n resp = requests.get(\"https://\" + subdomain + \".example.com/data/\")\n\n```\n\n## Example\nThe following example shows code vulnerable to a partial SSRF attack, because it uses untrusted input (HTTP request parameter) directly to construct a URL. By using `../transfer-funds-to/123?amount=456` as the `user_id` value, the requested URL will be `https://api.example.com/transfer-funds-to/123?amount=456`. It also shows how to remedy the problem by validating the input.\n\n\n```python\nimport requests\nfrom flask import Flask, request\n\napp = Flask(__name__)\n\n@app.route(\"/partial_ssrf\")\ndef partial_ssrf():\n user_id = request.args[\"user_id\"]\n\n # BAD: user can fully control the path component of the URL\n resp = requests.get(\"https://api.example.com/user_info/\" + user_id)\n\n if user_id.isalnum():\n # GOOD: user_id is restricted to be alpha-numeric, and cannot alter path component of URL\n resp = requests.get(\"https://api.example.com/user_info/\" + user_id)\n\n```\n\n## References\n* [OWASP SSRF article](https://owasp.org/www-community/attacks/Server_Side_Request_Forgery)\n* [PortSwigger SSRF article](https://portswigger.net/web-security/ssrf)\n* Common Weakness Enumeration: [CWE-918](https://cwe.mitre.org/data/definitions/918.html).\n"},"id":"py/full-ssrf","name":"py/full-ssrf","properties":{"precision":"high","queryURI":"https://github.com/github/codeql/blob/39a67b6e2e6490a9bd010db50e148f647765e9f7/python/ql/src/Security/CWE-918/FullServerSideRequestForgery.ql","security-severity":"9.1","tags":["external/cwe/cwe-918","security"]},"shortDescription":{"text":"Full server-side request forgery"}},{"defaultConfiguration":{"level":"error"},"fullDescription":{"text":"Writing user input directly to an HTTP header makes code vulnerable to attack by header splitting."},"help":{"markdown":"# HTTP Response Splitting\nDirectly writing user input (for example, an HTTP request parameter) to an HTTP header can lead to an HTTP response-splitting vulnerability.\n\nIf user-controlled input is used in an HTTP header that allows line break characters, an attacker can inject additional headers or control the response body, leading to vulnerabilities such as XSS or cache poisoning.\n\n\n## Recommendation\nEnsure that user input containing line break characters is not written to an HTTP header.\n\n\n## Example\nIn the following example, the case marked BAD writes user input to the header name. In the GOOD case, input is first escaped to not contain any line break characters.\n\n\n```python\n@app.route(\"/example_bad\")\ndef example_bad():\n rfs_header = request.args[\"rfs_header\"]\n response = Response()\n custom_header = \"X-MyHeader-\" + rfs_header\n # BAD: User input is used as part of the header name.\n response.headers[custom_header] = \"HeaderValue\" \n return response\n\n@app.route(\"/example_good\")\ndef example_bad():\n rfs_header = request.args[\"rfs_header\"]\n response = Response()\n custom_header = \"X-MyHeader-\" + rfs_header.replace(\"\\n\", \"\").replace(\"\\r\",\"\").replace(\":\",\"\")\n # GOOD: Line break characters are removed from the input.\n response.headers[custom_header] = \"HeaderValue\" \n return response\n```\n\n## References\n* SecLists.org: [HTTP response splitting](https://seclists.org/bugtraq/2005/Apr/187).\n* OWASP: [HTTP Response Splitting](https://www.owasp.org/index.php/HTTP_Response_Splitting).\n* Wikipedia: [HTTP response splitting](http://en.wikipedia.org/wiki/HTTP_response_splitting).\n* CAPEC: [CAPEC-105: HTTP Request Splitting](https://capec.mitre.org/data/definitions/105.html)\n* Common Weakness Enumeration: [CWE-113](https://cwe.mitre.org/data/definitions/113.html).\n* Common Weakness Enumeration: [CWE-79](https://cwe.mitre.org/data/definitions/79.html).\n","text":"# HTTP Response Splitting\nDirectly writing user input (for example, an HTTP request parameter) to an HTTP header can lead to an HTTP response-splitting vulnerability.\n\nIf user-controlled input is used in an HTTP header that allows line break characters, an attacker can inject additional headers or control the response body, leading to vulnerabilities such as XSS or cache poisoning.\n\n\n## Recommendation\nEnsure that user input containing line break characters is not written to an HTTP header.\n\n\n## Example\nIn the following example, the case marked BAD writes user input to the header name. In the GOOD case, input is first escaped to not contain any line break characters.\n\n\n```python\n@app.route(\"/example_bad\")\ndef example_bad():\n rfs_header = request.args[\"rfs_header\"]\n response = Response()\n custom_header = \"X-MyHeader-\" + rfs_header\n # BAD: User input is used as part of the header name.\n response.headers[custom_header] = \"HeaderValue\" \n return response\n\n@app.route(\"/example_good\")\ndef example_bad():\n rfs_header = request.args[\"rfs_header\"]\n response = Response()\n custom_header = \"X-MyHeader-\" + rfs_header.replace(\"\\n\", \"\").replace(\"\\r\",\"\").replace(\":\",\"\")\n # GOOD: Line break characters are removed from the input.\n response.headers[custom_header] = \"HeaderValue\" \n return response\n```\n\n## References\n* SecLists.org: [HTTP response splitting](https://seclists.org/bugtraq/2005/Apr/187).\n* OWASP: [HTTP Response Splitting](https://www.owasp.org/index.php/HTTP_Response_Splitting).\n* Wikipedia: [HTTP response splitting](http://en.wikipedia.org/wiki/HTTP_response_splitting).\n* CAPEC: [CAPEC-105: HTTP Request Splitting](https://capec.mitre.org/data/definitions/105.html)\n* Common Weakness Enumeration: [CWE-113](https://cwe.mitre.org/data/definitions/113.html).\n* Common Weakness Enumeration: [CWE-79](https://cwe.mitre.org/data/definitions/79.html).\n"},"id":"py/http-response-splitting","name":"py/http-response-splitting","properties":{"precision":"high","queryURI":"https://github.com/github/codeql/blob/39a67b6e2e6490a9bd010db50e148f647765e9f7/python/ql/src/Security/CWE-113/HeaderInjection.ql","security-severity":"6.1","tags":["external/cwe/cwe-079","external/cwe/cwe-113","security"]},"shortDescription":{"text":"HTTP Response Splitting"}},{"defaultConfiguration":{"level":"warning"},"fullDescription":{"text":"Matching a URL or hostname against a regular expression that contains an unescaped dot as part of the hostname might match more hostnames than expected."},"help":{"markdown":"# Incomplete regular expression for hostnames\nSanitizing untrusted URLs is a common technique for preventing attacks such as request forgeries and malicious redirections. Often, this is done by checking that the host of a URL is in a set of allowed hosts.\n\nIf a regular expression implements such a check, it is easy to accidentally make the check too permissive by not escaping the `.` meta-characters appropriately. Even if the check is not used in a security-critical context, the incomplete check may still cause undesirable behaviors when it accidentally succeeds.\n\n\n## Recommendation\nEscape all meta-characters appropriately when constructing regular expressions for security checks, and pay special attention to the `.` meta-character.\n\n\n## Example\nThe following example code checks that a URL redirection will reach the `example.com` domain, or one of its subdomains.\n\n\n```python\nfrom flask import Flask, request, redirect\nimport re\n\napp = Flask(__name__)\n\nUNSAFE_REGEX = re.compile(\"(www|beta).example.com/\")\nSAFE_REGEX = re.compile(r\"(www|beta)\\.example\\.com/\")\n\n@app.route('/some/path/bad')\ndef unsafe(request):\n target = request.args.get('target', '')\n if UNSAFE_REGEX.match(target):\n return redirect(target)\n\n@app.route('/some/path/good')\ndef safe(request):\n target = request.args.get('target', '')\n if SAFE_REGEX.match(target):\n return redirect(target)\n\n```\nThe `unsafe` check is easy to bypass because the unescaped `.` allows for any character before `example.com`, effectively allowing the redirect to go to an attacker-controlled domain such as `wwwXexample.com`.\n\nThe `safe` check closes this vulnerability by escaping the `.` so that URLs of the form `wwwXexample.com` are rejected.\n\n\n## References\n* OWASP: [SSRF](https://www.owasp.org/index.php/Server_Side_Request_Forgery)\n* OWASP: [XSS Unvalidated Redirects and Forwards Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html).\n* Common Weakness Enumeration: [CWE-20](https://cwe.mitre.org/data/definitions/20.html).\n","text":"# Incomplete regular expression for hostnames\nSanitizing untrusted URLs is a common technique for preventing attacks such as request forgeries and malicious redirections. Often, this is done by checking that the host of a URL is in a set of allowed hosts.\n\nIf a regular expression implements such a check, it is easy to accidentally make the check too permissive by not escaping the `.` meta-characters appropriately. Even if the check is not used in a security-critical context, the incomplete check may still cause undesirable behaviors when it accidentally succeeds.\n\n\n## Recommendation\nEscape all meta-characters appropriately when constructing regular expressions for security checks, and pay special attention to the `.` meta-character.\n\n\n## Example\nThe following example code checks that a URL redirection will reach the `example.com` domain, or one of its subdomains.\n\n\n```python\nfrom flask import Flask, request, redirect\nimport re\n\napp = Flask(__name__)\n\nUNSAFE_REGEX = re.compile(\"(www|beta).example.com/\")\nSAFE_REGEX = re.compile(r\"(www|beta)\\.example\\.com/\")\n\n@app.route('/some/path/bad')\ndef unsafe(request):\n target = request.args.get('target', '')\n if UNSAFE_REGEX.match(target):\n return redirect(target)\n\n@app.route('/some/path/good')\ndef safe(request):\n target = request.args.get('target', '')\n if SAFE_REGEX.match(target):\n return redirect(target)\n\n```\nThe `unsafe` check is easy to bypass because the unescaped `.` allows for any character before `example.com`, effectively allowing the redirect to go to an attacker-controlled domain such as `wwwXexample.com`.\n\nThe `safe` check closes this vulnerability by escaping the `.` so that URLs of the form `wwwXexample.com` are rejected.\n\n\n## References\n* OWASP: [SSRF](https://www.owasp.org/index.php/Server_Side_Request_Forgery)\n* OWASP: [XSS Unvalidated Redirects and Forwards Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html).\n* Common Weakness Enumeration: [CWE-20](https://cwe.mitre.org/data/definitions/20.html).\n"},"id":"py/incomplete-hostname-regexp","name":"py/incomplete-hostname-regexp","properties":{"precision":"high","queryURI":"https://github.com/github/codeql/blob/39a67b6e2e6490a9bd010db50e148f647765e9f7/python/ql/src/Security/CWE-020/IncompleteHostnameRegExp.ql","security-severity":"7.8","tags":["correctness","external/cwe/cwe-020","security"]},"shortDescription":{"text":"Incomplete regular expression for hostnames"}},{"defaultConfiguration":{"level":"warning"},"fullDescription":{"text":"Security checks on the substrings of an unparsed URL are often vulnerable to bypassing."},"help":{"markdown":"# Incomplete URL substring sanitization\nSanitizing untrusted URLs is a common technique for preventing attacks such as request forgeries and malicious redirections. Usually, this is done by checking that the host of a URL is in a set of allowed hosts.\n\nHowever, treating the URL as a string and checking if one of the allowed hosts is a substring of the URL is very prone to errors. Malicious URLs can bypass such security checks by embedding one of the allowed hosts in an unexpected location.\n\nEven if the substring check is not used in a security-critical context, the incomplete check may still cause undesirable behaviors when the check succeeds accidentally.\n\n\n## Recommendation\nParse a URL before performing a check on its host value, and ensure that the check handles arbitrary subdomain sequences correctly.\n\n\n## Example\nThe following example code checks that a URL redirection will reach the `example.com` domain.\n\n\n```python\nfrom flask import Flask, request, redirect\nfrom urllib.parse import urlparse\n\napp = Flask(__name__)\n\n# Not safe, as \"evil-example.net/example.com\" would be accepted\n\n@app.route('/some/path/bad1')\ndef unsafe1(request):\n target = request.args.get('target', '')\n if \"example.com\" in target:\n return redirect(target)\n\n# Not safe, as \"benign-looking-prefix-example.com\" would be accepted\n\n@app.route('/some/path/bad2')\ndef unsafe2(request):\n target = request.args.get('target', '')\n if target.endswith(\"example.com\"):\n return redirect(target)\n\n\n\n#Simplest and safest approach is to use an allowlist\n\n@app.route('/some/path/good1')\ndef safe1(request):\n allowlist = [\n \"example.com/home\",\n \"example.com/login\",\n ]\n target = request.args.get('target', '')\n if target in allowlist:\n return redirect(target)\n\n#More complex example allowing sub-domains.\n\n@app.route('/some/path/good2')\ndef safe2(request):\n target = request.args.get('target', '')\n host = urlparse(target).hostname\n #Note the '.' preceding example.com\n if host and host.endswith(\".example.com\"):\n return redirect(target)\n\n\n```\nThe first two examples show unsafe checks that are easily bypassed. In `unsafe1` the attacker can simply add `example.com` anywhere in the url. For example, `http://evil-example.net/example.com`.\n\nIn `unsafe2` the attacker must use a hostname ending in `example.com`, but that is easy to do. For example, `http://benign-looking-prefix-example.com`.\n\nThe second two examples show safe checks. In `safe1`, an allowlist is used. Although fairly inflexible, this is easy to get right and is most likely to be safe.\n\nIn `safe2`, `urlparse` is used to parse the URL, then the hostname is checked to make sure it ends with `.example.com`.\n\n\n## References\n* OWASP: [SSRF](https://www.owasp.org/index.php/Server_Side_Request_Forgery)\n* OWASP: [XSS Unvalidated Redirects and Forwards Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html).\n* Common Weakness Enumeration: [CWE-20](https://cwe.mitre.org/data/definitions/20.html).\n","text":"# Incomplete URL substring sanitization\nSanitizing untrusted URLs is a common technique for preventing attacks such as request forgeries and malicious redirections. Usually, this is done by checking that the host of a URL is in a set of allowed hosts.\n\nHowever, treating the URL as a string and checking if one of the allowed hosts is a substring of the URL is very prone to errors. Malicious URLs can bypass such security checks by embedding one of the allowed hosts in an unexpected location.\n\nEven if the substring check is not used in a security-critical context, the incomplete check may still cause undesirable behaviors when the check succeeds accidentally.\n\n\n## Recommendation\nParse a URL before performing a check on its host value, and ensure that the check handles arbitrary subdomain sequences correctly.\n\n\n## Example\nThe following example code checks that a URL redirection will reach the `example.com` domain.\n\n\n```python\nfrom flask import Flask, request, redirect\nfrom urllib.parse import urlparse\n\napp = Flask(__name__)\n\n# Not safe, as \"evil-example.net/example.com\" would be accepted\n\n@app.route('/some/path/bad1')\ndef unsafe1(request):\n target = request.args.get('target', '')\n if \"example.com\" in target:\n return redirect(target)\n\n# Not safe, as \"benign-looking-prefix-example.com\" would be accepted\n\n@app.route('/some/path/bad2')\ndef unsafe2(request):\n target = request.args.get('target', '')\n if target.endswith(\"example.com\"):\n return redirect(target)\n\n\n\n#Simplest and safest approach is to use an allowlist\n\n@app.route('/some/path/good1')\ndef safe1(request):\n allowlist = [\n \"example.com/home\",\n \"example.com/login\",\n ]\n target = request.args.get('target', '')\n if target in allowlist:\n return redirect(target)\n\n#More complex example allowing sub-domains.\n\n@app.route('/some/path/good2')\ndef safe2(request):\n target = request.args.get('target', '')\n host = urlparse(target).hostname\n #Note the '.' preceding example.com\n if host and host.endswith(\".example.com\"):\n return redirect(target)\n\n\n```\nThe first two examples show unsafe checks that are easily bypassed. In `unsafe1` the attacker can simply add `example.com` anywhere in the url. For example, `http://evil-example.net/example.com`.\n\nIn `unsafe2` the attacker must use a hostname ending in `example.com`, but that is easy to do. For example, `http://benign-looking-prefix-example.com`.\n\nThe second two examples show safe checks. In `safe1`, an allowlist is used. Although fairly inflexible, this is easy to get right and is most likely to be safe.\n\nIn `safe2`, `urlparse` is used to parse the URL, then the hostname is checked to make sure it ends with `.example.com`.\n\n\n## References\n* OWASP: [SSRF](https://www.owasp.org/index.php/Server_Side_Request_Forgery)\n* OWASP: [XSS Unvalidated Redirects and Forwards Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html).\n* Common Weakness Enumeration: [CWE-20](https://cwe.mitre.org/data/definitions/20.html).\n"},"id":"py/incomplete-url-substring-sanitization","name":"py/incomplete-url-substring-sanitization","properties":{"precision":"high","queryURI":"https://github.com/github/codeql/blob/39a67b6e2e6490a9bd010db50e148f647765e9f7/python/ql/src/Security/CWE-020/IncompleteUrlSubstringSanitization.ql","security-severity":"7.8","tags":["correctness","external/cwe/cwe-20","security"]},"shortDescription":{"text":"Incomplete URL substring sanitization"}},{"defaultConfiguration":{"level":"warning"},"fullDescription":{"text":"Insecure cookies may be sent in cleartext, which makes them vulnerable to interception."},"help":{"markdown":"# Failure to use secure cookies\nCookies without the `Secure` flag set may be transmitted using HTTP instead of HTTPS, which leaves them vulnerable to reading by a third party.\n\nCookies without the `HttpOnly` flag set are accessible to JavaScript running in the same origin. In case of a Cross-Site Scripting (XSS) vulnerability, the cookie can be stolen by a malicious script.\n\nCookies with the `SameSite` attribute set to `'None'` will be sent with cross-origin requests, which can be controlled by third-party JavaScript code and allow for Cross-Site Request Forgery (CSRF) attacks.\n\n\n## Recommendation\nAlways set `secure` to `True` or add \"; Secure;\" to the cookie's raw value.\n\nAlways set `httponly` to `True` or add \"; HttpOnly;\" to the cookie's raw value.\n\nAlways set `samesite` to `Lax` or `Strict`, or add \"; SameSite=Lax;\", or \"; Samesite=Strict;\" to the cookie's raw header value.\n\n\n## Example\nIn the following examples, the cases marked GOOD show secure cookie attributes being set; whereas in the cases marked BAD they are not set.\n\n\n```python\nfrom flask import Flask, request, make_response, Response\n\n\n@app.route(\"/good1\")\ndef good1():\n resp = make_response()\n resp.set_cookie(\"name\", value=\"value\", secure=True, httponly=True, samesite='Strict') # GOOD: Attributes are securely set\n return resp\n\n\n@app.route(\"/good2\")\ndef good2():\n resp = make_response()\n resp.headers['Set-Cookie'] = \"name=value; Secure; HttpOnly; SameSite=Strict\" # GOOD: Attributes are securely set \n return resp\n\n@app.route(\"/bad1\")\n resp = make_response()\n resp.set_cookie(\"name\", value=\"value\", samesite='None') # BAD: the SameSite attribute is set to 'None' and the 'Secure' and 'HttpOnly' attributes are set to False by default.\n return resp\n```\n\n## References\n* Detectify: [Cookie lack Secure flag](https://support.detectify.com/support/solutions/articles/48001048982-cookie-lack-secure-flag).\n* PortSwigger: [TLS cookie without secure flag set](https://portswigger.net/kb/issues/00500200_tls-cookie-without-secure-flag-set).\n* Common Weakness Enumeration: [CWE-614](https://cwe.mitre.org/data/definitions/614.html).\n* Common Weakness Enumeration: [CWE-1004](https://cwe.mitre.org/data/definitions/1004.html).\n* Common Weakness Enumeration: [CWE-1275](https://cwe.mitre.org/data/definitions/1275.html).\n","text":"# Failure to use secure cookies\nCookies without the `Secure` flag set may be transmitted using HTTP instead of HTTPS, which leaves them vulnerable to reading by a third party.\n\nCookies without the `HttpOnly` flag set are accessible to JavaScript running in the same origin. In case of a Cross-Site Scripting (XSS) vulnerability, the cookie can be stolen by a malicious script.\n\nCookies with the `SameSite` attribute set to `'None'` will be sent with cross-origin requests, which can be controlled by third-party JavaScript code and allow for Cross-Site Request Forgery (CSRF) attacks.\n\n\n## Recommendation\nAlways set `secure` to `True` or add \"; Secure;\" to the cookie's raw value.\n\nAlways set `httponly` to `True` or add \"; HttpOnly;\" to the cookie's raw value.\n\nAlways set `samesite` to `Lax` or `Strict`, or add \"; SameSite=Lax;\", or \"; Samesite=Strict;\" to the cookie's raw header value.\n\n\n## Example\nIn the following examples, the cases marked GOOD show secure cookie attributes being set; whereas in the cases marked BAD they are not set.\n\n\n```python\nfrom flask import Flask, request, make_response, Response\n\n\n@app.route(\"/good1\")\ndef good1():\n resp = make_response()\n resp.set_cookie(\"name\", value=\"value\", secure=True, httponly=True, samesite='Strict') # GOOD: Attributes are securely set\n return resp\n\n\n@app.route(\"/good2\")\ndef good2():\n resp = make_response()\n resp.headers['Set-Cookie'] = \"name=value; Secure; HttpOnly; SameSite=Strict\" # GOOD: Attributes are securely set \n return resp\n\n@app.route(\"/bad1\")\n resp = make_response()\n resp.set_cookie(\"name\", value=\"value\", samesite='None') # BAD: the SameSite attribute is set to 'None' and the 'Secure' and 'HttpOnly' attributes are set to False by default.\n return resp\n```\n\n## References\n* Detectify: [Cookie lack Secure flag](https://support.detectify.com/support/solutions/articles/48001048982-cookie-lack-secure-flag).\n* PortSwigger: [TLS cookie without secure flag set](https://portswigger.net/kb/issues/00500200_tls-cookie-without-secure-flag-set).\n* Common Weakness Enumeration: [CWE-614](https://cwe.mitre.org/data/definitions/614.html).\n* Common Weakness Enumeration: [CWE-1004](https://cwe.mitre.org/data/definitions/1004.html).\n* Common Weakness Enumeration: [CWE-1275](https://cwe.mitre.org/data/definitions/1275.html).\n"},"id":"py/insecure-cookie","name":"py/insecure-cookie","properties":{"precision":"high","queryURI":"https://github.com/github/codeql/blob/39a67b6e2e6490a9bd010db50e148f647765e9f7/python/ql/src/Security/CWE-614/InsecureCookie.ql","security-severity":"5","tags":["external/cwe/cwe-1004","external/cwe/cwe-1275","external/cwe/cwe-614","security"]},"shortDescription":{"text":"Failure to use secure cookies"}},{"defaultConfiguration":{"level":"warning"},"fullDescription":{"text":"Leaving the SSL/TLS version unspecified may result in an insecure default protocol being used."},"help":{"markdown":"# Default version of SSL/TLS may be insecure\nThe `ssl.wrap_socket` function defaults to an insecure version of SSL/TLS when no specific protocol version is specified. This may leave the connection vulnerable to attack.\n\n\n## Recommendation\nEnsure that a modern, strong protocol is used. All versions of SSL, and TLS 1.0 and 1.1 are known to be vulnerable to attacks. Using TLS 1.2 or above is strongly recommended. If no explicit `ssl_version` is specified, the default `PROTOCOL_TLS` is chosen. This protocol is insecure because it allows TLS 1.0 and TLS 1.1 and so should not be used.\n\n\n## Example\nThe following code shows two different ways of setting up a connection using SSL or TLS. They are both potentially insecure because the default version is used.\n\n\n```python\nimport ssl\nimport socket\n\n# Using the deprecated ssl.wrap_socket method\nssl.wrap_socket(socket.socket())\n\n# Using SSLContext\ncontext = ssl.SSLContext()\n\n```\nBoth of the cases above should be updated to use a secure protocol instead, for instance by specifying `ssl_version=PROTOCOL_TLSv1_2` as a keyword argument.\n\nThe latter example can also be made secure by modifying the created context before it is used to create a connection. Therefore it will not be flagged by this query. However, if a connection is created before the context has been secured (for example, by setting the value of `minimum_version`), then the code should be flagged by the query `py/insecure-protocol`.\n\nNote that `ssl.wrap_socket` has been deprecated in Python 3.7. The recommended alternatives are:\n\n* `ssl.SSLContext` - supported in Python 2.7.9, 3.2, and later versions\n* `ssl.create_default_context` - a convenience function, supported in Python 3.4 and later versions.\nEven when you use these alternatives, you should ensure that a safe protocol is used. The following code illustrates how to use flags (available since Python 3.2) or the \\`minimum_version\\` field (favored since Python 3.7) to restrict the protocols accepted when creating a connection.\n\n\n```python\nimport ssl\n\n# Using flags to restrict the protocol\ncontext = ssl.SSLContext()\ncontext.options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1\n\n# Declaring a minimum version to restrict the protocol\ncontext = ssl.create_default_context()\ncontext.minimum_version = ssl.TLSVersion.TLSv1_2\n\n```\n\n## References\n* Wikipedia: [ Transport Layer Security](https://en.wikipedia.org/wiki/Transport_Layer_Security).\n* Python 3 documentation: [ class ssl.SSLContext](https://docs.python.org/3/library/ssl.html#ssl.SSLContext).\n* Python 3 documentation: [ ssl.wrap_socket](https://docs.python.org/3/library/ssl.html#ssl.wrap_socket).\n* Python 3 documentation: [ notes on context creation](https://docs.python.org/3/library/ssl.html#functions-constants-and-exceptions).\n* Python 3 documentation: [ notes on security considerations](https://docs.python.org/3/library/ssl.html#ssl-security).\n* Common Weakness Enumeration: [CWE-327](https://cwe.mitre.org/data/definitions/327.html).\n","text":"# Default version of SSL/TLS may be insecure\nThe `ssl.wrap_socket` function defaults to an insecure version of SSL/TLS when no specific protocol version is specified. This may leave the connection vulnerable to attack.\n\n\n## Recommendation\nEnsure that a modern, strong protocol is used. All versions of SSL, and TLS 1.0 and 1.1 are known to be vulnerable to attacks. Using TLS 1.2 or above is strongly recommended. If no explicit `ssl_version` is specified, the default `PROTOCOL_TLS` is chosen. This protocol is insecure because it allows TLS 1.0 and TLS 1.1 and so should not be used.\n\n\n## Example\nThe following code shows two different ways of setting up a connection using SSL or TLS. They are both potentially insecure because the default version is used.\n\n\n```python\nimport ssl\nimport socket\n\n# Using the deprecated ssl.wrap_socket method\nssl.wrap_socket(socket.socket())\n\n# Using SSLContext\ncontext = ssl.SSLContext()\n\n```\nBoth of the cases above should be updated to use a secure protocol instead, for instance by specifying `ssl_version=PROTOCOL_TLSv1_2` as a keyword argument.\n\nThe latter example can also be made secure by modifying the created context before it is used to create a connection. Therefore it will not be flagged by this query. However, if a connection is created before the context has been secured (for example, by setting the value of `minimum_version`), then the code should be flagged by the query `py/insecure-protocol`.\n\nNote that `ssl.wrap_socket` has been deprecated in Python 3.7. The recommended alternatives are:\n\n* `ssl.SSLContext` - supported in Python 2.7.9, 3.2, and later versions\n* `ssl.create_default_context` - a convenience function, supported in Python 3.4 and later versions.\nEven when you use these alternatives, you should ensure that a safe protocol is used. The following code illustrates how to use flags (available since Python 3.2) or the \\`minimum_version\\` field (favored since Python 3.7) to restrict the protocols accepted when creating a connection.\n\n\n```python\nimport ssl\n\n# Using flags to restrict the protocol\ncontext = ssl.SSLContext()\ncontext.options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1\n\n# Declaring a minimum version to restrict the protocol\ncontext = ssl.create_default_context()\ncontext.minimum_version = ssl.TLSVersion.TLSv1_2\n\n```\n\n## References\n* Wikipedia: [ Transport Layer Security](https://en.wikipedia.org/wiki/Transport_Layer_Security).\n* Python 3 documentation: [ class ssl.SSLContext](https://docs.python.org/3/library/ssl.html#ssl.SSLContext).\n* Python 3 documentation: [ ssl.wrap_socket](https://docs.python.org/3/library/ssl.html#ssl.wrap_socket).\n* Python 3 documentation: [ notes on context creation](https://docs.python.org/3/library/ssl.html#functions-constants-and-exceptions).\n* Python 3 documentation: [ notes on security considerations](https://docs.python.org/3/library/ssl.html#ssl-security).\n* Common Weakness Enumeration: [CWE-327](https://cwe.mitre.org/data/definitions/327.html).\n"},"id":"py/insecure-default-protocol","name":"py/insecure-default-protocol","properties":{"precision":"high","queryURI":"https://github.com/github/codeql/blob/39a67b6e2e6490a9bd010db50e148f647765e9f7/python/ql/src/Security/CWE-327/InsecureDefaultProtocol.ql","security-severity":"7.5","tags":["external/cwe/cwe-327","security"]},"shortDescription":{"text":"Default version of SSL/TLS may be insecure"}},{"defaultConfiguration":{"level":"warning"},"fullDescription":{"text":"Using an insecure SSL/TLS version may leave the connection vulnerable to attacks."},"help":{"markdown":"# Use of insecure SSL/TLS version\nUsing a broken or weak cryptographic protocol may make a connection vulnerable to interference from an attacker.\n\n\n## Recommendation\nEnsure that a modern, strong protocol is used. All versions of SSL, and TLS versions 1.0 and 1.1 are known to be vulnerable to attacks. Using TLS 1.2 or above is strongly recommended.\n\n\n## Example\nThe following code shows a variety of ways of setting up a connection using SSL or TLS. They are all insecure because of the version specified.\n\n\n```python\nimport ssl\nimport socket\n\n# Using the deprecated ssl.wrap_socket method\nssl.wrap_socket(socket.socket(), ssl_version=ssl.PROTOCOL_SSLv2)\n\n# Using SSLContext\ncontext = ssl.SSLContext(ssl_version=ssl.PROTOCOL_SSLv3)\n\n# Using pyOpenSSL\n\nfrom pyOpenSSL import SSL\n\ncontext = SSL.Context(SSL.TLSv1_METHOD)\n\n\n\n```\nAll cases should be updated to use a secure protocol, such as `PROTOCOL_TLSv1_2`.\n\nNote that `ssl.wrap_socket` has been deprecated in Python 3.7. The recommended alternatives are:\n\n* `ssl.SSLContext` - supported in Python 2.7.9, 3.2, and later versions\n* `ssl.create_default_context` - a convenience function, supported in Python 3.4 and later versions.\nEven when you use these alternatives, you should ensure that a safe protocol is used. The following code illustrates how to use flags (available since Python 3.2) or the \\`minimum_version\\` field (favored since Python 3.7) to restrict the protocols accepted when creating a connection.\n\n\n```python\nimport ssl\n\n# Using flags to restrict the protocol\ncontext = ssl.SSLContext()\ncontext.options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1\n\n# Declaring a minimum version to restrict the protocol\ncontext = ssl.create_default_context()\ncontext.minimum_version = ssl.TLSVersion.TLSv1_2\n\n```\n\n## References\n* Wikipedia: [ Transport Layer Security](https://en.wikipedia.org/wiki/Transport_Layer_Security).\n* Python 3 documentation: [ class ssl.SSLContext](https://docs.python.org/3/library/ssl.html#ssl.SSLContext).\n* Python 3 documentation: [ ssl.wrap_socket](https://docs.python.org/3/library/ssl.html#ssl.wrap_socket).\n* Python 3 documentation: [ notes on context creation](https://docs.python.org/3/library/ssl.html#functions-constants-and-exceptions).\n* Python 3 documentation: [ notes on security considerations](https://docs.python.org/3/library/ssl.html#ssl-security).\n* pyOpenSSL documentation: [ An interface to the SSL-specific parts of OpenSSL](https://pyopenssl.org/en/stable/api/ssl.html).\n* Common Weakness Enumeration: [CWE-327](https://cwe.mitre.org/data/definitions/327.html).\n","text":"# Use of insecure SSL/TLS version\nUsing a broken or weak cryptographic protocol may make a connection vulnerable to interference from an attacker.\n\n\n## Recommendation\nEnsure that a modern, strong protocol is used. All versions of SSL, and TLS versions 1.0 and 1.1 are known to be vulnerable to attacks. Using TLS 1.2 or above is strongly recommended.\n\n\n## Example\nThe following code shows a variety of ways of setting up a connection using SSL or TLS. They are all insecure because of the version specified.\n\n\n```python\nimport ssl\nimport socket\n\n# Using the deprecated ssl.wrap_socket method\nssl.wrap_socket(socket.socket(), ssl_version=ssl.PROTOCOL_SSLv2)\n\n# Using SSLContext\ncontext = ssl.SSLContext(ssl_version=ssl.PROTOCOL_SSLv3)\n\n# Using pyOpenSSL\n\nfrom pyOpenSSL import SSL\n\ncontext = SSL.Context(SSL.TLSv1_METHOD)\n\n\n\n```\nAll cases should be updated to use a secure protocol, such as `PROTOCOL_TLSv1_2`.\n\nNote that `ssl.wrap_socket` has been deprecated in Python 3.7. The recommended alternatives are:\n\n* `ssl.SSLContext` - supported in Python 2.7.9, 3.2, and later versions\n* `ssl.create_default_context` - a convenience function, supported in Python 3.4 and later versions.\nEven when you use these alternatives, you should ensure that a safe protocol is used. The following code illustrates how to use flags (available since Python 3.2) or the \\`minimum_version\\` field (favored since Python 3.7) to restrict the protocols accepted when creating a connection.\n\n\n```python\nimport ssl\n\n# Using flags to restrict the protocol\ncontext = ssl.SSLContext()\ncontext.options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1\n\n# Declaring a minimum version to restrict the protocol\ncontext = ssl.create_default_context()\ncontext.minimum_version = ssl.TLSVersion.TLSv1_2\n\n```\n\n## References\n* Wikipedia: [ Transport Layer Security](https://en.wikipedia.org/wiki/Transport_Layer_Security).\n* Python 3 documentation: [ class ssl.SSLContext](https://docs.python.org/3/library/ssl.html#ssl.SSLContext).\n* Python 3 documentation: [ ssl.wrap_socket](https://docs.python.org/3/library/ssl.html#ssl.wrap_socket).\n* Python 3 documentation: [ notes on context creation](https://docs.python.org/3/library/ssl.html#functions-constants-and-exceptions).\n* Python 3 documentation: [ notes on security considerations](https://docs.python.org/3/library/ssl.html#ssl-security).\n* pyOpenSSL documentation: [ An interface to the SSL-specific parts of OpenSSL](https://pyopenssl.org/en/stable/api/ssl.html).\n* Common Weakness Enumeration: [CWE-327](https://cwe.mitre.org/data/definitions/327.html).\n"},"id":"py/insecure-protocol","name":"py/insecure-protocol","properties":{"precision":"high","queryURI":"https://github.com/github/codeql/blob/39a67b6e2e6490a9bd010db50e148f647765e9f7/python/ql/src/Security/CWE-327/InsecureProtocol.ql","security-severity":"7.5","tags":["external/cwe/cwe-327","security"]},"shortDescription":{"text":"Use of insecure SSL/TLS version"}},{"defaultConfiguration":{"level":"error"},"fullDescription":{"text":"Creating a temporary file using this method may be insecure."},"help":{"markdown":"# Insecure temporary file\nFunctions that create temporary file names (such as `tempfile.mktemp` and `os.tempnam`) are fundamentally insecure, as they do not ensure exclusive access to a file with the temporary name they return. The file name returned by these functions is guaranteed to be unique on creation but the file must be opened in a separate operation. There is no guarantee that the creation and open operations will happen atomically. This provides an opportunity for an attacker to interfere with the file before it is opened.\n\nNote that `mktemp` has been deprecated since Python 2.3.\n\n\n## Recommendation\nReplace the use of `mktemp` with some of the more secure functions in the `tempfile` module, such as `TemporaryFile`. If the file is intended to be accessed from other processes, consider using the `NamedTemporaryFile` function.\n\n\n## Example\nThe following piece of code opens a temporary file and writes a set of results to it. Because the file name is created using `mktemp`, another process may access this file before it is opened using `open`.\n\n\n```python\nfrom tempfile import mktemp\n\ndef write_results(results):\n filename = mktemp()\n with open(filename, \"w+\") as f:\n f.write(results)\n print(\"Results written to\", filename)\n\n```\nBy changing the code to use `NamedTemporaryFile` instead, the file is opened immediately.\n\n\n```python\nfrom tempfile import NamedTemporaryFile\n\ndef write_results(results):\n with NamedTemporaryFile(mode=\"w+\", delete=False) as f:\n f.write(results)\n print(\"Results written to\", f.name)\n\n```\n\n## References\n* Python Standard Library: [tempfile.mktemp](https://docs.python.org/3/library/tempfile.html#tempfile.mktemp).\n* Common Weakness Enumeration: [CWE-377](https://cwe.mitre.org/data/definitions/377.html).\n","text":"# Insecure temporary file\nFunctions that create temporary file names (such as `tempfile.mktemp` and `os.tempnam`) are fundamentally insecure, as they do not ensure exclusive access to a file with the temporary name they return. The file name returned by these functions is guaranteed to be unique on creation but the file must be opened in a separate operation. There is no guarantee that the creation and open operations will happen atomically. This provides an opportunity for an attacker to interfere with the file before it is opened.\n\nNote that `mktemp` has been deprecated since Python 2.3.\n\n\n## Recommendation\nReplace the use of `mktemp` with some of the more secure functions in the `tempfile` module, such as `TemporaryFile`. If the file is intended to be accessed from other processes, consider using the `NamedTemporaryFile` function.\n\n\n## Example\nThe following piece of code opens a temporary file and writes a set of results to it. Because the file name is created using `mktemp`, another process may access this file before it is opened using `open`.\n\n\n```python\nfrom tempfile import mktemp\n\ndef write_results(results):\n filename = mktemp()\n with open(filename, \"w+\") as f:\n f.write(results)\n print(\"Results written to\", filename)\n\n```\nBy changing the code to use `NamedTemporaryFile` instead, the file is opened immediately.\n\n\n```python\nfrom tempfile import NamedTemporaryFile\n\ndef write_results(results):\n with NamedTemporaryFile(mode=\"w+\", delete=False) as f:\n f.write(results)\n print(\"Results written to\", f.name)\n\n```\n\n## References\n* Python Standard Library: [tempfile.mktemp](https://docs.python.org/3/library/tempfile.html#tempfile.mktemp).\n* Common Weakness Enumeration: [CWE-377](https://cwe.mitre.org/data/definitions/377.html).\n"},"id":"py/insecure-temporary-file","name":"py/insecure-temporary-file","properties":{"precision":"high","queryURI":"https://github.com/github/codeql/blob/39a67b6e2e6490a9bd010db50e148f647765e9f7/python/ql/src/Security/CWE-377/InsecureTemporaryFile.ql","security-severity":"7","tags":["external/cwe/cwe-377","security"]},"shortDescription":{"text":"Insecure temporary file"}},{"defaultConfiguration":{"level":"error"},"fullDescription":{"text":"Building an LDAP query from user-controlled sources is vulnerable to insertion of malicious LDAP code by the user."},"help":{"markdown":"# LDAP query built from user-controlled sources\nIf an LDAP query or DN is built using string concatenation or string formatting, and the components of the concatenation include user input without any proper sanitization, a user is likely to be able to run malicious LDAP queries.\n\n\n## Recommendation\nIf user input must be included in an LDAP query or DN, it should be escaped to avoid a malicious user providing special characters that change the meaning of the query. In Python2, user input should be escaped with `ldap.dn.escape_dn_chars` or `ldap.filter.escape_filter_chars`, while in Python3, user input should be escaped with `ldap3.utils.dn.escape_rdn` or `ldap3.utils.conv.escape_filter_chars` depending on the component tainted by the user. A good practice is to escape filter characters that could change the meaning of the query (https://tools.ietf.org/search/rfc4515\\#section-3).\n\n\n## Example\nIn the following examples, the code accepts both `username` and `dc` from the user, which it then uses to build a LDAP query and DN.\n\nThe first and the second example uses the unsanitized user input directly in the search filter and DN for the LDAP query. A malicious user could provide special characters to change the meaning of these components, and search for a completely different set of values.\n\n\n```python\nfrom flask import request, Flask\nimport ldap\n\n\n@app.route(\"/normal\")\ndef normal():\n unsafe_dc = request.args['dc']\n unsafe_filter = request.args['username']\n\n dn = \"dc={}\".format(unsafe_dc)\n search_filter = \"(user={})\".format(unsafe_filter)\n\n ldap_connection = ldap.initialize(\"ldap://127.0.0.1\")\n user = ldap_connection.search_s(\n dn, ldap.SCOPE_SUBTREE, search_filter)\n\n```\n\n```python\nfrom flask import request, Flask\nimport ldap3\n\n\n@app.route(\"/normal\")\ndef normal():\n unsafe_dc = request.args['dc']\n unsafe_filter = request.args['username']\n\n dn = \"dc={}\".format(unsafe_dc)\n search_filter = \"(user={})\".format(unsafe_filter)\n\n srv = ldap3.Server('ldap://127.0.0.1')\n conn = ldap3.Connection(srv, user=dn, auto_bind=True)\n conn.search(dn, search_filter)\n\n```\nIn the third and fourth example, the input provided by the user is sanitized before it is included in the search filter or DN. This ensures the meaning of the query cannot be changed by a malicious user.\n\n\n```python\nfrom flask import request, Flask\nimport ldap\nimport ldap.filter\nimport ldap.dn\n\n\n@app.route(\"/normal\")\ndef normal():\n unsafe_dc = request.args['dc']\n unsafe_filter = request.args['username']\n\n safe_dc = ldap.dn.escape_dn_chars(unsafe_dc)\n safe_filter = ldap.filter.escape_filter_chars(unsafe_filter)\n\n dn = \"dc={}\".format(safe_dc)\n search_filter = \"(user={})\".format(safe_filter)\n\n ldap_connection = ldap.initialize(\"ldap://127.0.0.1\")\n user = ldap_connection.search_s(\n dn, ldap.SCOPE_SUBTREE, search_filter)\n\n```\n\n```python\nfrom flask import request, Flask\nimport ldap3\nfrom ldap3.utils.dn import escape_rdn\nfrom ldap3.utils.conv import escape_filter_chars\n\n\n@app.route(\"/normal\")\ndef normal():\n unsafe_dc = request.args['dc']\n unsafe_filter = request.args['username']\n\n safe_dc = escape_rdn(unsafe_dc)\n safe_filter = escape_filter_chars(unsafe_filter)\n\n dn = \"dc={}\".format(safe_dc)\n search_filter = \"(user={})\".format(safe_filter)\n\n srv = ldap3.Server('ldap://127.0.0.1')\n conn = ldap3.Connection(srv, user=dn, auto_bind=True)\n conn.search(dn, search_filter)\n\n```\n\n## References\n* OWASP: [LDAP Injection Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/LDAP_Injection_Prevention_Cheat_Sheet.html).\n* OWASP: [LDAP Injection](https://owasp.org/www-community/attacks/LDAP_Injection).\n* SonarSource: [RSPEC-2078](https://rules.sonarsource.com/python/RSPEC-2078).\n* Python2: [LDAP Documentation](https://www.python-ldap.org/en/python-ldap-3.3.0/reference/ldap.html).\n* Python3: [LDAP Documentation](https://ldap3.readthedocs.io/en/latest/).\n* Wikipedia: [LDAP injection](https://en.wikipedia.org/wiki/LDAP_injection).\n* BlackHat: [LDAP Injection and Blind LDAP Injection](https://www.blackhat.com/presentations/bh-europe-08/Alonso-Parada/Whitepaper/bh-eu-08-alonso-parada-WP.pdf).\n* LDAP: [Understanding and Defending Against LDAP Injection Attacks](https://ldap.com/2018/05/04/understanding-and-defending-against-ldap-injection-attacks/).\n* Common Weakness Enumeration: [CWE-90](https://cwe.mitre.org/data/definitions/90.html).\n","text":"# LDAP query built from user-controlled sources\nIf an LDAP query or DN is built using string concatenation or string formatting, and the components of the concatenation include user input without any proper sanitization, a user is likely to be able to run malicious LDAP queries.\n\n\n## Recommendation\nIf user input must be included in an LDAP query or DN, it should be escaped to avoid a malicious user providing special characters that change the meaning of the query. In Python2, user input should be escaped with `ldap.dn.escape_dn_chars` or `ldap.filter.escape_filter_chars`, while in Python3, user input should be escaped with `ldap3.utils.dn.escape_rdn` or `ldap3.utils.conv.escape_filter_chars` depending on the component tainted by the user. A good practice is to escape filter characters that could change the meaning of the query (https://tools.ietf.org/search/rfc4515\\#section-3).\n\n\n## Example\nIn the following examples, the code accepts both `username` and `dc` from the user, which it then uses to build a LDAP query and DN.\n\nThe first and the second example uses the unsanitized user input directly in the search filter and DN for the LDAP query. A malicious user could provide special characters to change the meaning of these components, and search for a completely different set of values.\n\n\n```python\nfrom flask import request, Flask\nimport ldap\n\n\n@app.route(\"/normal\")\ndef normal():\n unsafe_dc = request.args['dc']\n unsafe_filter = request.args['username']\n\n dn = \"dc={}\".format(unsafe_dc)\n search_filter = \"(user={})\".format(unsafe_filter)\n\n ldap_connection = ldap.initialize(\"ldap://127.0.0.1\")\n user = ldap_connection.search_s(\n dn, ldap.SCOPE_SUBTREE, search_filter)\n\n```\n\n```python\nfrom flask import request, Flask\nimport ldap3\n\n\n@app.route(\"/normal\")\ndef normal():\n unsafe_dc = request.args['dc']\n unsafe_filter = request.args['username']\n\n dn = \"dc={}\".format(unsafe_dc)\n search_filter = \"(user={})\".format(unsafe_filter)\n\n srv = ldap3.Server('ldap://127.0.0.1')\n conn = ldap3.Connection(srv, user=dn, auto_bind=True)\n conn.search(dn, search_filter)\n\n```\nIn the third and fourth example, the input provided by the user is sanitized before it is included in the search filter or DN. This ensures the meaning of the query cannot be changed by a malicious user.\n\n\n```python\nfrom flask import request, Flask\nimport ldap\nimport ldap.filter\nimport ldap.dn\n\n\n@app.route(\"/normal\")\ndef normal():\n unsafe_dc = request.args['dc']\n unsafe_filter = request.args['username']\n\n safe_dc = ldap.dn.escape_dn_chars(unsafe_dc)\n safe_filter = ldap.filter.escape_filter_chars(unsafe_filter)\n\n dn = \"dc={}\".format(safe_dc)\n search_filter = \"(user={})\".format(safe_filter)\n\n ldap_connection = ldap.initialize(\"ldap://127.0.0.1\")\n user = ldap_connection.search_s(\n dn, ldap.SCOPE_SUBTREE, search_filter)\n\n```\n\n```python\nfrom flask import request, Flask\nimport ldap3\nfrom ldap3.utils.dn import escape_rdn\nfrom ldap3.utils.conv import escape_filter_chars\n\n\n@app.route(\"/normal\")\ndef normal():\n unsafe_dc = request.args['dc']\n unsafe_filter = request.args['username']\n\n safe_dc = escape_rdn(unsafe_dc)\n safe_filter = escape_filter_chars(unsafe_filter)\n\n dn = \"dc={}\".format(safe_dc)\n search_filter = \"(user={})\".format(safe_filter)\n\n srv = ldap3.Server('ldap://127.0.0.1')\n conn = ldap3.Connection(srv, user=dn, auto_bind=True)\n conn.search(dn, search_filter)\n\n```\n\n## References\n* OWASP: [LDAP Injection Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/LDAP_Injection_Prevention_Cheat_Sheet.html).\n* OWASP: [LDAP Injection](https://owasp.org/www-community/attacks/LDAP_Injection).\n* SonarSource: [RSPEC-2078](https://rules.sonarsource.com/python/RSPEC-2078).\n* Python2: [LDAP Documentation](https://www.python-ldap.org/en/python-ldap-3.3.0/reference/ldap.html).\n* Python3: [LDAP Documentation](https://ldap3.readthedocs.io/en/latest/).\n* Wikipedia: [LDAP injection](https://en.wikipedia.org/wiki/LDAP_injection).\n* BlackHat: [LDAP Injection and Blind LDAP Injection](https://www.blackhat.com/presentations/bh-europe-08/Alonso-Parada/Whitepaper/bh-eu-08-alonso-parada-WP.pdf).\n* LDAP: [Understanding and Defending Against LDAP Injection Attacks](https://ldap.com/2018/05/04/understanding-and-defending-against-ldap-injection-attacks/).\n* Common Weakness Enumeration: [CWE-90](https://cwe.mitre.org/data/definitions/90.html).\n"},"id":"py/ldap-injection","name":"py/ldap-injection","properties":{"precision":"high","queryURI":"https://github.com/github/codeql/blob/39a67b6e2e6490a9bd010db50e148f647765e9f7/python/ql/src/Security/CWE-090/LdapInjection.ql","security-severity":"9.8","tags":["external/cwe/cwe-090","security"]},"shortDescription":{"text":"LDAP query built from user-controlled sources"}},{"defaultConfiguration":{"level":"error"},"fullDescription":{"text":"Building a NoSQL query from user-controlled sources is vulnerable to insertion of malicious NoSQL code by the user."},"help":{"markdown":"# NoSQL Injection\nPassing user-controlled sources into NoSQL queries can result in a NoSQL injection flaw. This tainted NoSQL query containing a user-controlled source can then execute a malicious query in a NoSQL database such as MongoDB. In order for the user-controlled source to taint the NoSQL query, the user-controller source must be converted into a Python object using something like `json.loads` or `xmltodict.parse`.\n\nBecause a user-controlled source is passed into the query, the malicious user can have complete control over the query itself. When the tainted query is executed, the malicious user can commit malicious actions such as bypassing role restrictions or accessing and modifying restricted data in the NoSQL database.\n\n\n## Recommendation\nNoSQL injections can be prevented by escaping user-input's special characters that are passed into the NoSQL query from the user-supplied source. Alternatively, using a sanitize library such as MongoSanitizer will ensure that user-supplied sources can not act as a malicious query.\n\n\n## Example\nIn the example below, the user-supplied source is passed to a MongoDB function that queries the MongoDB database.\n\n\n```python\nfrom flask import Flask, request\nfrom flask_pymongo import PyMongo\nimport json\n\nmongo = PyMongo(app)\n\n\n@app.route(\"/\")\ndef home_page():\n unsanitized_search = request.args['search']\n json_search = json.loads(unsanitized_search)\n\n result = mongo.db.user.find({'name': json_search})\n\n```\nThis can be fixed by using a sanitizer library like MongoSanitizer as shown in this annotated code version below.\n\n\n```python\nfrom flask import Flask, request\nfrom flask_pymongo import PyMongo\nfrom mongosanitizer.sanitizer import sanitize\nimport json\n\nmongo = PyMongo(app)\n\n\n@app.route(\"/\")\ndef home_page():\n unsafe_search = request.args['search']\n json_search = json.loads(unsafe_search)\n safe_search = sanitize(unsanitized_search)\n\n result = client.db.collection.find_one({'data': safe_search})\n\n```\n\n## References\n* Mongoengine: [Documentation](http://mongoengine.org/).\n* Flask-Mongoengine: [Documentation](http://docs.mongoengine.org/projects/flask-mongoengine/en/latest/).\n* PyMongo: [Documentation](https://pypi.org/project/pymongo/).\n* Flask-PyMongo: [Documentation](https://flask-pymongo.readthedocs.io/en/latest/).\n* OWASP: [NoSQL Injection](https://owasp.org/www-pdf-archive/GOD16-NOSQL.pdf).\n* Security Stack Exchange Discussion: [Question 83231](https://security.stackexchange.com/questions/83231/mongodb-nosql-injection-in-python-code).\n* Common Weakness Enumeration: [CWE-943](https://cwe.mitre.org/data/definitions/943.html).\n","text":"# NoSQL Injection\nPassing user-controlled sources into NoSQL queries can result in a NoSQL injection flaw. This tainted NoSQL query containing a user-controlled source can then execute a malicious query in a NoSQL database such as MongoDB. In order for the user-controlled source to taint the NoSQL query, the user-controller source must be converted into a Python object using something like `json.loads` or `xmltodict.parse`.\n\nBecause a user-controlled source is passed into the query, the malicious user can have complete control over the query itself. When the tainted query is executed, the malicious user can commit malicious actions such as bypassing role restrictions or accessing and modifying restricted data in the NoSQL database.\n\n\n## Recommendation\nNoSQL injections can be prevented by escaping user-input's special characters that are passed into the NoSQL query from the user-supplied source. Alternatively, using a sanitize library such as MongoSanitizer will ensure that user-supplied sources can not act as a malicious query.\n\n\n## Example\nIn the example below, the user-supplied source is passed to a MongoDB function that queries the MongoDB database.\n\n\n```python\nfrom flask import Flask, request\nfrom flask_pymongo import PyMongo\nimport json\n\nmongo = PyMongo(app)\n\n\n@app.route(\"/\")\ndef home_page():\n unsanitized_search = request.args['search']\n json_search = json.loads(unsanitized_search)\n\n result = mongo.db.user.find({'name': json_search})\n\n```\nThis can be fixed by using a sanitizer library like MongoSanitizer as shown in this annotated code version below.\n\n\n```python\nfrom flask import Flask, request\nfrom flask_pymongo import PyMongo\nfrom mongosanitizer.sanitizer import sanitize\nimport json\n\nmongo = PyMongo(app)\n\n\n@app.route(\"/\")\ndef home_page():\n unsafe_search = request.args['search']\n json_search = json.loads(unsafe_search)\n safe_search = sanitize(unsanitized_search)\n\n result = client.db.collection.find_one({'data': safe_search})\n\n```\n\n## References\n* Mongoengine: [Documentation](http://mongoengine.org/).\n* Flask-Mongoengine: [Documentation](http://docs.mongoengine.org/projects/flask-mongoengine/en/latest/).\n* PyMongo: [Documentation](https://pypi.org/project/pymongo/).\n* Flask-PyMongo: [Documentation](https://flask-pymongo.readthedocs.io/en/latest/).\n* OWASP: [NoSQL Injection](https://owasp.org/www-pdf-archive/GOD16-NOSQL.pdf).\n* Security Stack Exchange Discussion: [Question 83231](https://security.stackexchange.com/questions/83231/mongodb-nosql-injection-in-python-code).\n* Common Weakness Enumeration: [CWE-943](https://cwe.mitre.org/data/definitions/943.html).\n"},"id":"py/nosql-injection","name":"py/nosql-injection","properties":{"precision":"high","queryURI":"https://github.com/github/codeql/blob/39a67b6e2e6490a9bd010db50e148f647765e9f7/python/ql/src/Security/CWE-943/NoSqlInjection.ql","security-severity":"8.8","tags":["external/cwe/cwe-943","security"]},"shortDescription":{"text":"NoSQL Injection"}},{"defaultConfiguration":{"level":"warning"},"fullDescription":{"text":"Overly permissive regular expression ranges match a wider range of characters than intended. This may allow an attacker to bypass a filter or sanitizer."},"help":{"markdown":"# Overly permissive regular expression range\nIt's easy to write a regular expression range that matches a wider range of characters than you intended. For example, `/[a-zA-z]/` matches all lowercase and all uppercase letters, as you would expect, but it also matches the characters: `` [ \\ ] ^ _ ` ``.\n\nAnother common problem is failing to escape the dash character in a regular expression. An unescaped dash is interpreted as part of a range. For example, in the character class `[a-zA-Z0-9%=.,-_]` the last character range matches the 55 characters between `,` and `_` (both included), which overlaps with the range `[0-9]` and is clearly not intended by the writer.\n\n\n## Recommendation\nAvoid any confusion about which characters are included in the range by writing unambiguous regular expressions. Always check that character ranges match only the expected characters.\n\n\n## Example\nThe following example code is intended to check whether a string is a valid 6 digit hex color.\n\n```python\n\nimport re\ndef is_valid_hex_color(color):\n return re.match(r'^#[0-9a-fA-f]{6}$', color) is not None\n\n```\nHowever, the `A-f` range is overly large and matches every uppercase character. It would parse a \"color\" like `#XXYYZZ` as valid.\n\nThe fix is to use an uppercase `A-F` range instead.\n\n```python\n\nimport re\ndef is_valid_hex_color(color):\n return re.match(r'^#[0-9a-fA-F]{6}$', color) is not None\n\n```\n\n## References\n* GitHub Advisory Database: [CVE-2021-42740: Improper Neutralization of Special Elements used in a Command in Shell-quote](https://github.com/advisories/GHSA-g4rg-993r-mgx7)\n* wh0.github.io: [Exploiting CVE-2021-42740](https://wh0.github.io/2021/10/28/shell-quote-rce-exploiting.html)\n* Yosuke Ota: [no-obscure-range](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-obscure-range.html)\n* Paul Boyd: [The regex \\[,-.\\]](https://pboyd.io/posts/comma-dash-dot/)\n* Common Weakness Enumeration: [CWE-20](https://cwe.mitre.org/data/definitions/20.html).\n","text":"# Overly permissive regular expression range\nIt's easy to write a regular expression range that matches a wider range of characters than you intended. For example, `/[a-zA-z]/` matches all lowercase and all uppercase letters, as you would expect, but it also matches the characters: `` [ \\ ] ^ _ ` ``.\n\nAnother common problem is failing to escape the dash character in a regular expression. An unescaped dash is interpreted as part of a range. For example, in the character class `[a-zA-Z0-9%=.,-_]` the last character range matches the 55 characters between `,` and `_` (both included), which overlaps with the range `[0-9]` and is clearly not intended by the writer.\n\n\n## Recommendation\nAvoid any confusion about which characters are included in the range by writing unambiguous regular expressions. Always check that character ranges match only the expected characters.\n\n\n## Example\nThe following example code is intended to check whether a string is a valid 6 digit hex color.\n\n```python\n\nimport re\ndef is_valid_hex_color(color):\n return re.match(r'^#[0-9a-fA-f]{6}$', color) is not None\n\n```\nHowever, the `A-f` range is overly large and matches every uppercase character. It would parse a \"color\" like `#XXYYZZ` as valid.\n\nThe fix is to use an uppercase `A-F` range instead.\n\n```python\n\nimport re\ndef is_valid_hex_color(color):\n return re.match(r'^#[0-9a-fA-F]{6}$', color) is not None\n\n```\n\n## References\n* GitHub Advisory Database: [CVE-2021-42740: Improper Neutralization of Special Elements used in a Command in Shell-quote](https://github.com/advisories/GHSA-g4rg-993r-mgx7)\n* wh0.github.io: [Exploiting CVE-2021-42740](https://wh0.github.io/2021/10/28/shell-quote-rce-exploiting.html)\n* Yosuke Ota: [no-obscure-range](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-obscure-range.html)\n* Paul Boyd: [The regex \\[,-.\\]](https://pboyd.io/posts/comma-dash-dot/)\n* Common Weakness Enumeration: [CWE-20](https://cwe.mitre.org/data/definitions/20.html).\n"},"id":"py/overly-large-range","name":"py/overly-large-range","properties":{"precision":"high","queryURI":"https://github.com/github/codeql/blob/39a67b6e2e6490a9bd010db50e148f647765e9f7/python/ql/src/Security/CWE-020/OverlyLargeRange.ql","security-severity":"5","tags":["correctness","external/cwe/cwe-020","security"]},"shortDescription":{"text":"Overly permissive regular expression range"}},{"defaultConfiguration":{"level":"warning"},"fullDescription":{"text":"Not using `pam_acct_mgmt` after `pam_authenticate` to check the validity of a login can lead to authorization bypass."},"help":{"markdown":"# PAM authorization bypass due to incorrect usage\nUsing only a call to `pam_authenticate` to check the validity of a login can lead to authorization bypass vulnerabilities.\n\nA `pam_authenticate` only verifies the credentials of a user. It does not check if a user has an appropriate authorization to actually login. This means a user with an expired login or a password can still access the system.\n\n\n## Recommendation\nA call to `pam_authenticate` should be followed by a call to `pam_acct_mgmt` to check if a user is allowed to login.\n\n\n## Example\nIn the following example, the code only checks the credentials of a user. Hence, in this case, a user with expired credentials can still login. This can be verified by creating a new user account, expiring it with ``` chage -E0 `username` ``` and then trying to log in.\n\n\n```python\nlibpam = CDLL(find_library(\"pam\"))\n\npam_authenticate = libpam.pam_authenticate\npam_authenticate.restype = c_int\npam_authenticate.argtypes = [PamHandle, c_int]\n\ndef authenticate(username, password, service='login'):\n def my_conv(n_messages, messages, p_response, app_data):\n \"\"\"\n Simple conversation function that responds to any prompt where the echo is off with the supplied password\n \"\"\"\n ...\n\n handle = PamHandle()\n conv = PamConv(my_conv, 0)\n retval = pam_start(service, username, byref(conv), byref(handle))\n\n retval = pam_authenticate(handle, 0)\n return retval == 0\n\n```\nThis can be avoided by calling `pam_acct_mgmt` call to verify access as has been done in the snippet shown below.\n\n\n```python\nlibpam = CDLL(find_library(\"pam\"))\n\npam_authenticate = libpam.pam_authenticate\npam_authenticate.restype = c_int\npam_authenticate.argtypes = [PamHandle, c_int]\n\npam_acct_mgmt = libpam.pam_acct_mgmt\npam_acct_mgmt.restype = c_int\npam_acct_mgmt.argtypes = [PamHandle, c_int]\n\ndef authenticate(username, password, service='login'):\n def my_conv(n_messages, messages, p_response, app_data):\n \"\"\"\n Simple conversation function that responds to any prompt where the echo is off with the supplied password\n \"\"\"\n ...\n\n handle = PamHandle()\n conv = PamConv(my_conv, 0)\n retval = pam_start(service, username, byref(conv), byref(handle))\n\n retval = pam_authenticate(handle, 0)\n if retval == 0:\n retval = pam_acct_mgmt(handle, 0)\n return retval == 0\n\n```\n\n## References\n* Man-Page: [pam_acct_mgmt](https://man7.org/linux/man-pages/man3/pam_acct_mgmt.3.html)\n* Common Weakness Enumeration: [CWE-285](https://cwe.mitre.org/data/definitions/285.html).\n","text":"# PAM authorization bypass due to incorrect usage\nUsing only a call to `pam_authenticate` to check the validity of a login can lead to authorization bypass vulnerabilities.\n\nA `pam_authenticate` only verifies the credentials of a user. It does not check if a user has an appropriate authorization to actually login. This means a user with an expired login or a password can still access the system.\n\n\n## Recommendation\nA call to `pam_authenticate` should be followed by a call to `pam_acct_mgmt` to check if a user is allowed to login.\n\n\n## Example\nIn the following example, the code only checks the credentials of a user. Hence, in this case, a user with expired credentials can still login. This can be verified by creating a new user account, expiring it with ``` chage -E0 `username` ``` and then trying to log in.\n\n\n```python\nlibpam = CDLL(find_library(\"pam\"))\n\npam_authenticate = libpam.pam_authenticate\npam_authenticate.restype = c_int\npam_authenticate.argtypes = [PamHandle, c_int]\n\ndef authenticate(username, password, service='login'):\n def my_conv(n_messages, messages, p_response, app_data):\n \"\"\"\n Simple conversation function that responds to any prompt where the echo is off with the supplied password\n \"\"\"\n ...\n\n handle = PamHandle()\n conv = PamConv(my_conv, 0)\n retval = pam_start(service, username, byref(conv), byref(handle))\n\n retval = pam_authenticate(handle, 0)\n return retval == 0\n\n```\nThis can be avoided by calling `pam_acct_mgmt` call to verify access as has been done in the snippet shown below.\n\n\n```python\nlibpam = CDLL(find_library(\"pam\"))\n\npam_authenticate = libpam.pam_authenticate\npam_authenticate.restype = c_int\npam_authenticate.argtypes = [PamHandle, c_int]\n\npam_acct_mgmt = libpam.pam_acct_mgmt\npam_acct_mgmt.restype = c_int\npam_acct_mgmt.argtypes = [PamHandle, c_int]\n\ndef authenticate(username, password, service='login'):\n def my_conv(n_messages, messages, p_response, app_data):\n \"\"\"\n Simple conversation function that responds to any prompt where the echo is off with the supplied password\n \"\"\"\n ...\n\n handle = PamHandle()\n conv = PamConv(my_conv, 0)\n retval = pam_start(service, username, byref(conv), byref(handle))\n\n retval = pam_authenticate(handle, 0)\n if retval == 0:\n retval = pam_acct_mgmt(handle, 0)\n return retval == 0\n\n```\n\n## References\n* Man-Page: [pam_acct_mgmt](https://man7.org/linux/man-pages/man3/pam_acct_mgmt.3.html)\n* Common Weakness Enumeration: [CWE-285](https://cwe.mitre.org/data/definitions/285.html).\n"},"id":"py/pam-auth-bypass","name":"py/pam-auth-bypass","properties":{"precision":"high","queryURI":"https://github.com/github/codeql/blob/39a67b6e2e6490a9bd010db50e148f647765e9f7/python/ql/src/Security/CWE-285/PamAuthorization.ql","security-severity":"8.1","tags":["external/cwe/cwe-285","security"]},"shortDescription":{"text":"PAM authorization bypass due to incorrect usage"}},{"defaultConfiguration":{"level":"error"},"fullDescription":{"text":"Accepting unknown host keys can allow man-in-the-middle attacks."},"help":{"markdown":"# Accepting unknown SSH host keys when using Paramiko\nIn the Secure Shell (SSH) protocol, host keys are used to verify the identity of remote hosts. Accepting unknown host keys may leave the connection open to man-in-the-middle attacks.\n\n\n## Recommendation\nDo not accept unknown host keys. In particular, do not set the default missing host key policy for the Paramiko library to either `AutoAddPolicy` or `WarningPolicy`. Both of these policies continue even when the host key is unknown. The default setting of `RejectPolicy` is secure because it throws an exception when it encounters an unknown host key.\n\n\n## Example\nThe following example shows two ways of opening an SSH connection to `example.com`. The first function sets the missing host key policy to `AutoAddPolicy`. If the host key verification fails, the client will continue to interact with the server, even though the connection may be compromised. The second function sets the host key policy to `RejectPolicy`, and will throw an exception if the host key verification fails.\n\n\n```python\nfrom paramiko.client import SSHClient, AutoAddPolicy, RejectPolicy\n\ndef unsafe_connect():\n client = SSHClient()\n client.set_missing_host_key_policy(AutoAddPolicy)\n client.connect(\"example.com\")\n\n # ... interaction with server\n\n client.close()\n\ndef safe_connect():\n client = SSHClient()\n client.set_missing_host_key_policy(RejectPolicy)\n client.connect(\"example.com\")\n\n # ... interaction with server\n\n client.close()\n\n```\n\n## References\n* Paramiko documentation: [set_missing_host_key_policy](http://docs.paramiko.org/en/2.4/api/client.html?highlight=set_missing_host_key_policy#paramiko.client.SSHClient.set_missing_host_key_policy).\n* Common Weakness Enumeration: [CWE-295](https://cwe.mitre.org/data/definitions/295.html).\n","text":"# Accepting unknown SSH host keys when using Paramiko\nIn the Secure Shell (SSH) protocol, host keys are used to verify the identity of remote hosts. Accepting unknown host keys may leave the connection open to man-in-the-middle attacks.\n\n\n## Recommendation\nDo not accept unknown host keys. In particular, do not set the default missing host key policy for the Paramiko library to either `AutoAddPolicy` or `WarningPolicy`. Both of these policies continue even when the host key is unknown. The default setting of `RejectPolicy` is secure because it throws an exception when it encounters an unknown host key.\n\n\n## Example\nThe following example shows two ways of opening an SSH connection to `example.com`. The first function sets the missing host key policy to `AutoAddPolicy`. If the host key verification fails, the client will continue to interact with the server, even though the connection may be compromised. The second function sets the host key policy to `RejectPolicy`, and will throw an exception if the host key verification fails.\n\n\n```python\nfrom paramiko.client import SSHClient, AutoAddPolicy, RejectPolicy\n\ndef unsafe_connect():\n client = SSHClient()\n client.set_missing_host_key_policy(AutoAddPolicy)\n client.connect(\"example.com\")\n\n # ... interaction with server\n\n client.close()\n\ndef safe_connect():\n client = SSHClient()\n client.set_missing_host_key_policy(RejectPolicy)\n client.connect(\"example.com\")\n\n # ... interaction with server\n\n client.close()\n\n```\n\n## References\n* Paramiko documentation: [set_missing_host_key_policy](http://docs.paramiko.org/en/2.4/api/client.html?highlight=set_missing_host_key_policy#paramiko.client.SSHClient.set_missing_host_key_policy).\n* Common Weakness Enumeration: [CWE-295](https://cwe.mitre.org/data/definitions/295.html).\n"},"id":"py/paramiko-missing-host-key-validation","name":"py/paramiko-missing-host-key-validation","properties":{"precision":"high","queryURI":"https://github.com/github/codeql/blob/39a67b6e2e6490a9bd010db50e148f647765e9f7/python/ql/src/Security/CWE-295/MissingHostKeyValidation.ql","security-severity":"7.5","tags":["external/cwe/cwe-295","security"]},"shortDescription":{"text":"Accepting unknown SSH host keys when using Paramiko"}},{"defaultConfiguration":{"level":"error"},"fullDescription":{"text":"Accessing paths influenced by users can allow an attacker to access unexpected resources."},"help":{"markdown":"# Uncontrolled data used in path expression\nAccessing files using paths constructed from user-controlled data can allow an attacker to access unexpected resources. This can result in sensitive information being revealed or deleted, or an attacker being able to influence behavior by modifying unexpected files.\n\n\n## Recommendation\nValidate user input before using it to construct a file path, either using an off-the-shelf library function like `werkzeug.utils.secure_filename`, or by performing custom validation.\n\nIdeally, follow these rules:\n\n* Do not allow more than a single \".\" character.\n* Do not allow directory separators such as \"/\" or \"\\\\\" (depending on the file system).\n* Do not rely on simply replacing problematic sequences such as \"../\". For example, after applying this filter to \".../...//\", the resulting string would still be \"../\".\n* Use an allowlist of known good patterns.\n\n## Example\nIn the first example, a file name is read from an HTTP request and then used to access a file. However, a malicious user could enter a file name that is an absolute path, such as `\"/etc/passwd\"`.\n\nIn the second example, it appears that the user is restricted to opening a file within the `\"user\"` home directory. However, a malicious user could enter a file name containing special characters. For example, the string `\"../../../etc/passwd\"` will result in the code reading the file located at `\"/server/static/images/../../../etc/passwd\"`, which is the system's password file. This file would then be sent back to the user, giving them access to all the system's passwords. Note that a user could also use an absolute path here, since the result of `os.path.join(\"/server/static/images/\", \"/etc/passwd\")` is `\"/etc/passwd\"`.\n\nIn the third example, the path used to access the file system is normalized *before* being checked against a known prefix. This ensures that regardless of the user input, the resulting path is safe.\n\n\n```python\nimport os.path\nfrom flask import Flask, request, abort\n\napp = Flask(__name__)\n\n@app.route(\"/user_picture1\")\ndef user_picture1():\n filename = request.args.get('p')\n # BAD: This could read any file on the file system\n data = open(filename, 'rb').read()\n return data\n\n@app.route(\"/user_picture2\")\ndef user_picture2():\n base_path = '/server/static/images'\n filename = request.args.get('p')\n # BAD: This could still read any file on the file system\n data = open(os.path.join(base_path, filename), 'rb').read()\n return data\n\n@app.route(\"/user_picture3\")\ndef user_picture3():\n base_path = '/server/static/images'\n filename = request.args.get('p')\n #GOOD -- Verify with normalised version of path\n fullpath = os.path.normpath(os.path.join(base_path, filename))\n if not fullpath.startswith(base_path):\n raise Exception(\"not allowed\")\n data = open(fullpath, 'rb').read()\n return data\n\n```\n\n## References\n* OWASP: [Path Traversal](https://owasp.org/www-community/attacks/Path_Traversal).\n* npm: [werkzeug.utils.secure_filename](http://werkzeug.pocoo.org/docs/utils/#werkzeug.utils.secure_filename).\n* Common Weakness Enumeration: [CWE-22](https://cwe.mitre.org/data/definitions/22.html).\n* Common Weakness Enumeration: [CWE-23](https://cwe.mitre.org/data/definitions/23.html).\n* Common Weakness Enumeration: [CWE-36](https://cwe.mitre.org/data/definitions/36.html).\n* Common Weakness Enumeration: [CWE-73](https://cwe.mitre.org/data/definitions/73.html).\n* Common Weakness Enumeration: [CWE-99](https://cwe.mitre.org/data/definitions/99.html).\n","text":"# Uncontrolled data used in path expression\nAccessing files using paths constructed from user-controlled data can allow an attacker to access unexpected resources. This can result in sensitive information being revealed or deleted, or an attacker being able to influence behavior by modifying unexpected files.\n\n\n## Recommendation\nValidate user input before using it to construct a file path, either using an off-the-shelf library function like `werkzeug.utils.secure_filename`, or by performing custom validation.\n\nIdeally, follow these rules:\n\n* Do not allow more than a single \".\" character.\n* Do not allow directory separators such as \"/\" or \"\\\\\" (depending on the file system).\n* Do not rely on simply replacing problematic sequences such as \"../\". For example, after applying this filter to \".../...//\", the resulting string would still be \"../\".\n* Use an allowlist of known good patterns.\n\n## Example\nIn the first example, a file name is read from an HTTP request and then used to access a file. However, a malicious user could enter a file name that is an absolute path, such as `\"/etc/passwd\"`.\n\nIn the second example, it appears that the user is restricted to opening a file within the `\"user\"` home directory. However, a malicious user could enter a file name containing special characters. For example, the string `\"../../../etc/passwd\"` will result in the code reading the file located at `\"/server/static/images/../../../etc/passwd\"`, which is the system's password file. This file would then be sent back to the user, giving them access to all the system's passwords. Note that a user could also use an absolute path here, since the result of `os.path.join(\"/server/static/images/\", \"/etc/passwd\")` is `\"/etc/passwd\"`.\n\nIn the third example, the path used to access the file system is normalized *before* being checked against a known prefix. This ensures that regardless of the user input, the resulting path is safe.\n\n\n```python\nimport os.path\nfrom flask import Flask, request, abort\n\napp = Flask(__name__)\n\n@app.route(\"/user_picture1\")\ndef user_picture1():\n filename = request.args.get('p')\n # BAD: This could read any file on the file system\n data = open(filename, 'rb').read()\n return data\n\n@app.route(\"/user_picture2\")\ndef user_picture2():\n base_path = '/server/static/images'\n filename = request.args.get('p')\n # BAD: This could still read any file on the file system\n data = open(os.path.join(base_path, filename), 'rb').read()\n return data\n\n@app.route(\"/user_picture3\")\ndef user_picture3():\n base_path = '/server/static/images'\n filename = request.args.get('p')\n #GOOD -- Verify with normalised version of path\n fullpath = os.path.normpath(os.path.join(base_path, filename))\n if not fullpath.startswith(base_path):\n raise Exception(\"not allowed\")\n data = open(fullpath, 'rb').read()\n return data\n\n```\n\n## References\n* OWASP: [Path Traversal](https://owasp.org/www-community/attacks/Path_Traversal).\n* npm: [werkzeug.utils.secure_filename](http://werkzeug.pocoo.org/docs/utils/#werkzeug.utils.secure_filename).\n* Common Weakness Enumeration: [CWE-22](https://cwe.mitre.org/data/definitions/22.html).\n* Common Weakness Enumeration: [CWE-23](https://cwe.mitre.org/data/definitions/23.html).\n* Common Weakness Enumeration: [CWE-36](https://cwe.mitre.org/data/definitions/36.html).\n* Common Weakness Enumeration: [CWE-73](https://cwe.mitre.org/data/definitions/73.html).\n* Common Weakness Enumeration: [CWE-99](https://cwe.mitre.org/data/definitions/99.html).\n"},"id":"py/path-injection","name":"py/path-injection","properties":{"precision":"high","queryURI":"https://github.com/github/codeql/blob/39a67b6e2e6490a9bd010db50e148f647765e9f7/python/ql/src/Security/CWE-022/PathInjection.ql","security-severity":"7.5","tags":["correctness","external/cwe/cwe-022","external/cwe/cwe-023","external/cwe/cwe-036","external/cwe/cwe-073","external/cwe/cwe-099","security"]},"shortDescription":{"text":"Uncontrolled data used in path expression"}},{"defaultConfiguration":{"level":"warning"},"fullDescription":{"text":"A regular expression that can require polynomial time to match may be vulnerable to denial-of-service attacks."},"help":{"markdown":"# Polynomial regular expression used on uncontrolled data\nSome regular expressions take a long time to match certain input strings to the point where the time it takes to match a string of length *n* is proportional to *n\u003csup\u003ek\u003c/sup\u003e* or even *2\u003csup\u003en\u003c/sup\u003e*. Such regular expressions can negatively affect performance, or even allow a malicious user to perform a Denial of Service (\"DoS\") attack by crafting an expensive input string for the regular expression to match.\n\nThe regular expression engine provided by Python uses a backtracking non-deterministic finite automata to implement regular expression matching. While this approach is space-efficient and allows supporting advanced features like capture groups, it is not time-efficient in general. The worst-case time complexity of such an automaton can be polynomial or even exponential, meaning that for strings of a certain shape, increasing the input length by ten characters may make the automaton about 1000 times slower.\n\nTypically, a regular expression is affected by this problem if it contains a repetition of the form `r*` or `r+` where the sub-expression `r` is ambiguous in the sense that it can match some string in multiple ways. More information about the precise circumstances can be found in the references.\n\n\n## Recommendation\nModify the regular expression to remove the ambiguity, or ensure that the strings matched with the regular expression are short enough that the time-complexity does not matter.\n\n\n## Example\nConsider this use of a regular expression, which removes all leading and trailing whitespace in a string:\n\n```python\n\nre.sub(r\"^\\s+|\\s+$\", \"\", text) # BAD\n```\nThe sub-expression `\"\\s+$\"` will match the whitespace characters in `text` from left to right, but it can start matching anywhere within a whitespace sequence. This is problematic for strings that do **not** end with a whitespace character. Such a string will force the regular expression engine to process each whitespace sequence once per whitespace character in the sequence.\n\nThis ultimately means that the time cost of trimming a string is quadratic in the length of the string. So a string like `\"a b\"` will take milliseconds to process, but a similar string with a million spaces instead of just one will take several minutes.\n\nAvoid this problem by rewriting the regular expression to not contain the ambiguity about when to start matching whitespace sequences. For instance, by using a negative look-behind (`^\\s+|(?\u003c!\\s)\\s+$`), or just by using the built-in strip method (`text.strip()`).\n\nNote that the sub-expression `\"^\\s+\"` is **not** problematic as the `^` anchor restricts when that sub-expression can start matching, and as the regular expression engine matches from left to right.\n\n\n## Example\nAs a similar, but slightly subtler problem, consider the regular expression that matches lines with numbers, possibly written using scientific notation:\n\n```python\n\n^0\\.\\d+E?\\d+$ # BAD\n```\nThe problem with this regular expression is in the sub-expression `\\d+E?\\d+` because the second `\\d+` can start matching digits anywhere after the first match of the first `\\d+` if there is no `E` in the input string.\n\nThis is problematic for strings that do **not** end with a digit. Such a string will force the regular expression engine to process each digit sequence once per digit in the sequence, again leading to a quadratic time complexity.\n\nTo make the processing faster, the regular expression should be rewritten such that the two `\\d+` sub-expressions do not have overlapping matches: `^0\\.\\d+(E\\d+)?$`.\n\n\n## Example\nSometimes it is unclear how a regular expression can be rewritten to avoid the problem. In such cases, it often suffices to limit the length of the input string. For instance, the following regular expression is used to match numbers, and on some non-number inputs it can have quadratic time complexity:\n\n```python\n\nmatch = re.search(r'^(\\+|-)?(\\d+|(\\d*\\.\\d*))?(E|e)?([-+])?(\\d+)?$', str) \n```\nIt is not immediately obvious how to rewrite this regular expression to avoid the problem. However, you can mitigate performance issues by limiting the length to 1000 characters, which will always finish in a reasonable amount of time.\n\n```python\n\nif len(str) \u003e 1000:\n raise ValueError(\"Input too long\")\n\nmatch = re.search(r'^(\\+|-)?(\\d+|(\\d*\\.\\d*))?(E|e)?([-+])?(\\d+)?$', str) \n```\n\n## References\n* OWASP: [Regular expression Denial of Service - ReDoS](https://www.owasp.org/index.php/Regular_expression_Denial_of_Service_-_ReDoS).\n* Wikipedia: [ReDoS](https://en.wikipedia.org/wiki/ReDoS).\n* Wikipedia: [Time complexity](https://en.wikipedia.org/wiki/Time_complexity).\n* James Kirrage, Asiri Rathnayake, Hayo Thielecke: [Static Analysis for Regular Expression Denial-of-Service Attack](https://arxiv.org/abs/1301.0849).\n* Common Weakness Enumeration: [CWE-1333](https://cwe.mitre.org/data/definitions/1333.html).\n* Common Weakness Enumeration: [CWE-730](https://cwe.mitre.org/data/definitions/730.html).\n* Common Weakness Enumeration: [CWE-400](https://cwe.mitre.org/data/definitions/400.html).\n","text":"# Polynomial regular expression used on uncontrolled data\nSome regular expressions take a long time to match certain input strings to the point where the time it takes to match a string of length *n* is proportional to *n\u003csup\u003ek\u003c/sup\u003e* or even *2\u003csup\u003en\u003c/sup\u003e*. Such regular expressions can negatively affect performance, or even allow a malicious user to perform a Denial of Service (\"DoS\") attack by crafting an expensive input string for the regular expression to match.\n\nThe regular expression engine provided by Python uses a backtracking non-deterministic finite automata to implement regular expression matching. While this approach is space-efficient and allows supporting advanced features like capture groups, it is not time-efficient in general. The worst-case time complexity of such an automaton can be polynomial or even exponential, meaning that for strings of a certain shape, increasing the input length by ten characters may make the automaton about 1000 times slower.\n\nTypically, a regular expression is affected by this problem if it contains a repetition of the form `r*` or `r+` where the sub-expression `r` is ambiguous in the sense that it can match some string in multiple ways. More information about the precise circumstances can be found in the references.\n\n\n## Recommendation\nModify the regular expression to remove the ambiguity, or ensure that the strings matched with the regular expression are short enough that the time-complexity does not matter.\n\n\n## Example\nConsider this use of a regular expression, which removes all leading and trailing whitespace in a string:\n\n```python\n\nre.sub(r\"^\\s+|\\s+$\", \"\", text) # BAD\n```\nThe sub-expression `\"\\s+$\"` will match the whitespace characters in `text` from left to right, but it can start matching anywhere within a whitespace sequence. This is problematic for strings that do **not** end with a whitespace character. Such a string will force the regular expression engine to process each whitespace sequence once per whitespace character in the sequence.\n\nThis ultimately means that the time cost of trimming a string is quadratic in the length of the string. So a string like `\"a b\"` will take milliseconds to process, but a similar string with a million spaces instead of just one will take several minutes.\n\nAvoid this problem by rewriting the regular expression to not contain the ambiguity about when to start matching whitespace sequences. For instance, by using a negative look-behind (`^\\s+|(?\u003c!\\s)\\s+$`), or just by using the built-in strip method (`text.strip()`).\n\nNote that the sub-expression `\"^\\s+\"` is **not** problematic as the `^` anchor restricts when that sub-expression can start matching, and as the regular expression engine matches from left to right.\n\n\n## Example\nAs a similar, but slightly subtler problem, consider the regular expression that matches lines with numbers, possibly written using scientific notation:\n\n```python\n\n^0\\.\\d+E?\\d+$ # BAD\n```\nThe problem with this regular expression is in the sub-expression `\\d+E?\\d+` because the second `\\d+` can start matching digits anywhere after the first match of the first `\\d+` if there is no `E` in the input string.\n\nThis is problematic for strings that do **not** end with a digit. Such a string will force the regular expression engine to process each digit sequence once per digit in the sequence, again leading to a quadratic time complexity.\n\nTo make the processing faster, the regular expression should be rewritten such that the two `\\d+` sub-expressions do not have overlapping matches: `^0\\.\\d+(E\\d+)?$`.\n\n\n## Example\nSometimes it is unclear how a regular expression can be rewritten to avoid the problem. In such cases, it often suffices to limit the length of the input string. For instance, the following regular expression is used to match numbers, and on some non-number inputs it can have quadratic time complexity:\n\n```python\n\nmatch = re.search(r'^(\\+|-)?(\\d+|(\\d*\\.\\d*))?(E|e)?([-+])?(\\d+)?$', str) \n```\nIt is not immediately obvious how to rewrite this regular expression to avoid the problem. However, you can mitigate performance issues by limiting the length to 1000 characters, which will always finish in a reasonable amount of time.\n\n```python\n\nif len(str) \u003e 1000:\n raise ValueError(\"Input too long\")\n\nmatch = re.search(r'^(\\+|-)?(\\d+|(\\d*\\.\\d*))?(E|e)?([-+])?(\\d+)?$', str) \n```\n\n## References\n* OWASP: [Regular expression Denial of Service - ReDoS](https://www.owasp.org/index.php/Regular_expression_Denial_of_Service_-_ReDoS).\n* Wikipedia: [ReDoS](https://en.wikipedia.org/wiki/ReDoS).\n* Wikipedia: [Time complexity](https://en.wikipedia.org/wiki/Time_complexity).\n* James Kirrage, Asiri Rathnayake, Hayo Thielecke: [Static Analysis for Regular Expression Denial-of-Service Attack](https://arxiv.org/abs/1301.0849).\n* Common Weakness Enumeration: [CWE-1333](https://cwe.mitre.org/data/definitions/1333.html).\n* Common Weakness Enumeration: [CWE-730](https://cwe.mitre.org/data/definitions/730.html).\n* Common Weakness Enumeration: [CWE-400](https://cwe.mitre.org/data/definitions/400.html).\n"},"id":"py/polynomial-redos","name":"py/polynomial-redos","properties":{"precision":"high","queryURI":"https://github.com/github/codeql/blob/39a67b6e2e6490a9bd010db50e148f647765e9f7/python/ql/src/Security/CWE-730/PolynomialReDoS.ql","security-severity":"7.5","tags":["external/cwe/cwe-1333","external/cwe/cwe-400","external/cwe/cwe-730","security"]},"shortDescription":{"text":"Polynomial regular expression used on uncontrolled data"}},{"defaultConfiguration":{"level":"error"},"fullDescription":{"text":"A regular expression that requires exponential time to match certain inputs can be a performance bottleneck, and may be vulnerable to denial-of-service attacks."},"help":{"markdown":"# Inefficient regular expression\nSome regular expressions take a long time to match certain input strings to the point where the time it takes to match a string of length *n* is proportional to *n\u003csup\u003ek\u003c/sup\u003e* or even *2\u003csup\u003en\u003c/sup\u003e*. Such regular expressions can negatively affect performance, or even allow a malicious user to perform a Denial of Service (\"DoS\") attack by crafting an expensive input string for the regular expression to match.\n\nThe regular expression engine provided by Python uses a backtracking non-deterministic finite automata to implement regular expression matching. While this approach is space-efficient and allows supporting advanced features like capture groups, it is not time-efficient in general. The worst-case time complexity of such an automaton can be polynomial or even exponential, meaning that for strings of a certain shape, increasing the input length by ten characters may make the automaton about 1000 times slower.\n\nTypically, a regular expression is affected by this problem if it contains a repetition of the form `r*` or `r+` where the sub-expression `r` is ambiguous in the sense that it can match some string in multiple ways. More information about the precise circumstances can be found in the references.\n\n\n## Recommendation\nModify the regular expression to remove the ambiguity, or ensure that the strings matched with the regular expression are short enough that the time-complexity does not matter.\n\n\n## Example\nConsider this regular expression:\n\n```python\n\n^_(__|.)+_$\n```\nIts sub-expression `\"(__|.)+?\"` can match the string `\"__\"` either by the first alternative `\"__\"` to the left of the `\"|\"` operator, or by two repetitions of the second alternative `\".\"` to the right. Thus, a string consisting of an odd number of underscores followed by some other character will cause the regular expression engine to run for an exponential amount of time before rejecting the input.\n\nThis problem can be avoided by rewriting the regular expression to remove the ambiguity between the two branches of the alternative inside the repetition:\n\n```python\n\n^_(__|[^_])+_$\n```\n\n## References\n* OWASP: [Regular expression Denial of Service - ReDoS](https://www.owasp.org/index.php/Regular_expression_Denial_of_Service_-_ReDoS).\n* Wikipedia: [ReDoS](https://en.wikipedia.org/wiki/ReDoS).\n* Wikipedia: [Time complexity](https://en.wikipedia.org/wiki/Time_complexity).\n* James Kirrage, Asiri Rathnayake, Hayo Thielecke: [Static Analysis for Regular Expression Denial-of-Service Attack](https://arxiv.org/abs/1301.0849).\n* Common Weakness Enumeration: [CWE-1333](https://cwe.mitre.org/data/definitions/1333.html).\n* Common Weakness Enumeration: [CWE-730](https://cwe.mitre.org/data/definitions/730.html).\n* Common Weakness Enumeration: [CWE-400](https://cwe.mitre.org/data/definitions/400.html).\n","text":"# Inefficient regular expression\nSome regular expressions take a long time to match certain input strings to the point where the time it takes to match a string of length *n* is proportional to *n\u003csup\u003ek\u003c/sup\u003e* or even *2\u003csup\u003en\u003c/sup\u003e*. Such regular expressions can negatively affect performance, or even allow a malicious user to perform a Denial of Service (\"DoS\") attack by crafting an expensive input string for the regular expression to match.\n\nThe regular expression engine provided by Python uses a backtracking non-deterministic finite automata to implement regular expression matching. While this approach is space-efficient and allows supporting advanced features like capture groups, it is not time-efficient in general. The worst-case time complexity of such an automaton can be polynomial or even exponential, meaning that for strings of a certain shape, increasing the input length by ten characters may make the automaton about 1000 times slower.\n\nTypically, a regular expression is affected by this problem if it contains a repetition of the form `r*` or `r+` where the sub-expression `r` is ambiguous in the sense that it can match some string in multiple ways. More information about the precise circumstances can be found in the references.\n\n\n## Recommendation\nModify the regular expression to remove the ambiguity, or ensure that the strings matched with the regular expression are short enough that the time-complexity does not matter.\n\n\n## Example\nConsider this regular expression:\n\n```python\n\n^_(__|.)+_$\n```\nIts sub-expression `\"(__|.)+?\"` can match the string `\"__\"` either by the first alternative `\"__\"` to the left of the `\"|\"` operator, or by two repetitions of the second alternative `\".\"` to the right. Thus, a string consisting of an odd number of underscores followed by some other character will cause the regular expression engine to run for an exponential amount of time before rejecting the input.\n\nThis problem can be avoided by rewriting the regular expression to remove the ambiguity between the two branches of the alternative inside the repetition:\n\n```python\n\n^_(__|[^_])+_$\n```\n\n## References\n* OWASP: [Regular expression Denial of Service - ReDoS](https://www.owasp.org/index.php/Regular_expression_Denial_of_Service_-_ReDoS).\n* Wikipedia: [ReDoS](https://en.wikipedia.org/wiki/ReDoS).\n* Wikipedia: [Time complexity](https://en.wikipedia.org/wiki/Time_complexity).\n* James Kirrage, Asiri Rathnayake, Hayo Thielecke: [Static Analysis for Regular Expression Denial-of-Service Attack](https://arxiv.org/abs/1301.0849).\n* Common Weakness Enumeration: [CWE-1333](https://cwe.mitre.org/data/definitions/1333.html).\n* Common Weakness Enumeration: [CWE-730](https://cwe.mitre.org/data/definitions/730.html).\n* Common Weakness Enumeration: [CWE-400](https://cwe.mitre.org/data/definitions/400.html).\n"},"id":"py/redos","name":"py/redos","properties":{"precision":"high","queryURI":"https://github.com/github/codeql/blob/39a67b6e2e6490a9bd010db50e148f647765e9f7/python/ql/src/Security/CWE-730/ReDoS.ql","security-severity":"7.5","tags":["external/cwe/cwe-1333","external/cwe/cwe-400","external/cwe/cwe-730","security"]},"shortDescription":{"text":"Inefficient regular expression"}},{"defaultConfiguration":{"level":"error"},"fullDescription":{"text":"Writing user input directly to a web page allows for a cross-site scripting vulnerability."},"help":{"markdown":"# Reflected server-side cross-site scripting\nDirectly writing user input (for example, an HTTP request parameter) to a webpage without properly sanitizing the input first, allows for a cross-site scripting vulnerability.\n\n\n## Recommendation\nTo guard against cross-site scripting, consider escaping the input before writing user input to the page. The standard library provides escaping functions: `html.escape()` for Python 3.2 upwards or `cgi.escape()` older versions of Python. Most frameworks also provide their own escaping functions, for example `flask.escape()`.\n\n\n## Example\nThe following example is a minimal flask app which shows a safe and unsafe way to render the given name back to the page. The first view is unsafe as `first_name` is not escaped, leaving the page vulnerable to cross-site scripting attacks. The second view is safe as `first_name` is escaped, so it is not vulnerable to cross-site scripting attacks.\n\n\n```python\nfrom flask import Flask, request, make_response, escape\n\napp = Flask(__name__)\n\n@app.route('/unsafe')\ndef unsafe():\n first_name = request.args.get('name', '')\n return make_response(\"Your name is \" + first_name)\n\n@app.route('/safe')\ndef safe():\n first_name = request.args.get('name', '')\n return make_response(\"Your name is \" + escape(first_name))\n\n```\n\n## References\n* OWASP: [XSS (Cross Site Scripting) Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html).\n* Wikipedia: [Cross-site scripting](http://en.wikipedia.org/wiki/Cross-site_scripting).\n* Python Library Reference: [html.escape()](https://docs.python.org/3/library/html.html#html.escape).\n* Common Weakness Enumeration: [CWE-79](https://cwe.mitre.org/data/definitions/79.html).\n* Common Weakness Enumeration: [CWE-116](https://cwe.mitre.org/data/definitions/116.html).\n","text":"# Reflected server-side cross-site scripting\nDirectly writing user input (for example, an HTTP request parameter) to a webpage without properly sanitizing the input first, allows for a cross-site scripting vulnerability.\n\n\n## Recommendation\nTo guard against cross-site scripting, consider escaping the input before writing user input to the page. The standard library provides escaping functions: `html.escape()` for Python 3.2 upwards or `cgi.escape()` older versions of Python. Most frameworks also provide their own escaping functions, for example `flask.escape()`.\n\n\n## Example\nThe following example is a minimal flask app which shows a safe and unsafe way to render the given name back to the page. The first view is unsafe as `first_name` is not escaped, leaving the page vulnerable to cross-site scripting attacks. The second view is safe as `first_name` is escaped, so it is not vulnerable to cross-site scripting attacks.\n\n\n```python\nfrom flask import Flask, request, make_response, escape\n\napp = Flask(__name__)\n\n@app.route('/unsafe')\ndef unsafe():\n first_name = request.args.get('name', '')\n return make_response(\"Your name is \" + first_name)\n\n@app.route('/safe')\ndef safe():\n first_name = request.args.get('name', '')\n return make_response(\"Your name is \" + escape(first_name))\n\n```\n\n## References\n* OWASP: [XSS (Cross Site Scripting) Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html).\n* Wikipedia: [Cross-site scripting](http://en.wikipedia.org/wiki/Cross-site_scripting).\n* Python Library Reference: [html.escape()](https://docs.python.org/3/library/html.html#html.escape).\n* Common Weakness Enumeration: [CWE-79](https://cwe.mitre.org/data/definitions/79.html).\n* Common Weakness Enumeration: [CWE-116](https://cwe.mitre.org/data/definitions/116.html).\n"},"id":"py/reflective-xss","name":"py/reflective-xss","properties":{"precision":"high","queryURI":"https://github.com/github/codeql/blob/39a67b6e2e6490a9bd010db50e148f647765e9f7/python/ql/src/Security/CWE-079/ReflectedXss.ql","security-severity":"6.1","tags":["external/cwe/cwe-079","external/cwe/cwe-116","security"]},"shortDescription":{"text":"Reflected server-side cross-site scripting"}},{"defaultConfiguration":{"level":"error"},"fullDescription":{"text":"User input should not be used in regular expressions without first being escaped, otherwise a malicious user may be able to inject an expression that could require exponential time on certain inputs."},"help":{"markdown":"# Regular expression injection\nConstructing a regular expression with unsanitized user input is dangerous as a malicious user may be able to modify the meaning of the expression. In particular, such a user may be able to provide a regular expression fragment that takes exponential time in the worst case, and use that to perform a Denial of Service attack.\n\n\n## Recommendation\nBefore embedding user input into a regular expression, use a sanitization function such as `re.escape` to escape meta-characters that have a special meaning regarding regular expressions' syntax.\n\n\n## Example\nThe following examples are based on a simple Flask web server environment.\n\nThe following example shows a HTTP request parameter that is used to construct a regular expression without sanitizing it first:\n\n\n```python\nfrom flask import request, Flask\nimport re\n\n\n@app.route(\"/direct\")\ndef direct():\n unsafe_pattern = request.args[\"pattern\"]\n re.search(unsafe_pattern, \"\")\n\n\n@app.route(\"/compile\")\ndef compile():\n unsafe_pattern = request.args[\"pattern\"]\n compiled_pattern = re.compile(unsafe_pattern)\n compiled_pattern.search(\"\")\n\n```\nInstead, the request parameter should be sanitized first, for example using the function `re.escape`. This ensures that the user cannot insert characters which have a special meaning in regular expressions.\n\n\n```python\nfrom flask import request, Flask\nimport re\n\n\n@app.route(\"/direct\")\ndef direct():\n unsafe_pattern = request.args['pattern']\n safe_pattern = re.escape(unsafe_pattern)\n re.search(safe_pattern, \"\")\n\n\n@app.route(\"/compile\")\ndef compile():\n unsafe_pattern = request.args['pattern']\n safe_pattern = re.escape(unsafe_pattern)\n compiled_pattern = re.compile(safe_pattern)\n compiled_pattern.search(\"\")\n\n```\n\n## References\n* OWASP: [Regular expression Denial of Service - ReDoS](https://www.owasp.org/index.php/Regular_expression_Denial_of_Service_-_ReDoS).\n* Wikipedia: [ReDoS](https://en.wikipedia.org/wiki/ReDoS).\n* Python docs: [re](https://docs.python.org/3/library/re.html).\n* SonarSource: [RSPEC-2631](https://rules.sonarsource.com/python/type/Vulnerability/RSPEC-2631).\n* Common Weakness Enumeration: [CWE-730](https://cwe.mitre.org/data/definitions/730.html).\n* Common Weakness Enumeration: [CWE-400](https://cwe.mitre.org/data/definitions/400.html).\n","text":"# Regular expression injection\nConstructing a regular expression with unsanitized user input is dangerous as a malicious user may be able to modify the meaning of the expression. In particular, such a user may be able to provide a regular expression fragment that takes exponential time in the worst case, and use that to perform a Denial of Service attack.\n\n\n## Recommendation\nBefore embedding user input into a regular expression, use a sanitization function such as `re.escape` to escape meta-characters that have a special meaning regarding regular expressions' syntax.\n\n\n## Example\nThe following examples are based on a simple Flask web server environment.\n\nThe following example shows a HTTP request parameter that is used to construct a regular expression without sanitizing it first:\n\n\n```python\nfrom flask import request, Flask\nimport re\n\n\n@app.route(\"/direct\")\ndef direct():\n unsafe_pattern = request.args[\"pattern\"]\n re.search(unsafe_pattern, \"\")\n\n\n@app.route(\"/compile\")\ndef compile():\n unsafe_pattern = request.args[\"pattern\"]\n compiled_pattern = re.compile(unsafe_pattern)\n compiled_pattern.search(\"\")\n\n```\nInstead, the request parameter should be sanitized first, for example using the function `re.escape`. This ensures that the user cannot insert characters which have a special meaning in regular expressions.\n\n\n```python\nfrom flask import request, Flask\nimport re\n\n\n@app.route(\"/direct\")\ndef direct():\n unsafe_pattern = request.args['pattern']\n safe_pattern = re.escape(unsafe_pattern)\n re.search(safe_pattern, \"\")\n\n\n@app.route(\"/compile\")\ndef compile():\n unsafe_pattern = request.args['pattern']\n safe_pattern = re.escape(unsafe_pattern)\n compiled_pattern = re.compile(safe_pattern)\n compiled_pattern.search(\"\")\n\n```\n\n## References\n* OWASP: [Regular expression Denial of Service - ReDoS](https://www.owasp.org/index.php/Regular_expression_Denial_of_Service_-_ReDoS).\n* Wikipedia: [ReDoS](https://en.wikipedia.org/wiki/ReDoS).\n* Python docs: [re](https://docs.python.org/3/library/re.html).\n* SonarSource: [RSPEC-2631](https://rules.sonarsource.com/python/type/Vulnerability/RSPEC-2631).\n* Common Weakness Enumeration: [CWE-730](https://cwe.mitre.org/data/definitions/730.html).\n* Common Weakness Enumeration: [CWE-400](https://cwe.mitre.org/data/definitions/400.html).\n"},"id":"py/regex-injection","name":"py/regex-injection","properties":{"precision":"high","queryURI":"https://github.com/github/codeql/blob/39a67b6e2e6490a9bd010db50e148f647765e9f7/python/ql/src/Security/CWE-730/RegexInjection.ql","security-severity":"7.5","tags":["external/cwe/cwe-400","external/cwe/cwe-730","security"]},"shortDescription":{"text":"Regular expression injection"}},{"defaultConfiguration":{"level":"error"},"fullDescription":{"text":"Building a SQL query from user-controlled sources is vulnerable to insertion of malicious SQL code by the user."},"help":{"markdown":"# SQL query built from user-controlled sources\nIf a database query (such as a SQL or NoSQL query) is built from user-provided data without sufficient sanitization, a user may be able to run malicious database queries.\n\nThis also includes using the `TextClause` class in the `[SQLAlchemy](https://pypi.org/project/SQLAlchemy/)` PyPI package, which is used to represent a literal SQL fragment and is inserted directly into the final SQL when used in a query built using the ORM.\n\n\n## Recommendation\nMost database connector libraries offer a way of safely embedding untrusted data into a query by means of query parameters or prepared statements.\n\n\n## Example\nIn the following snippet, a user is fetched from the database using three different queries.\n\nIn the first case, the query string is built by directly using string formatting from a user-supplied request parameter. The parameter may include quote characters, so this code is vulnerable to a SQL injection attack.\n\nIn the second case, the user-supplied request attribute is passed to the database using query parameters. The database connector library will take care of escaping and inserting quotes as needed.\n\nIn the third case, the placeholder in the SQL string has been manually quoted. Since most databaseconnector libraries will insert their own quotes, doing so yourself will make the code vulnerable to SQL injection attacks. In this example, if `username` was `; DROP ALL TABLES -- `, the final SQL query would be `SELECT * FROM users WHERE username = ''; DROP ALL TABLES -- ''`\n\n\n```python\nfrom django.conf.urls import url\nfrom django.db import connection\n\n\ndef show_user(request, username):\n with connection.cursor() as cursor:\n # BAD -- Using string formatting\n cursor.execute(\"SELECT * FROM users WHERE username = '%s'\" % username)\n user = cursor.fetchone()\n\n # GOOD -- Using parameters\n cursor.execute(\"SELECT * FROM users WHERE username = %s\", username)\n user = cursor.fetchone()\n\n # BAD -- Manually quoting placeholder (%s)\n cursor.execute(\"SELECT * FROM users WHERE username = '%s'\", username)\n user = cursor.fetchone()\n\nurlpatterns = [url(r'^users/(?P\u003cusername\u003e[^/]+)$', show_user)]\n\n```\n\n## References\n* Wikipedia: [SQL injection](https://en.wikipedia.org/wiki/SQL_injection).\n* OWASP: [SQL Injection Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html).\n* [SQLAlchemy documentation for TextClause](https://docs.sqlalchemy.org/en/14/core/sqlelement.html#sqlalchemy.sql.expression.text.params.text).\n* Common Weakness Enumeration: [CWE-89](https://cwe.mitre.org/data/definitions/89.html).\n","text":"# SQL query built from user-controlled sources\nIf a database query (such as a SQL or NoSQL query) is built from user-provided data without sufficient sanitization, a user may be able to run malicious database queries.\n\nThis also includes using the `TextClause` class in the `[SQLAlchemy](https://pypi.org/project/SQLAlchemy/)` PyPI package, which is used to represent a literal SQL fragment and is inserted directly into the final SQL when used in a query built using the ORM.\n\n\n## Recommendation\nMost database connector libraries offer a way of safely embedding untrusted data into a query by means of query parameters or prepared statements.\n\n\n## Example\nIn the following snippet, a user is fetched from the database using three different queries.\n\nIn the first case, the query string is built by directly using string formatting from a user-supplied request parameter. The parameter may include quote characters, so this code is vulnerable to a SQL injection attack.\n\nIn the second case, the user-supplied request attribute is passed to the database using query parameters. The database connector library will take care of escaping and inserting quotes as needed.\n\nIn the third case, the placeholder in the SQL string has been manually quoted. Since most databaseconnector libraries will insert their own quotes, doing so yourself will make the code vulnerable to SQL injection attacks. In this example, if `username` was `; DROP ALL TABLES -- `, the final SQL query would be `SELECT * FROM users WHERE username = ''; DROP ALL TABLES -- ''`\n\n\n```python\nfrom django.conf.urls import url\nfrom django.db import connection\n\n\ndef show_user(request, username):\n with connection.cursor() as cursor:\n # BAD -- Using string formatting\n cursor.execute(\"SELECT * FROM users WHERE username = '%s'\" % username)\n user = cursor.fetchone()\n\n # GOOD -- Using parameters\n cursor.execute(\"SELECT * FROM users WHERE username = %s\", username)\n user = cursor.fetchone()\n\n # BAD -- Manually quoting placeholder (%s)\n cursor.execute(\"SELECT * FROM users WHERE username = '%s'\", username)\n user = cursor.fetchone()\n\nurlpatterns = [url(r'^users/(?P\u003cusername\u003e[^/]+)$', show_user)]\n\n```\n\n## References\n* Wikipedia: [SQL injection](https://en.wikipedia.org/wiki/SQL_injection).\n* OWASP: [SQL Injection Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html).\n* [SQLAlchemy documentation for TextClause](https://docs.sqlalchemy.org/en/14/core/sqlelement.html#sqlalchemy.sql.expression.text.params.text).\n* Common Weakness Enumeration: [CWE-89](https://cwe.mitre.org/data/definitions/89.html).\n"},"id":"py/sql-injection","name":"py/sql-injection","properties":{"precision":"high","queryURI":"https://github.com/github/codeql/blob/39a67b6e2e6490a9bd010db50e148f647765e9f7/python/ql/src/Security/CWE-089/SqlInjection.ql","security-severity":"8.8","tags":["external/cwe/cwe-089","security"]},"shortDescription":{"text":"SQL query built from user-controlled sources"}},{"defaultConfiguration":{"level":"error"},"fullDescription":{"text":"Leaking information about an exception, such as messages and stack traces, to an external user can expose implementation details that are useful to an attacker for developing a subsequent exploit."},"help":{"markdown":"# Information exposure through an exception\nSoftware developers often add stack traces to error messages, as a debugging aid. Whenever that error message occurs for an end user, the developer can use the stack trace to help identify how to fix the problem. In particular, stack traces can tell the developer more about the sequence of events that led to a failure, as opposed to merely the final state of the software when the error occurred.\n\nUnfortunately, the same information can be useful to an attacker. The sequence of class names in a stack trace can reveal the structure of the application as well as any internal components it relies on. Furthermore, the error message at the top of a stack trace can include information such as server-side file names and SQL code that the application relies on, allowing an attacker to fine-tune a subsequent injection attack.\n\n\n## Recommendation\nSend the user a more generic error message that reveals less information. Either suppress the stack trace entirely, or log it only on the server.\n\n\n## Example\nIn the following example, an exception is handled in two different ways. In the first version, labeled BAD, the exception is sent back to the remote user by returning it from the function. As such, the user is able to see a detailed stack trace, which may contain sensitive information. In the second version, the error message is logged only on the server, and a generic error message is displayed to the user. That way, the developers can still access and use the error log, but remote users will not see the information.\n\n\n```python\nfrom flask import Flask\napp = Flask(__name__)\n\n\nimport traceback\n\ndef do_computation():\n raise Exception(\"Secret info\")\n\n# BAD\n@app.route('/bad')\ndef server_bad():\n try:\n do_computation()\n except Exception as e:\n return traceback.format_exc()\n\n# GOOD\n@app.route('/good')\ndef server_good():\n try:\n do_computation()\n except Exception as e:\n log(traceback.format_exc())\n return \"An internal error has occurred!\"\n\n```\n\n## References\n* OWASP: [Improper Error Handling](https://owasp.org/www-community/Improper_Error_Handling).\n* Common Weakness Enumeration: [CWE-209](https://cwe.mitre.org/data/definitions/209.html).\n* Common Weakness Enumeration: [CWE-497](https://cwe.mitre.org/data/definitions/497.html).\n","text":"# Information exposure through an exception\nSoftware developers often add stack traces to error messages, as a debugging aid. Whenever that error message occurs for an end user, the developer can use the stack trace to help identify how to fix the problem. In particular, stack traces can tell the developer more about the sequence of events that led to a failure, as opposed to merely the final state of the software when the error occurred.\n\nUnfortunately, the same information can be useful to an attacker. The sequence of class names in a stack trace can reveal the structure of the application as well as any internal components it relies on. Furthermore, the error message at the top of a stack trace can include information such as server-side file names and SQL code that the application relies on, allowing an attacker to fine-tune a subsequent injection attack.\n\n\n## Recommendation\nSend the user a more generic error message that reveals less information. Either suppress the stack trace entirely, or log it only on the server.\n\n\n## Example\nIn the following example, an exception is handled in two different ways. In the first version, labeled BAD, the exception is sent back to the remote user by returning it from the function. As such, the user is able to see a detailed stack trace, which may contain sensitive information. In the second version, the error message is logged only on the server, and a generic error message is displayed to the user. That way, the developers can still access and use the error log, but remote users will not see the information.\n\n\n```python\nfrom flask import Flask\napp = Flask(__name__)\n\n\nimport traceback\n\ndef do_computation():\n raise Exception(\"Secret info\")\n\n# BAD\n@app.route('/bad')\ndef server_bad():\n try:\n do_computation()\n except Exception as e:\n return traceback.format_exc()\n\n# GOOD\n@app.route('/good')\ndef server_good():\n try:\n do_computation()\n except Exception as e:\n log(traceback.format_exc())\n return \"An internal error has occurred!\"\n\n```\n\n## References\n* OWASP: [Improper Error Handling](https://owasp.org/www-community/Improper_Error_Handling).\n* Common Weakness Enumeration: [CWE-209](https://cwe.mitre.org/data/definitions/209.html).\n* Common Weakness Enumeration: [CWE-497](https://cwe.mitre.org/data/definitions/497.html).\n"},"id":"py/stack-trace-exposure","name":"py/stack-trace-exposure","properties":{"precision":"high","queryURI":"https://github.com/github/codeql/blob/39a67b6e2e6490a9bd010db50e148f647765e9f7/python/ql/src/Security/CWE-209/StackTraceExposure.ql","security-severity":"5.4","tags":["external/cwe/cwe-209","external/cwe/cwe-497","security"]},"shortDescription":{"text":"Information exposure through an exception"}},{"defaultConfiguration":{"level":"warning"},"fullDescription":{"text":"The total number of lines of Python code across all files, including external libraries and auto-generated files. This is a useful metric of the size of a database. This query counts the lines of code, excluding whitespace or comments."},"id":"py/summary/lines-of-code","name":"py/summary/lines-of-code","properties":{"tags":["summary","telemetry"]},"shortDescription":{"text":"Total lines of Python code in the database"}},{"defaultConfiguration":{"level":"warning"},"fullDescription":{"text":"The total number of lines of Python code from the source code directory, excluding auto-generated files. This query counts the lines of code, excluding whitespace or comments. Note: If external libraries are included in the codebase either in a checked-in virtual environment or as vendored code, that will currently be counted as user written code."},"id":"py/summary/lines-of-user-code","name":"py/summary/lines-of-user-code","properties":{"tags":["debug","lines-of-code","summary"]},"shortDescription":{"text":"Total lines of user written Python code in the database"}},{"defaultConfiguration":{"level":"error"},"fullDescription":{"text":"Deserializing user-controlled data may allow attackers to execute arbitrary code."},"help":{"markdown":"# Deserialization of user-controlled data\nDeserializing untrusted data using any deserialization framework that allows the construction of arbitrary serializable objects is easily exploitable and in many cases allows an attacker to execute arbitrary code. Even before a deserialized object is returned to the caller of a deserialization method a lot of code may have been executed, including static initializers, constructors, and finalizers. Automatic deserialization of fields means that an attacker may craft a nested combination of objects on which the executed initialization code may have unforeseen effects, such as the execution of arbitrary code.\n\nThere are many different serialization frameworks. This query currently supports Pickle, Marshal and Yaml.\n\n\n## Recommendation\nAvoid deserialization of untrusted data if at all possible. If the architecture permits it then use other formats instead of serialized objects, for example JSON.\n\nIf you need to use YAML, use the `yaml.safe_load` function.\n\n\n## Example\nThe following example calls `pickle.loads` directly on a value provided by an incoming HTTP request. Pickle then creates a new value from untrusted data, and is therefore inherently unsafe.\n\n\n```python\n\nfrom django.conf.urls import url\nimport pickle\n\ndef unsafe(pickled):\n return pickle.loads(pickled)\n\nurlpatterns = [\n url(r'^(?P\u003cobject\u003e.*)$', unsafe)\n]\n```\nChanging the code to use `json.loads` instead of `pickle.loads` removes the vulnerability.\n\n\n```python\n\nfrom django.conf.urls import url\nimport json\n\ndef safe(pickled):\n return json.loads(pickled)\n\nurlpatterns = [\n url(r'^(?P\u003cobject\u003e.*)$', safe)\n]\n\n```\n\n## References\n* OWASP vulnerability description: [Deserialization of untrusted data](https://www.owasp.org/index.php/Deserialization_of_untrusted_data).\n* OWASP guidance on deserializing objects: [Deserialization Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html).\n* Talks by Chris Frohoff \u0026amp; Gabriel Lawrence: [ AppSecCali 2015: Marshalling Pickles - how deserializing objects will ruin your day](http://frohoff.github.io/appseccali-marshalling-pickles/)\n* Common Weakness Enumeration: [CWE-502](https://cwe.mitre.org/data/definitions/502.html).\n","text":"# Deserialization of user-controlled data\nDeserializing untrusted data using any deserialization framework that allows the construction of arbitrary serializable objects is easily exploitable and in many cases allows an attacker to execute arbitrary code. Even before a deserialized object is returned to the caller of a deserialization method a lot of code may have been executed, including static initializers, constructors, and finalizers. Automatic deserialization of fields means that an attacker may craft a nested combination of objects on which the executed initialization code may have unforeseen effects, such as the execution of arbitrary code.\n\nThere are many different serialization frameworks. This query currently supports Pickle, Marshal and Yaml.\n\n\n## Recommendation\nAvoid deserialization of untrusted data if at all possible. If the architecture permits it then use other formats instead of serialized objects, for example JSON.\n\nIf you need to use YAML, use the `yaml.safe_load` function.\n\n\n## Example\nThe following example calls `pickle.loads` directly on a value provided by an incoming HTTP request. Pickle then creates a new value from untrusted data, and is therefore inherently unsafe.\n\n\n```python\n\nfrom django.conf.urls import url\nimport pickle\n\ndef unsafe(pickled):\n return pickle.loads(pickled)\n\nurlpatterns = [\n url(r'^(?P\u003cobject\u003e.*)$', unsafe)\n]\n```\nChanging the code to use `json.loads` instead of `pickle.loads` removes the vulnerability.\n\n\n```python\n\nfrom django.conf.urls import url\nimport json\n\ndef safe(pickled):\n return json.loads(pickled)\n\nurlpatterns = [\n url(r'^(?P\u003cobject\u003e.*)$', safe)\n]\n\n```\n\n## References\n* OWASP vulnerability description: [Deserialization of untrusted data](https://www.owasp.org/index.php/Deserialization_of_untrusted_data).\n* OWASP guidance on deserializing objects: [Deserialization Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html).\n* Talks by Chris Frohoff \u0026amp; Gabriel Lawrence: [ AppSecCali 2015: Marshalling Pickles - how deserializing objects will ruin your day](http://frohoff.github.io/appseccali-marshalling-pickles/)\n* Common Weakness Enumeration: [CWE-502](https://cwe.mitre.org/data/definitions/502.html).\n"},"id":"py/unsafe-deserialization","name":"py/unsafe-deserialization","properties":{"precision":"high","queryURI":"https://github.com/github/codeql/blob/39a67b6e2e6490a9bd010db50e148f647765e9f7/python/ql/src/Security/CWE-502/UnsafeDeserialization.ql","security-severity":"9.8","tags":["external/cwe/cwe-502","security","serialization"]},"shortDescription":{"text":"Deserialization of user-controlled data"}},{"defaultConfiguration":{"level":"error"},"fullDescription":{"text":"URL redirection based on unvalidated user input may cause redirection to malicious web sites."},"help":{"markdown":"# URL redirection from remote source\nDirectly incorporating user input into a URL redirect request without validating the input can facilitate phishing attacks. In these attacks, unsuspecting users can be redirected to a malicious site that looks very similar to the real site they intend to visit, but which is controlled by the attacker.\n\n\n## Recommendation\nTo guard against untrusted URL redirection, it is advisable to avoid putting user input directly into a redirect URL. Instead, maintain a list of authorized redirects on the server; then choose from that list based on the user input provided.\n\nIf this is not possible, then the user input should be validated in some other way, for example, by verifying that the target URL does not include an explicit host name.\n\n\n## Example\nThe following example shows an HTTP request parameter being used directly in a URL redirect without validating the input, which facilitates phishing attacks:\n\n\n```python\nfrom flask import Flask, request, redirect\n\napp = Flask(__name__)\n\n@app.route('/')\ndef hello():\n target = request.args.get('target', '')\n return redirect(target, code=302)\n\n```\nIf you know the set of valid redirect targets, you can maintain a list of them on the server and check that the user input is in that list:\n\n\n```python\nfrom flask import Flask, request, redirect\n\nVALID_REDIRECT = \"http://cwe.mitre.org/data/definitions/601.html\"\n\napp = Flask(__name__)\n\n@app.route('/')\ndef hello():\n target = request.args.get('target', '')\n if target == VALID_REDIRECT:\n return redirect(target, code=302)\n else:\n # ignore the target and redirect to the home page\n return redirect('/', code=302)\n\n```\nOften this is not possible, so an alternative is to check that the target URL does not specify an explicit host name. For example, you can use the `urlparse` function from the Python standard library to parse the URL and check that the `netloc` attribute is empty.\n\nNote, however, that some cases are not handled as we desire out-of-the-box by `urlparse`, so we need to adjust two things, as shown in the example below:\n\n* Many browsers accept backslash characters (`\\`) as equivalent to forward slash characters (`/`) in URLs, but the `urlparse` function does not.\n* Mistyped URLs such as `https:/example.com` or `https:///example.com` are parsed as having an empty `netloc` attribute, while browsers will still redirect to the correct site.\n\n```python\nfrom flask import Flask, request, redirect\nfrom urllib.parse import urlparse\n\napp = Flask(__name__)\n\n@app.route('/')\ndef hello():\n target = request.args.get('target', '')\n target = target.replace('\\\\', '')\n if not urlparse(target).netloc and not urlparse(target).scheme:\n # relative path, safe to redirect\n return redirect(target, code=302)\n # ignore the target and redirect to the home page\n return redirect('/', code=302)\n\n```\nFor Django application, you can use the function `url_has_allowed_host_and_scheme` to check that a URL is safe to redirect to, as shown in the following example:\n\n\n```python\nfrom django.http import HttpResponseRedirect\nfrom django.shortcuts import redirect\nfrom django.utils.http import url_has_allowed_host_and_scheme\nfrom django.views import View\n\nclass RedirectView(View):\n def get(self, request, *args, **kwargs):\n target = request.GET.get('target', '')\n if url_has_allowed_host_and_scheme(target, allowed_hosts=None):\n return HttpResponseRedirect(target)\n else:\n # ignore the target and redirect to the home page\n return redirect('/')\n```\nNote that `url_has_allowed_host_and_scheme` handles backslashes correctly, so no additional processing is required.\n\n\n## References\n* OWASP: [ XSS Unvalidated Redirects and Forwards Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html).\n* Python standard library: [ urllib.parse](https://docs.python.org/3/library/urllib.parse.html).\n* Common Weakness Enumeration: [CWE-601](https://cwe.mitre.org/data/definitions/601.html).\n","text":"# URL redirection from remote source\nDirectly incorporating user input into a URL redirect request without validating the input can facilitate phishing attacks. In these attacks, unsuspecting users can be redirected to a malicious site that looks very similar to the real site they intend to visit, but which is controlled by the attacker.\n\n\n## Recommendation\nTo guard against untrusted URL redirection, it is advisable to avoid putting user input directly into a redirect URL. Instead, maintain a list of authorized redirects on the server; then choose from that list based on the user input provided.\n\nIf this is not possible, then the user input should be validated in some other way, for example, by verifying that the target URL does not include an explicit host name.\n\n\n## Example\nThe following example shows an HTTP request parameter being used directly in a URL redirect without validating the input, which facilitates phishing attacks:\n\n\n```python\nfrom flask import Flask, request, redirect\n\napp = Flask(__name__)\n\n@app.route('/')\ndef hello():\n target = request.args.get('target', '')\n return redirect(target, code=302)\n\n```\nIf you know the set of valid redirect targets, you can maintain a list of them on the server and check that the user input is in that list:\n\n\n```python\nfrom flask import Flask, request, redirect\n\nVALID_REDIRECT = \"http://cwe.mitre.org/data/definitions/601.html\"\n\napp = Flask(__name__)\n\n@app.route('/')\ndef hello():\n target = request.args.get('target', '')\n if target == VALID_REDIRECT:\n return redirect(target, code=302)\n else:\n # ignore the target and redirect to the home page\n return redirect('/', code=302)\n\n```\nOften this is not possible, so an alternative is to check that the target URL does not specify an explicit host name. For example, you can use the `urlparse` function from the Python standard library to parse the URL and check that the `netloc` attribute is empty.\n\nNote, however, that some cases are not handled as we desire out-of-the-box by `urlparse`, so we need to adjust two things, as shown in the example below:\n\n* Many browsers accept backslash characters (`\\`) as equivalent to forward slash characters (`/`) in URLs, but the `urlparse` function does not.\n* Mistyped URLs such as `https:/example.com` or `https:///example.com` are parsed as having an empty `netloc` attribute, while browsers will still redirect to the correct site.\n\n```python\nfrom flask import Flask, request, redirect\nfrom urllib.parse import urlparse\n\napp = Flask(__name__)\n\n@app.route('/')\ndef hello():\n target = request.args.get('target', '')\n target = target.replace('\\\\', '')\n if not urlparse(target).netloc and not urlparse(target).scheme:\n # relative path, safe to redirect\n return redirect(target, code=302)\n # ignore the target and redirect to the home page\n return redirect('/', code=302)\n\n```\nFor Django application, you can use the function `url_has_allowed_host_and_scheme` to check that a URL is safe to redirect to, as shown in the following example:\n\n\n```python\nfrom django.http import HttpResponseRedirect\nfrom django.shortcuts import redirect\nfrom django.utils.http import url_has_allowed_host_and_scheme\nfrom django.views import View\n\nclass RedirectView(View):\n def get(self, request, *args, **kwargs):\n target = request.GET.get('target', '')\n if url_has_allowed_host_and_scheme(target, allowed_hosts=None):\n return HttpResponseRedirect(target)\n else:\n # ignore the target and redirect to the home page\n return redirect('/')\n```\nNote that `url_has_allowed_host_and_scheme` handles backslashes correctly, so no additional processing is required.\n\n\n## References\n* OWASP: [ XSS Unvalidated Redirects and Forwards Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html).\n* Python standard library: [ urllib.parse](https://docs.python.org/3/library/urllib.parse.html).\n* Common Weakness Enumeration: [CWE-601](https://cwe.mitre.org/data/definitions/601.html).\n"},"id":"py/url-redirection","name":"py/url-redirection","properties":{"precision":"high","queryURI":"https://github.com/github/codeql/blob/39a67b6e2e6490a9bd010db50e148f647765e9f7/python/ql/src/Security/CWE-601/UrlRedirect.ql","security-severity":"6.1","tags":["external/cwe/cwe-601","security"]},"shortDescription":{"text":"URL redirection from remote source"}},{"defaultConfiguration":{"level":"error"},"fullDescription":{"text":"The built-in function 'input' is used which, in Python 2, can allow arbitrary code to be run."},"help":{"markdown":"# 'input' function used in Python 2\nIn Python 2, a call to the `input()` function, `input(prompt)` is equivalent to `eval(raw_input(prompt))`. Evaluating user input without any checking can be a serious security flaw.\n\n\n## Recommendation\nGet user input with `raw_input(prompt)` and then validate that input before evaluating. If the expected input is a number or string, then `ast.literal_eval()` can always be used safely.\n\n\n## References\n* Python Standard Library: [input](http://docs.python.org/2/library/functions.html#input), [ast.literal_eval](http://docs.python.org/2/library/ast.html#ast.literal_eval).\n* Wikipedia: [Data validation](http://en.wikipedia.org/wiki/Data_validation).\n","text":"# 'input' function used in Python 2\nIn Python 2, a call to the `input()` function, `input(prompt)` is equivalent to `eval(raw_input(prompt))`. Evaluating user input without any checking can be a serious security flaw.\n\n\n## Recommendation\nGet user input with `raw_input(prompt)` and then validate that input before evaluating. If the expected input is a number or string, then `ast.literal_eval()` can always be used safely.\n\n\n## References\n* Python Standard Library: [input](http://docs.python.org/2/library/functions.html#input), [ast.literal_eval](http://docs.python.org/2/library/ast.html#ast.literal_eval).\n* Wikipedia: [Data validation](http://en.wikipedia.org/wiki/Data_validation).\n"},"id":"py/use-of-input","name":"py/use-of-input","properties":{"precision":"high","queryURI":"https://github.com/github/codeql/blob/39a67b6e2e6490a9bd010db50e148f647765e9f7/python/ql/src/Expressions/UseofInput.ql","security-severity":"9.8","tags":["correctness","security","security/cwe/cwe-94","security/cwe/cwe-95"]},"shortDescription":{"text":"'input' function used in Python 2"}},{"defaultConfiguration":{"level":"error"},"fullDescription":{"text":"Use of a cryptographic key that is too small may allow the encryption to be broken."},"help":{"markdown":"# Use of weak cryptographic key\nModern encryption relies on it being computationally infeasible to break the cipher and decode a message without the key. As computational power increases, the ability to break ciphers grows and keys need to become larger.\n\nThe three main asymmetric key algorithms currently in use are Rivest–Shamir–Adleman (RSA) cryptography, Digital Signature Algorithm (DSA), and Elliptic-curve cryptography (ECC). With current technology, key sizes of 2048 bits for RSA and DSA, or 256 bits for ECC, are regarded as unbreakable.\n\n\n## Recommendation\nIncrease the key size to the recommended amount or larger. For RSA or DSA this is at least 2048 bits, for ECC this is at least 256 bits.\n\n\n## References\n* Wikipedia: [Digital Signature Algorithm](https://en.wikipedia.org/wiki/Digital_Signature_Algorithm).\n* Wikipedia: [RSA cryptosystem](https://en.wikipedia.org/wiki/RSA_(cryptosystem)).\n* Wikipedia: [Elliptic-curve cryptography](https://en.wikipedia.org/wiki/Elliptic-curve_cryptography).\n* Python cryptography module: [cryptography.io](https://cryptography.io/en/latest/).\n* NIST: [ Recommendation for Transitioning the Use of Cryptographic Algorithms and Key Lengths](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar1.pdf).\n* Common Weakness Enumeration: [CWE-326](https://cwe.mitre.org/data/definitions/326.html).\n","text":"# Use of weak cryptographic key\nModern encryption relies on it being computationally infeasible to break the cipher and decode a message without the key. As computational power increases, the ability to break ciphers grows and keys need to become larger.\n\nThe three main asymmetric key algorithms currently in use are Rivest–Shamir–Adleman (RSA) cryptography, Digital Signature Algorithm (DSA), and Elliptic-curve cryptography (ECC). With current technology, key sizes of 2048 bits for RSA and DSA, or 256 bits for ECC, are regarded as unbreakable.\n\n\n## Recommendation\nIncrease the key size to the recommended amount or larger. For RSA or DSA this is at least 2048 bits, for ECC this is at least 256 bits.\n\n\n## References\n* Wikipedia: [Digital Signature Algorithm](https://en.wikipedia.org/wiki/Digital_Signature_Algorithm).\n* Wikipedia: [RSA cryptosystem](https://en.wikipedia.org/wiki/RSA_(cryptosystem)).\n* Wikipedia: [Elliptic-curve cryptography](https://en.wikipedia.org/wiki/Elliptic-curve_cryptography).\n* Python cryptography module: [cryptography.io](https://cryptography.io/en/latest/).\n* NIST: [ Recommendation for Transitioning the Use of Cryptographic Algorithms and Key Lengths](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar1.pdf).\n* Common Weakness Enumeration: [CWE-326](https://cwe.mitre.org/data/definitions/326.html).\n"},"id":"py/weak-crypto-key","name":"py/weak-crypto-key","properties":{"precision":"high","queryURI":"https://github.com/github/codeql/blob/39a67b6e2e6490a9bd010db50e148f647765e9f7/python/ql/src/Security/CWE-326/WeakCryptoKey.ql","security-severity":"7.5","tags":["external/cwe/cwe-326","security"]},"shortDescription":{"text":"Use of weak cryptographic key"}},{"defaultConfiguration":{"level":"warning"},"fullDescription":{"text":"Using broken or weak cryptographic algorithms can compromise security."},"help":{"markdown":"# Use of a broken or weak cryptographic algorithm\nUsing broken or weak cryptographic algorithms can leave data vulnerable to being decrypted or forged by an attacker.\n\nMany cryptographic algorithms provided by cryptography libraries are known to be weak, or flawed. Using such an algorithm means that encrypted or hashed data is less secure than it appears to be.\n\nThis query alerts on any use of a weak cryptographic algorithm, that is not a hashing algorithm. Use of broken or weak cryptographic hash functions are handled by the `py/weak-sensitive-data-hashing` query.\n\n\n## Recommendation\nEnsure that you use a strong, modern cryptographic algorithm, such as AES-128 or RSA-2048.\n\n\n## Example\nThe following code uses the `pycryptodome` library to encrypt some secret data. When you create a cipher using `pycryptodome` you must specify the encryption algorithm to use. The first example uses DES, which is an older algorithm that is now considered weak. The second example uses AES, which is a stronger modern algorithm.\n\n\n```python\nfrom Crypto.Cipher import DES, AES\n\ncipher = DES.new(SECRET_KEY)\n\ndef send_encrypted(channel, message):\n channel.send(cipher.encrypt(message)) # BAD: weak encryption\n\n\ncipher = AES.new(SECRET_KEY)\n\ndef send_encrypted(channel, message):\n channel.send(cipher.encrypt(message)) # GOOD: strong encryption\n\n\n```\nNOTICE: the original `[pycrypto](https://pypi.org/project/pycrypto/)` PyPI package that provided the `Crypto` module is not longer actively maintained, so you should use the `[pycryptodome](https://pypi.org/project/pycryptodome/)` PyPI package instead (which has a compatible API).\n\n\n## References\n* NIST, FIPS 140 Annex a: [ Approved Security Functions](http://csrc.nist.gov/publications/fips/fips140-2/fips1402annexa.pdf).\n* NIST, SP 800-131A: [ Transitions: Recommendation for Transitioning the Use of Cryptographic Algorithms and Key Lengths](http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar1.pdf).\n* OWASP: [Rule - Use strong approved cryptographic algorithms](https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html#rule---use-strong-approved-authenticated-encryption).\n* Common Weakness Enumeration: [CWE-327](https://cwe.mitre.org/data/definitions/327.html).\n","text":"# Use of a broken or weak cryptographic algorithm\nUsing broken or weak cryptographic algorithms can leave data vulnerable to being decrypted or forged by an attacker.\n\nMany cryptographic algorithms provided by cryptography libraries are known to be weak, or flawed. Using such an algorithm means that encrypted or hashed data is less secure than it appears to be.\n\nThis query alerts on any use of a weak cryptographic algorithm, that is not a hashing algorithm. Use of broken or weak cryptographic hash functions are handled by the `py/weak-sensitive-data-hashing` query.\n\n\n## Recommendation\nEnsure that you use a strong, modern cryptographic algorithm, such as AES-128 or RSA-2048.\n\n\n## Example\nThe following code uses the `pycryptodome` library to encrypt some secret data. When you create a cipher using `pycryptodome` you must specify the encryption algorithm to use. The first example uses DES, which is an older algorithm that is now considered weak. The second example uses AES, which is a stronger modern algorithm.\n\n\n```python\nfrom Crypto.Cipher import DES, AES\n\ncipher = DES.new(SECRET_KEY)\n\ndef send_encrypted(channel, message):\n channel.send(cipher.encrypt(message)) # BAD: weak encryption\n\n\ncipher = AES.new(SECRET_KEY)\n\ndef send_encrypted(channel, message):\n channel.send(cipher.encrypt(message)) # GOOD: strong encryption\n\n\n```\nNOTICE: the original `[pycrypto](https://pypi.org/project/pycrypto/)` PyPI package that provided the `Crypto` module is not longer actively maintained, so you should use the `[pycryptodome](https://pypi.org/project/pycryptodome/)` PyPI package instead (which has a compatible API).\n\n\n## References\n* NIST, FIPS 140 Annex a: [ Approved Security Functions](http://csrc.nist.gov/publications/fips/fips140-2/fips1402annexa.pdf).\n* NIST, SP 800-131A: [ Transitions: Recommendation for Transitioning the Use of Cryptographic Algorithms and Key Lengths](http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar1.pdf).\n* OWASP: [Rule - Use strong approved cryptographic algorithms](https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html#rule---use-strong-approved-authenticated-encryption).\n* Common Weakness Enumeration: [CWE-327](https://cwe.mitre.org/data/definitions/327.html).\n"},"id":"py/weak-cryptographic-algorithm","name":"py/weak-cryptographic-algorithm","properties":{"precision":"high","queryURI":"https://github.com/github/codeql/blob/39a67b6e2e6490a9bd010db50e148f647765e9f7/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.ql","security-severity":"7.5","tags":["external/cwe/cwe-327","security"]},"shortDescription":{"text":"Use of a broken or weak cryptographic algorithm"}},{"defaultConfiguration":{"level":"warning"},"fullDescription":{"text":"Using broken or weak cryptographic hashing algorithms can compromise security."},"help":{"markdown":"# Use of a broken or weak cryptographic hashing algorithm on sensitive data\nUsing a broken or weak cryptographic hash function can leave data vulnerable, and should not be used in security related code.\n\nA strong cryptographic hash function should be resistant to:\n\n* pre-image attacks: if you know a hash value `h(x)`, you should not be able to easily find the input `x`.\n* collision attacks: if you know a hash value `h(x)`, you should not be able to easily find a different input `y` with the same hash value `h(x) = h(y)`.\nIn cases with a limited input space, such as for passwords, the hash function also needs to be computationally expensive to be resistant to brute-force attacks. Passwords should also have an unique salt applied before hashing, but that is not considered by this query.\n\nAs an example, both MD5 and SHA-1 are known to be vulnerable to collision attacks.\n\nSince it's OK to use a weak cryptographic hash function in a non-security context, this query only alerts when these are used to hash sensitive data (such as passwords, certificates, usernames).\n\nUse of broken or weak cryptographic algorithms that are not hashing algorithms, is handled by the `py/weak-cryptographic-algorithm` query.\n\n\n## Recommendation\nEnsure that you use a strong, modern cryptographic hash function:\n\n* such as Argon2, scrypt, bcrypt, or PBKDF2 for passwords and other data with limited input space.\n* such as SHA-2, or SHA-3 in other cases.\n\n## Example\nThe following example shows two functions for checking whether the hash of a certificate matches a known value -- to prevent tampering. The first function uses MD5 that is known to be vulnerable to collision attacks. The second function uses SHA-256 that is a strong cryptographic hashing function.\n\n\n```python\nimport hashlib\n\ndef certificate_matches_known_hash_bad(certificate, known_hash):\n hash = hashlib.md5(certificate).hexdigest() # BAD\n return hash == known_hash\n\ndef certificate_matches_known_hash_good(certificate, known_hash):\n hash = hashlib.sha256(certificate).hexdigest() # GOOD\n return hash == known_hash\n\n```\n\n## Example\nThe following example shows two functions for hashing passwords. The first function uses SHA-256 to hash passwords. Although SHA-256 is a strong cryptographic hash function, it is not suitable for password hashing since it is not computationally expensive.\n\n\n```python\nimport hashlib\n\ndef get_password_hash(password: str, salt: str):\n return hashlib.sha256(password + salt).hexdigest() # BAD\n\n```\nThe second function uses Argon2 (through the `argon2-cffi` PyPI package), which is a strong password hashing algorithm (and includes a per-password salt by default).\n\n\n```python\nfrom argon2 import PasswordHasher\n\ndef get_initial_hash(password: str):\n ph = PasswordHasher()\n return ph.hash(password) # GOOD\n\ndef check_password(password: str, known_hash):\n ph = PasswordHasher()\n return ph.verify(known_hash, password) # GOOD\n\n```\n\n## References\n* OWASP: [Password Storage Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html)\n* Common Weakness Enumeration: [CWE-327](https://cwe.mitre.org/data/definitions/327.html).\n* Common Weakness Enumeration: [CWE-328](https://cwe.mitre.org/data/definitions/328.html).\n* Common Weakness Enumeration: [CWE-916](https://cwe.mitre.org/data/definitions/916.html).\n","text":"# Use of a broken or weak cryptographic hashing algorithm on sensitive data\nUsing a broken or weak cryptographic hash function can leave data vulnerable, and should not be used in security related code.\n\nA strong cryptographic hash function should be resistant to:\n\n* pre-image attacks: if you know a hash value `h(x)`, you should not be able to easily find the input `x`.\n* collision attacks: if you know a hash value `h(x)`, you should not be able to easily find a different input `y` with the same hash value `h(x) = h(y)`.\nIn cases with a limited input space, such as for passwords, the hash function also needs to be computationally expensive to be resistant to brute-force attacks. Passwords should also have an unique salt applied before hashing, but that is not considered by this query.\n\nAs an example, both MD5 and SHA-1 are known to be vulnerable to collision attacks.\n\nSince it's OK to use a weak cryptographic hash function in a non-security context, this query only alerts when these are used to hash sensitive data (such as passwords, certificates, usernames).\n\nUse of broken or weak cryptographic algorithms that are not hashing algorithms, is handled by the `py/weak-cryptographic-algorithm` query.\n\n\n## Recommendation\nEnsure that you use a strong, modern cryptographic hash function:\n\n* such as Argon2, scrypt, bcrypt, or PBKDF2 for passwords and other data with limited input space.\n* such as SHA-2, or SHA-3 in other cases.\n\n## Example\nThe following example shows two functions for checking whether the hash of a certificate matches a known value -- to prevent tampering. The first function uses MD5 that is known to be vulnerable to collision attacks. The second function uses SHA-256 that is a strong cryptographic hashing function.\n\n\n```python\nimport hashlib\n\ndef certificate_matches_known_hash_bad(certificate, known_hash):\n hash = hashlib.md5(certificate).hexdigest() # BAD\n return hash == known_hash\n\ndef certificate_matches_known_hash_good(certificate, known_hash):\n hash = hashlib.sha256(certificate).hexdigest() # GOOD\n return hash == known_hash\n\n```\n\n## Example\nThe following example shows two functions for hashing passwords. The first function uses SHA-256 to hash passwords. Although SHA-256 is a strong cryptographic hash function, it is not suitable for password hashing since it is not computationally expensive.\n\n\n```python\nimport hashlib\n\ndef get_password_hash(password: str, salt: str):\n return hashlib.sha256(password + salt).hexdigest() # BAD\n\n```\nThe second function uses Argon2 (through the `argon2-cffi` PyPI package), which is a strong password hashing algorithm (and includes a per-password salt by default).\n\n\n```python\nfrom argon2 import PasswordHasher\n\ndef get_initial_hash(password: str):\n ph = PasswordHasher()\n return ph.hash(password) # GOOD\n\ndef check_password(password: str, known_hash):\n ph = PasswordHasher()\n return ph.verify(known_hash, password) # GOOD\n\n```\n\n## References\n* OWASP: [Password Storage Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html)\n* Common Weakness Enumeration: [CWE-327](https://cwe.mitre.org/data/definitions/327.html).\n* Common Weakness Enumeration: [CWE-328](https://cwe.mitre.org/data/definitions/328.html).\n* Common Weakness Enumeration: [CWE-916](https://cwe.mitre.org/data/definitions/916.html).\n"},"id":"py/weak-sensitive-data-hashing","name":"py/weak-sensitive-data-hashing","properties":{"precision":"high","queryURI":"https://github.com/github/codeql/blob/39a67b6e2e6490a9bd010db50e148f647765e9f7/python/ql/src/Security/CWE-327/WeakSensitiveDataHashing.ql","security-severity":"7.5","tags":["external/cwe/cwe-327","external/cwe/cwe-328","external/cwe/cwe-916","security"]},"shortDescription":{"text":"Use of a broken or weak cryptographic hashing algorithm on sensitive data"}},{"defaultConfiguration":{"level":"warning"},"fullDescription":{"text":"Parsing user input as an XML document with arbitrary internal entity expansion is vulnerable to denial-of-service attacks."},"help":{"markdown":"# XML internal entity expansion\nParsing untrusted XML files with a weakly configured XML parser may be vulnerable to denial-of-service (DoS) attacks exploiting uncontrolled internal entity expansion.\n\nIn XML, so-called *internal entities* are a mechanism for introducing an abbreviation for a piece of text or part of a document. When a parser that has been configured to expand entities encounters a reference to an internal entity, it replaces the entity by the data it represents. The replacement text may itself contain other entity references, which are expanded recursively. This means that entity expansion can increase document size dramatically.\n\nIf untrusted XML is parsed with entity expansion enabled, a malicious attacker could submit a document that contains very deeply nested entity definitions, causing the parser to take a very long time or use large amounts of memory. This is sometimes called an *XML bomb* attack.\n\n\n## Recommendation\nThe safest way to prevent XML bomb attacks is to disable entity expansion when parsing untrusted data. Whether this can be done depends on the library being used. Note that some libraries, such as `lxml`, have measures enabled by default to prevent such DoS XML attacks, so unless you have explicitly set `huge_tree` to `True`, no further action is needed.\n\nWe recommend using the [defusedxml](https://pypi.org/project/defusedxml/) PyPI package, which has been created to prevent XML attacks (both XXE and XML bombs).\n\n\n## Example\nThe following example uses the `xml.etree` XML parser provided by the Python standard library to parse a string `xml_src`. That string is from an untrusted source, so this code is vulnerable to a DoS attack, since the `xml.etree` XML parser expands internal entities by default:\n\n\n```python\nfrom flask import Flask, request\nimport xml.etree.ElementTree as ET\n\napp = Flask(__name__)\n\n@app.post(\"/upload\")\ndef upload():\n xml_src = request.get_data()\n doc = ET.fromstring(xml_src)\n return ET.tostring(doc)\n\n```\nIt is not possible to guard against internal entity expansion with `xml.etree`, so to guard against these attacks, the following example uses the [defusedxml](https://pypi.org/project/defusedxml/) PyPI package instead, which is not exposed to such internal entity expansion attacks.\n\n\n```python\nfrom flask import Flask, request\nimport defusedxml.ElementTree as ET\n\napp = Flask(__name__)\n\n@app.post(\"/upload\")\ndef upload():\n xml_src = request.get_data()\n doc = ET.fromstring(xml_src)\n return ET.tostring(doc)\n\n```\n\n## References\n* Wikipedia: [Billion Laughs](https://en.wikipedia.org/wiki/Billion_laughs).\n* Bryan Sullivan: [Security Briefs - XML Denial of Service Attacks and Defenses](https://msdn.microsoft.com/en-us/magazine/ee335713.aspx).\n* Python 3 standard library: [XML Vulnerabilities](https://docs.python.org/3/library/xml.html#xml-vulnerabilities).\n* Python 2 standard library: [XML Vulnerabilities](https://docs.python.org/2/library/xml.html#xml-vulnerabilities).\n* Common Weakness Enumeration: [CWE-776](https://cwe.mitre.org/data/definitions/776.html).\n* Common Weakness Enumeration: [CWE-400](https://cwe.mitre.org/data/definitions/400.html).\n","text":"# XML internal entity expansion\nParsing untrusted XML files with a weakly configured XML parser may be vulnerable to denial-of-service (DoS) attacks exploiting uncontrolled internal entity expansion.\n\nIn XML, so-called *internal entities* are a mechanism for introducing an abbreviation for a piece of text or part of a document. When a parser that has been configured to expand entities encounters a reference to an internal entity, it replaces the entity by the data it represents. The replacement text may itself contain other entity references, which are expanded recursively. This means that entity expansion can increase document size dramatically.\n\nIf untrusted XML is parsed with entity expansion enabled, a malicious attacker could submit a document that contains very deeply nested entity definitions, causing the parser to take a very long time or use large amounts of memory. This is sometimes called an *XML bomb* attack.\n\n\n## Recommendation\nThe safest way to prevent XML bomb attacks is to disable entity expansion when parsing untrusted data. Whether this can be done depends on the library being used. Note that some libraries, such as `lxml`, have measures enabled by default to prevent such DoS XML attacks, so unless you have explicitly set `huge_tree` to `True`, no further action is needed.\n\nWe recommend using the [defusedxml](https://pypi.org/project/defusedxml/) PyPI package, which has been created to prevent XML attacks (both XXE and XML bombs).\n\n\n## Example\nThe following example uses the `xml.etree` XML parser provided by the Python standard library to parse a string `xml_src`. That string is from an untrusted source, so this code is vulnerable to a DoS attack, since the `xml.etree` XML parser expands internal entities by default:\n\n\n```python\nfrom flask import Flask, request\nimport xml.etree.ElementTree as ET\n\napp = Flask(__name__)\n\n@app.post(\"/upload\")\ndef upload():\n xml_src = request.get_data()\n doc = ET.fromstring(xml_src)\n return ET.tostring(doc)\n\n```\nIt is not possible to guard against internal entity expansion with `xml.etree`, so to guard against these attacks, the following example uses the [defusedxml](https://pypi.org/project/defusedxml/) PyPI package instead, which is not exposed to such internal entity expansion attacks.\n\n\n```python\nfrom flask import Flask, request\nimport defusedxml.ElementTree as ET\n\napp = Flask(__name__)\n\n@app.post(\"/upload\")\ndef upload():\n xml_src = request.get_data()\n doc = ET.fromstring(xml_src)\n return ET.tostring(doc)\n\n```\n\n## References\n* Wikipedia: [Billion Laughs](https://en.wikipedia.org/wiki/Billion_laughs).\n* Bryan Sullivan: [Security Briefs - XML Denial of Service Attacks and Defenses](https://msdn.microsoft.com/en-us/magazine/ee335713.aspx).\n* Python 3 standard library: [XML Vulnerabilities](https://docs.python.org/3/library/xml.html#xml-vulnerabilities).\n* Python 2 standard library: [XML Vulnerabilities](https://docs.python.org/2/library/xml.html#xml-vulnerabilities).\n* Common Weakness Enumeration: [CWE-776](https://cwe.mitre.org/data/definitions/776.html).\n* Common Weakness Enumeration: [CWE-400](https://cwe.mitre.org/data/definitions/400.html).\n"},"id":"py/xml-bomb","name":"py/xml-bomb","properties":{"precision":"high","queryURI":"https://github.com/github/codeql/blob/39a67b6e2e6490a9bd010db50e148f647765e9f7/python/ql/src/Security/CWE-776/XmlBomb.ql","security-severity":"7.5","tags":["external/cwe/cwe-400","external/cwe/cwe-776","security"]},"shortDescription":{"text":"XML internal entity expansion"}},{"defaultConfiguration":{"level":"error"},"fullDescription":{"text":"Building a XPath query from user-controlled sources is vulnerable to insertion of malicious Xpath code by the user."},"help":{"markdown":"# XPath query built from user-controlled sources\nIf an XPath expression is built using string concatenation, and the components of the concatenation include user input, it makes it very easy for a user to create a malicious XPath expression.\n\n\n## Recommendation\nIf user input must be included in an XPath expression, either sanitize the data or use variable references to safely embed it without altering the structure of the expression.\n\n\n## Example\nIn the example below, the xpath query is controlled by the user and hence leads to a vulnerability.\n\n\n```python\nfrom lxml import etree\nfrom io import StringIO\n\nfrom django.urls import path\nfrom django.http import HttpResponse\nfrom django.template import Template, Context, Engine, engines\n\n\ndef a(request):\n value = request.GET['xpath']\n f = StringIO('\u003cfoo\u003e\u003cbar\u003e\u003c/bar\u003e\u003c/foo\u003e')\n tree = etree.parse(f)\n r = tree.xpath(\"/tag[@id='%s']\" % value)\n\n\nurlpatterns = [\n path('a', a)\n]\n\n```\nThis can be fixed by using a parameterized query as shown below.\n\n\n```python\nfrom lxml import etree\nfrom io import StringIO\n\nfrom django.urls import path\nfrom django.http import HttpResponse\nfrom django.template import Template, Context, Engine, engines\n\n\ndef a(request):\n value = request.GET['xpath']\n f = StringIO('\u003cfoo\u003e\u003cbar\u003e\u003c/bar\u003e\u003c/foo\u003e')\n tree = etree.parse(f)\n r = tree.xpath(\"/tag[@id=$tagid]\", tagid=value)\n\n\nurlpatterns = [\n path('a', a)\n]\n\n```\n\n## References\n* OWASP XPath injection : [](https://owasp.org/www-community/attacks/XPATH_Injection)/\u0026gt;\u0026gt;\n* Common Weakness Enumeration: [CWE-643](https://cwe.mitre.org/data/definitions/643.html).\n","text":"# XPath query built from user-controlled sources\nIf an XPath expression is built using string concatenation, and the components of the concatenation include user input, it makes it very easy for a user to create a malicious XPath expression.\n\n\n## Recommendation\nIf user input must be included in an XPath expression, either sanitize the data or use variable references to safely embed it without altering the structure of the expression.\n\n\n## Example\nIn the example below, the xpath query is controlled by the user and hence leads to a vulnerability.\n\n\n```python\nfrom lxml import etree\nfrom io import StringIO\n\nfrom django.urls import path\nfrom django.http import HttpResponse\nfrom django.template import Template, Context, Engine, engines\n\n\ndef a(request):\n value = request.GET['xpath']\n f = StringIO('\u003cfoo\u003e\u003cbar\u003e\u003c/bar\u003e\u003c/foo\u003e')\n tree = etree.parse(f)\n r = tree.xpath(\"/tag[@id='%s']\" % value)\n\n\nurlpatterns = [\n path('a', a)\n]\n\n```\nThis can be fixed by using a parameterized query as shown below.\n\n\n```python\nfrom lxml import etree\nfrom io import StringIO\n\nfrom django.urls import path\nfrom django.http import HttpResponse\nfrom django.template import Template, Context, Engine, engines\n\n\ndef a(request):\n value = request.GET['xpath']\n f = StringIO('\u003cfoo\u003e\u003cbar\u003e\u003c/bar\u003e\u003c/foo\u003e')\n tree = etree.parse(f)\n r = tree.xpath(\"/tag[@id=$tagid]\", tagid=value)\n\n\nurlpatterns = [\n path('a', a)\n]\n\n```\n\n## References\n* OWASP XPath injection : [](https://owasp.org/www-community/attacks/XPATH_Injection)/\u0026gt;\u0026gt;\n* Common Weakness Enumeration: [CWE-643](https://cwe.mitre.org/data/definitions/643.html).\n"},"id":"py/xpath-injection","name":"py/xpath-injection","properties":{"precision":"high","queryURI":"https://github.com/github/codeql/blob/39a67b6e2e6490a9bd010db50e148f647765e9f7/python/ql/src/Security/CWE-643/XpathInjection.ql","security-severity":"9.8","tags":["external/cwe/cwe-643","security"]},"shortDescription":{"text":"XPath query built from user-controlled sources"}},{"defaultConfiguration":{"level":"error"},"fullDescription":{"text":"Parsing user input as an XML document with external entity expansion is vulnerable to XXE attacks."},"help":{"markdown":"# XML external entity expansion\nParsing untrusted XML files with a weakly configured XML parser may lead to an XML External Entity (XXE) attack. This type of attack uses external entity references to access arbitrary files on a system, carry out denial-of-service (DoS) attacks, or server-side request forgery. Even when the result of parsing is not returned to the user, DoS attacks are still possible and out-of-band data retrieval techniques may allow attackers to steal sensitive data.\n\n\n## Recommendation\nThe easiest way to prevent XXE attacks is to disable external entity handling when parsing untrusted data. How this is done depends on the library being used. Note that some libraries, such as recent versions of the XML libraries in the standard library of Python 3, disable entity expansion by default, so unless you have explicitly enabled entity expansion, no further action needs to be taken.\n\nWe recommend using the [defusedxml](https://pypi.org/project/defusedxml/) PyPI package, which has been created to prevent XML attacks (both XXE and XML bombs).\n\n\n## Example\nThe following example uses the `lxml` XML parser to parse a string `xml_src`. That string is from an untrusted source, so this code is vulnerable to an XXE attack, since the [ default parser](https://lxml.de/apidoc/lxml.etree.html#lxml.etree.XMLParser) from `lxml.etree` allows local external entities to be resolved.\n\n\n```python\nfrom flask import Flask, request\nimport lxml.etree\n\napp = Flask(__name__)\n\n@app.post(\"/upload\")\ndef upload():\n xml_src = request.get_data()\n doc = lxml.etree.fromstring(xml_src)\n return lxml.etree.tostring(doc)\n\n```\nTo guard against XXE attacks with the `lxml` library, you should create a parser with `resolve_entities` set to `false`. This means that no entity expansion is undertaken, although standard predefined entities such as `\u0026gt;`, for writing `\u003e` inside the text of an XML element, are still allowed.\n\n\n```python\nfrom flask import Flask, request\nimport lxml.etree\n\napp = Flask(__name__)\n\n@app.post(\"/upload\")\ndef upload():\n xml_src = request.get_data()\n parser = lxml.etree.XMLParser(resolve_entities=False)\n doc = lxml.etree.fromstring(xml_src, parser=parser)\n return lxml.etree.tostring(doc)\n\n```\n\n## References\n* OWASP: [XML External Entity (XXE) Processing](https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Processing).\n* Timothy Morgen: [XML Schema, DTD, and Entity Attacks](https://research.nccgroup.com/2014/05/19/xml-schema-dtd-and-entity-attacks-a-compendium-of-known-techniques/).\n* Timur Yunusov, Alexey Osipov: [XML Out-Of-Band Data Retrieval](https://www.slideshare.net/qqlan/bh-ready-v4).\n* Python 3 standard library: [XML Vulnerabilities](https://docs.python.org/3/library/xml.html#xml-vulnerabilities).\n* Python 2 standard library: [XML Vulnerabilities](https://docs.python.org/2/library/xml.html#xml-vulnerabilities).\n* PortSwigger: [XML external entity (XXE) injection](https://portswigger.net/web-security/xxe).\n* Common Weakness Enumeration: [CWE-611](https://cwe.mitre.org/data/definitions/611.html).\n* Common Weakness Enumeration: [CWE-827](https://cwe.mitre.org/data/definitions/827.html).\n","text":"# XML external entity expansion\nParsing untrusted XML files with a weakly configured XML parser may lead to an XML External Entity (XXE) attack. This type of attack uses external entity references to access arbitrary files on a system, carry out denial-of-service (DoS) attacks, or server-side request forgery. Even when the result of parsing is not returned to the user, DoS attacks are still possible and out-of-band data retrieval techniques may allow attackers to steal sensitive data.\n\n\n## Recommendation\nThe easiest way to prevent XXE attacks is to disable external entity handling when parsing untrusted data. How this is done depends on the library being used. Note that some libraries, such as recent versions of the XML libraries in the standard library of Python 3, disable entity expansion by default, so unless you have explicitly enabled entity expansion, no further action needs to be taken.\n\nWe recommend using the [defusedxml](https://pypi.org/project/defusedxml/) PyPI package, which has been created to prevent XML attacks (both XXE and XML bombs).\n\n\n## Example\nThe following example uses the `lxml` XML parser to parse a string `xml_src`. That string is from an untrusted source, so this code is vulnerable to an XXE attack, since the [ default parser](https://lxml.de/apidoc/lxml.etree.html#lxml.etree.XMLParser) from `lxml.etree` allows local external entities to be resolved.\n\n\n```python\nfrom flask import Flask, request\nimport lxml.etree\n\napp = Flask(__name__)\n\n@app.post(\"/upload\")\ndef upload():\n xml_src = request.get_data()\n doc = lxml.etree.fromstring(xml_src)\n return lxml.etree.tostring(doc)\n\n```\nTo guard against XXE attacks with the `lxml` library, you should create a parser with `resolve_entities` set to `false`. This means that no entity expansion is undertaken, although standard predefined entities such as `\u0026gt;`, for writing `\u003e` inside the text of an XML element, are still allowed.\n\n\n```python\nfrom flask import Flask, request\nimport lxml.etree\n\napp = Flask(__name__)\n\n@app.post(\"/upload\")\ndef upload():\n xml_src = request.get_data()\n parser = lxml.etree.XMLParser(resolve_entities=False)\n doc = lxml.etree.fromstring(xml_src, parser=parser)\n return lxml.etree.tostring(doc)\n\n```\n\n## References\n* OWASP: [XML External Entity (XXE) Processing](https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Processing).\n* Timothy Morgen: [XML Schema, DTD, and Entity Attacks](https://research.nccgroup.com/2014/05/19/xml-schema-dtd-and-entity-attacks-a-compendium-of-known-techniques/).\n* Timur Yunusov, Alexey Osipov: [XML Out-Of-Band Data Retrieval](https://www.slideshare.net/qqlan/bh-ready-v4).\n* Python 3 standard library: [XML Vulnerabilities](https://docs.python.org/3/library/xml.html#xml-vulnerabilities).\n* Python 2 standard library: [XML Vulnerabilities](https://docs.python.org/2/library/xml.html#xml-vulnerabilities).\n* PortSwigger: [XML external entity (XXE) injection](https://portswigger.net/web-security/xxe).\n* Common Weakness Enumeration: [CWE-611](https://cwe.mitre.org/data/definitions/611.html).\n* Common Weakness Enumeration: [CWE-827](https://cwe.mitre.org/data/definitions/827.html).\n"},"id":"py/xxe","name":"py/xxe","properties":{"precision":"high","queryURI":"https://github.com/github/codeql/blob/39a67b6e2e6490a9bd010db50e148f647765e9f7/python/ql/src/Security/CWE-611/Xxe.ql","security-severity":"9.1","tags":["external/cwe/cwe-611","external/cwe/cwe-827","security"]},"shortDescription":{"text":"XML external entity expansion"}}],"semanticVersion":"1.3.2+39a67b6e2e6490a9bd010db50e148f647765e9f7"},{"name":"codeql/python-all","semanticVersion":"2.1.2+39a67b6e2e6490a9bd010db50e148f647765e9f7"},{"name":"codeql/threat-models","semanticVersion":"1.0.11+39a67b6e2e6490a9bd010db50e148f647765e9f7"}]},"versionControlProvenance":[{"branch":"refs/heads/master","repositoryUri":"https://github.com/nahsra/Vulnerable-Code-Snippets","revisionId":"07669239ed45467b3c169b9747b3ccdc229632ca"}]}],"$schema":"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json","version":"2.1.0"}