diff --git a/python/ql/integration-tests/query-suite/python-code-scanning.qls.expected b/python/ql/integration-tests/query-suite/python-code-scanning.qls.expected index 4db5af9c1a2f..90885318d0d8 100644 --- a/python/ql/integration-tests/query-suite/python-code-scanning.qls.expected +++ b/python/ql/integration-tests/query-suite/python-code-scanning.qls.expected @@ -13,8 +13,10 @@ ql/python/ql/src/Security/CWE-079/ReflectedXss.ql ql/python/ql/src/Security/CWE-089/SqlInjection.ql ql/python/ql/src/Security/CWE-090/LdapInjection.ql ql/python/ql/src/Security/CWE-094/CodeInjection.ql +ql/python/ql/src/Security/CWE-1004/NonHttpOnlyCookie.ql ql/python/ql/src/Security/CWE-113/HeaderInjection.ql ql/python/ql/src/Security/CWE-116/BadTagFilter.ql +ql/python/ql/src/Security/CWE-1275/SameSiteNoneCookie.ql ql/python/ql/src/Security/CWE-209/StackTraceExposure.ql ql/python/ql/src/Security/CWE-215/FlaskDebug.ql ql/python/ql/src/Security/CWE-285/PamAuthorization.ql diff --git a/python/ql/integration-tests/query-suite/python-security-and-quality.qls.expected b/python/ql/integration-tests/query-suite/python-security-and-quality.qls.expected index faa4204c3c71..11b75dd0ee39 100644 --- a/python/ql/integration-tests/query-suite/python-security-and-quality.qls.expected +++ b/python/ql/integration-tests/query-suite/python-security-and-quality.qls.expected @@ -106,9 +106,11 @@ ql/python/ql/src/Security/CWE-079/ReflectedXss.ql ql/python/ql/src/Security/CWE-089/SqlInjection.ql ql/python/ql/src/Security/CWE-090/LdapInjection.ql ql/python/ql/src/Security/CWE-094/CodeInjection.ql +ql/python/ql/src/Security/CWE-1004/NonHttpOnlyCookie.ql ql/python/ql/src/Security/CWE-113/HeaderInjection.ql ql/python/ql/src/Security/CWE-116/BadTagFilter.ql ql/python/ql/src/Security/CWE-117/LogInjection.ql +ql/python/ql/src/Security/CWE-1275/SameSiteNoneCookie.ql ql/python/ql/src/Security/CWE-209/StackTraceExposure.ql ql/python/ql/src/Security/CWE-215/FlaskDebug.ql ql/python/ql/src/Security/CWE-285/PamAuthorization.ql diff --git a/python/ql/integration-tests/query-suite/python-security-extended.qls.expected b/python/ql/integration-tests/query-suite/python-security-extended.qls.expected index 1b255c6a0d05..d677e65c3cfe 100644 --- a/python/ql/integration-tests/query-suite/python-security-extended.qls.expected +++ b/python/ql/integration-tests/query-suite/python-security-extended.qls.expected @@ -16,9 +16,11 @@ ql/python/ql/src/Security/CWE-079/ReflectedXss.ql ql/python/ql/src/Security/CWE-089/SqlInjection.ql ql/python/ql/src/Security/CWE-090/LdapInjection.ql ql/python/ql/src/Security/CWE-094/CodeInjection.ql +ql/python/ql/src/Security/CWE-1004/NonHttpOnlyCookie.ql ql/python/ql/src/Security/CWE-113/HeaderInjection.ql ql/python/ql/src/Security/CWE-116/BadTagFilter.ql ql/python/ql/src/Security/CWE-117/LogInjection.ql +ql/python/ql/src/Security/CWE-1275/SameSiteNoneCookie.ql ql/python/ql/src/Security/CWE-209/StackTraceExposure.ql ql/python/ql/src/Security/CWE-215/FlaskDebug.ql ql/python/ql/src/Security/CWE-285/PamAuthorization.ql diff --git a/python/ql/lib/semmle/python/Concepts.qll b/python/ql/lib/semmle/python/Concepts.qll index 16524aaf1dbd..0ca8a4dbef01 100644 --- a/python/ql/lib/semmle/python/Concepts.qll +++ b/python/ql/lib/semmle/python/Concepts.qll @@ -12,6 +12,7 @@ private import semmle.python.dataflow.new.TaintTracking private import semmle.python.Files private import semmle.python.Frameworks private import semmle.python.security.internal.EncryptionKeySizes +private import semmle.python.dataflow.new.SensitiveDataSources private import codeql.threatmodels.ThreatModels private import codeql.concepts.ConceptsShared @@ -1290,6 +1291,18 @@ module Http { */ DataFlow::Node getValueArg() { result = super.getValueArg() } + /** Holds if the name of this cookie indicates it may contain sensitive information. */ + predicate isSensitive() { + exists(DataFlow::Node name | + name = [this.getNameArg(), this.getHeaderArg()] and + ( + DataFlow::localFlow(any(SensitiveDataSource src), name) + or + name = sensitiveLookupStringConst(_) + ) + ) + } + /** * Holds if the `Secure` flag of the cookie is known to have a value of `b`. */ diff --git a/python/ql/lib/semmle/python/dataflow/new/SensitiveDataSources.qll b/python/ql/lib/semmle/python/dataflow/new/SensitiveDataSources.qll index 0e017c4a2295..1a32965d08d7 100644 --- a/python/ql/lib/semmle/python/dataflow/new/SensitiveDataSources.qll +++ b/python/ql/lib/semmle/python/dataflow/new/SensitiveDataSources.qll @@ -334,3 +334,5 @@ private module SensitiveDataModeling { } predicate sensitiveDataExtraStepForCalls = SensitiveDataModeling::extraStepForCalls/2; + +predicate sensitiveLookupStringConst = SensitiveDataModeling::sensitiveLookupStringConst/1; diff --git a/python/ql/src/Security/CWE-1004/NonHttpOnlyCookie.qhelp b/python/ql/src/Security/CWE-1004/NonHttpOnlyCookie.qhelp new file mode 100644 index 000000000000..7addd758fca9 --- /dev/null +++ b/python/ql/src/Security/CWE-1004/NonHttpOnlyCookie.qhelp @@ -0,0 +1,26 @@ + + + + +

Cookies 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. +If a sensitive cookie does not need to be accessed directly by client-side JS, the HttpOnly flag should be set.

+
+ + +

Set httponly to True, or add ; HttpOnly; to the cookie's raw header value, to ensure that the cookie is not accessible via JavaScript.

+
+ + +

In the following examples, the cases marked GOOD show secure cookie attributes being set; whereas in the case marked BAD they are not set.

+ +
+ + +
  • PortSwigger: Cookie without HttpOnly flag set
  • +
  • MDN: Set-Cookie.
  • +
    + +
    diff --git a/python/ql/src/Security/CWE-1004/NonHttpOnlyCookie.ql b/python/ql/src/Security/CWE-1004/NonHttpOnlyCookie.ql new file mode 100644 index 000000000000..01056daaf788 --- /dev/null +++ b/python/ql/src/Security/CWE-1004/NonHttpOnlyCookie.ql @@ -0,0 +1,21 @@ +/** + * @name Sensitive cookie missing `HttpOnly` attribute. + * @description Cookies without the `HttpOnly` attribute set can be accessed by JS scripts, making them more vulnerable to XSS attacks. + * @kind problem + * @problem.severity warning + * @security-severity 5.0 + * @precision high + * @id py/client-exposed-cookie + * @tags security + * external/cwe/cwe-1004 + */ + +import python +import semmle.python.dataflow.new.DataFlow +import semmle.python.Concepts + +from Http::Server::CookieWrite cookie +where + cookie.hasHttpOnlyFlag(false) and + cookie.isSensitive() +select cookie, "Sensitive server cookie is set without HttpOnly flag." diff --git a/python/ql/src/Security/CWE-1004/examples/InsecureCookie.py b/python/ql/src/Security/CWE-1004/examples/InsecureCookie.py new file mode 100644 index 000000000000..19d84d14537c --- /dev/null +++ b/python/ql/src/Security/CWE-1004/examples/InsecureCookie.py @@ -0,0 +1,21 @@ +from flask import Flask, request, make_response, Response + + +@app.route("/good1") +def good1(): + resp = make_response() + resp.set_cookie("sessionid", value="value", secure=True, httponly=True, samesite='Strict') # GOOD: Attributes are securely set + return resp + + +@app.route("/good2") +def good2(): + resp = make_response() + resp.headers['Set-Cookie'] = "sessionid=value; Secure; HttpOnly; SameSite=Strict" # GOOD: Attributes are securely set + return resp + +@app.route("/bad1") +def bad1(): + resp = make_response() + resp.set_cookie("sessionid", value="value", samesite='None') # BAD: the SameSite attribute is set to 'None' and the 'Secure' and 'HttpOnly' attributes are set to False by default. + return resp \ No newline at end of file diff --git a/python/ql/src/Security/CWE-1275/SameSiteNoneCookie.qhelp b/python/ql/src/Security/CWE-1275/SameSiteNoneCookie.qhelp new file mode 100644 index 000000000000..e0cc6eade1d3 --- /dev/null +++ b/python/ql/src/Security/CWE-1275/SameSiteNoneCookie.qhelp @@ -0,0 +1,26 @@ + + + + +

    Cookies with the SameSite attribute set to 'None' will be sent with cross-origin requests. +This can sometimes allow for Cross-Site Request Forgery (CSRF) attacks, in which a third-party site could perform actions on behalf of a user, if the cookie is used for authentication.

    +
    + + +

    Set the samesite to Lax or Strict, or add ; SameSite=Lax;, or +; SameSite=Strict; to the cookie's raw header value. The default value in most cases is Lax.

    +
    + + +

    In the following examples, the cases marked GOOD show secure cookie attributes being set; whereas in the case marked BAD they are not set.

    + +
    + + +
  • MDN: Set-Cookie.
  • +
  • OWASP: SameSite.
  • +
    + +
    diff --git a/python/ql/src/Security/CWE-1275/SameSiteNoneCookie.ql b/python/ql/src/Security/CWE-1275/SameSiteNoneCookie.ql new file mode 100644 index 000000000000..ebad2ddbba65 --- /dev/null +++ b/python/ql/src/Security/CWE-1275/SameSiteNoneCookie.ql @@ -0,0 +1,21 @@ +/** + * @name Sensitive cookie with `SameSite` attribute set to `None`. + * @description Cookies with `SameSite` set to `None` can allow for Cross-Site Request Forgery (CSRF) attacks. + * @kind problem + * @problem.severity warning + * @security-severity 4.0 + * @precision high + * @id py/samesite-none-cookie + * @tags security + * external/cwe/cwe-1275 + */ + +import python +import semmle.python.dataflow.new.DataFlow +import semmle.python.Concepts + +from Http::Server::CookieWrite cookie +where + cookie.hasSameSiteAttribute(any(Http::Server::CookieWrite::SameSiteNone v)) and + cookie.isSensitive() +select cookie, "Sensitive cookie with SameSite set to 'None'." diff --git a/python/ql/src/Security/CWE-1275/examples/InsecureCookie.py b/python/ql/src/Security/CWE-1275/examples/InsecureCookie.py new file mode 100644 index 000000000000..19d84d14537c --- /dev/null +++ b/python/ql/src/Security/CWE-1275/examples/InsecureCookie.py @@ -0,0 +1,21 @@ +from flask import Flask, request, make_response, Response + + +@app.route("/good1") +def good1(): + resp = make_response() + resp.set_cookie("sessionid", value="value", secure=True, httponly=True, samesite='Strict') # GOOD: Attributes are securely set + return resp + + +@app.route("/good2") +def good2(): + resp = make_response() + resp.headers['Set-Cookie'] = "sessionid=value; Secure; HttpOnly; SameSite=Strict" # GOOD: Attributes are securely set + return resp + +@app.route("/bad1") +def bad1(): + resp = make_response() + resp.set_cookie("sessionid", value="value", samesite='None') # BAD: the SameSite attribute is set to 'None' and the 'Secure' and 'HttpOnly' attributes are set to False by default. + return resp \ No newline at end of file diff --git a/python/ql/src/Security/CWE-614/InsecureCookie.qhelp b/python/ql/src/Security/CWE-614/InsecureCookie.qhelp index 5b36c9cc59d3..914d9d0baa58 100644 --- a/python/ql/src/Security/CWE-614/InsecureCookie.qhelp +++ b/python/ql/src/Security/CWE-614/InsecureCookie.qhelp @@ -4,26 +4,25 @@ -

    Cookies without the Secure flag set may be transmitted using HTTP instead of HTTPS, which leaves them vulnerable to reading by a third party.

    -

    Cookies 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.

    -

    Cookies 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.

    +

    Cookies without the Secure flag set may be transmitted using HTTP instead of HTTPS. +This leaves them vulnerable to being read by a third party attacker. If a sensitive cookie such as a session +key is intercepted this way, it would allow the attacker to perform actions on a user's behalf.

    -

    Always set secure to True or add "; Secure;" to the cookie's raw value.

    -

    Always set httponly to True or add "; HttpOnly;" to the cookie's raw value.

    -

    Always set samesite to Lax or Strict, or add "; SameSite=Lax;", or -"; Samesite=Strict;" to the cookie's raw header value.

    +

    Always set secure to True, or add ; Secure; to the cookie's raw header value, to ensure SSL is used to transmit the cookie +with encryption.

    -

    In the following examples, the cases marked GOOD show secure cookie attributes being set; whereas in the cases marked BAD they are not set.

    +

    In the following examples, the cases marked GOOD show secure cookie attributes being set; whereas in the case marked BAD they are not set.

  • Detectify: Cookie lack Secure flag.
  • PortSwigger: TLS cookie without secure flag set.
  • +
  • MDN: Set-Cookie.
  • diff --git a/python/ql/src/Security/CWE-614/InsecureCookie.ql b/python/ql/src/Security/CWE-614/InsecureCookie.ql index 260bd303310d..603c573e17f4 100644 --- a/python/ql/src/Security/CWE-614/InsecureCookie.ql +++ b/python/ql/src/Security/CWE-614/InsecureCookie.ql @@ -9,43 +9,14 @@ * @id py/insecure-cookie * @tags security * external/cwe/cwe-614 - * external/cwe/cwe-1004 - * external/cwe/cwe-1275 */ import python import semmle.python.dataflow.new.DataFlow import semmle.python.Concepts -predicate hasProblem(Http::Server::CookieWrite cookie, string alert, int idx) { +from Http::Server::CookieWrite cookie +where cookie.hasSecureFlag(false) and - alert = "Secure" and - idx = 0 - or - cookie.hasHttpOnlyFlag(false) and - alert = "HttpOnly" and - idx = 1 - or - cookie.hasSameSiteAttribute(any(Http::Server::CookieWrite::SameSiteNone v)) and - alert = "SameSite" and - idx = 2 -} - -predicate hasAlert(Http::Server::CookieWrite cookie, string alert) { - exists(int numProblems | numProblems = strictcount(string p | hasProblem(cookie, p, _)) | - numProblems = 1 and - alert = any(string prob | hasProblem(cookie, prob, _)) + " attribute" - or - numProblems = 2 and - alert = - strictconcat(string prob, int idx | hasProblem(cookie, prob, idx) | prob, " and " order by idx) - + " attributes" - or - numProblems = 3 and - alert = "Secure, HttpOnly, and SameSite attributes" - ) -} - -from Http::Server::CookieWrite cookie, string alert -where hasAlert(cookie, alert) -select cookie, "Cookie is added without the " + alert + " properly set." + cookie.isSensitive() +select cookie, "Cookie is added to response without the 'secure' flag being set." diff --git a/python/ql/src/Security/CWE-614/examples/InsecureCookie.py b/python/ql/src/Security/CWE-614/examples/InsecureCookie.py index 07cca6c3fcee..19d84d14537c 100644 --- a/python/ql/src/Security/CWE-614/examples/InsecureCookie.py +++ b/python/ql/src/Security/CWE-614/examples/InsecureCookie.py @@ -4,17 +4,18 @@ @app.route("/good1") def good1(): resp = make_response() - resp.set_cookie("name", value="value", secure=True, httponly=True, samesite='Strict') # GOOD: Attributes are securely set + resp.set_cookie("sessionid", value="value", secure=True, httponly=True, samesite='Strict') # GOOD: Attributes are securely set return resp @app.route("/good2") def good2(): resp = make_response() - resp.headers['Set-Cookie'] = "name=value; Secure; HttpOnly; SameSite=Strict" # GOOD: Attributes are securely set + resp.headers['Set-Cookie'] = "sessionid=value; Secure; HttpOnly; SameSite=Strict" # GOOD: Attributes are securely set return resp @app.route("/bad1") +def bad1(): resp = make_response() - 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. + resp.set_cookie("sessionid", value="value", samesite='None') # BAD: the SameSite attribute is set to 'None' and the 'Secure' and 'HttpOnly' attributes are set to False by default. return resp \ No newline at end of file diff --git a/python/ql/src/change-notes/2025-09-19-insecure-cookie.md b/python/ql/src/change-notes/2025-09-19-insecure-cookie.md new file mode 100644 index 000000000000..51c6dc6ce30f --- /dev/null +++ b/python/ql/src/change-notes/2025-09-19-insecure-cookie.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* The `py/insecure-cookie` query has been split into multiple queries; with `py/insecure-cookie` checking for cases in which `Secure` flag is not set, `py/client-exposed-cookie` checking for cases in which the `HttpOnly` flag is not set, and the `py/samesite-none` query checking for cases in which the `SameSite` attribute is set to `None`. These queries also now only alert for cases in which the cookie is detected to contain sensitive data. \ No newline at end of file diff --git a/python/ql/test/query-tests/Security/CWE-1004-NonHttpOnlyCookie/NonHttpOnlyCookie.expected b/python/ql/test/query-tests/Security/CWE-1004-NonHttpOnlyCookie/NonHttpOnlyCookie.expected new file mode 100644 index 000000000000..7af8af8d8708 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-1004-NonHttpOnlyCookie/NonHttpOnlyCookie.expected @@ -0,0 +1,3 @@ +| test.py:8:5:8:38 | ControlFlowNode for Attribute() | Sensitive server cookie is set without HttpOnly flag. | +| test.py:9:5:9:51 | ControlFlowNode for Attribute() | Sensitive server cookie is set without HttpOnly flag. | +| test.py:11:5:11:57 | ControlFlowNode for Attribute() | Sensitive server cookie is set without HttpOnly flag. | diff --git a/python/ql/test/query-tests/Security/CWE-1004-NonHttpOnlyCookie/NonHttpOnlyCookie.qlref b/python/ql/test/query-tests/Security/CWE-1004-NonHttpOnlyCookie/NonHttpOnlyCookie.qlref new file mode 100644 index 000000000000..7173918e3608 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-1004-NonHttpOnlyCookie/NonHttpOnlyCookie.qlref @@ -0,0 +1,2 @@ +query: Security/CWE-1004/NonHttpOnlyCookie.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Security/CWE-1004-NonHttpOnlyCookie/test.py b/python/ql/test/query-tests/Security/CWE-1004-NonHttpOnlyCookie/test.py new file mode 100644 index 000000000000..6091e31ae329 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-1004-NonHttpOnlyCookie/test.py @@ -0,0 +1,12 @@ +from flask import Flask, request, make_response + +app = Flask(__name__) + +@app.route("/test") +def test(): + resp = make_response() + resp.set_cookie("oauth", "value1") # $Alert[py/client-exposed-cookie] + resp.set_cookie("oauth", "value2", secure=True) # $Alert[py/client-exposed-cookie] + resp.set_cookie("oauth", "value2", httponly=True) + resp.set_cookie("oauth", "value2", samesite="Strict") # $Alert[py/client-exposed-cookie] + resp.set_cookie("oauth", "value2", httponly=True, samesite="None") \ No newline at end of file diff --git a/python/ql/test/query-tests/Security/CWE-1275-SameSiteNoneCookie/SameSiteNoneCookie.expected b/python/ql/test/query-tests/Security/CWE-1275-SameSiteNoneCookie/SameSiteNoneCookie.expected new file mode 100644 index 000000000000..7a8e83a732c6 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-1275-SameSiteNoneCookie/SameSiteNoneCookie.expected @@ -0,0 +1,2 @@ +| test.py:10:5:10:60 | ControlFlowNode for Attribute() | Sensitive cookie with SameSite set to 'None'. | +| test.py:13:5:13:78 | ControlFlowNode for Attribute() | Sensitive cookie with SameSite set to 'None'. | diff --git a/python/ql/test/query-tests/Security/CWE-1275-SameSiteNoneCookie/SameSiteNoneCookie.qlref b/python/ql/test/query-tests/Security/CWE-1275-SameSiteNoneCookie/SameSiteNoneCookie.qlref new file mode 100644 index 000000000000..ad4e08218b30 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-1275-SameSiteNoneCookie/SameSiteNoneCookie.qlref @@ -0,0 +1,2 @@ +query: Security/CWE-1275/SameSiteNoneCookie.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Security/CWE-1275-SameSiteNoneCookie/test.py b/python/ql/test/query-tests/Security/CWE-1275-SameSiteNoneCookie/test.py new file mode 100644 index 000000000000..f1fd1fd44864 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-1275-SameSiteNoneCookie/test.py @@ -0,0 +1,15 @@ +from flask import Flask, request, make_response + +app = Flask(__name__) + +@app.route("/test") +def test(oauth_cookie_name): + resp = make_response() + resp.set_cookie("password", "value1") + resp.set_cookie("authKey", "value2", samesite="Lax") + resp.set_cookie("session_id", "value2", samesite="None") # $Alert[py/samesite-none-cookie] + resp.set_cookie("oauth", "value2", secure=True, samesite="Strict") + resp.set_cookie("oauth", "value2", httponly=True, samesite="Strict") + resp.set_cookie(oauth_cookie_name, "value2", secure=True, samesite="None") # $Alert[py/samesite-none-cookie] + resp.set_cookie("not_sensitive", "value2", samesite="None") + \ No newline at end of file diff --git a/python/ql/test/query-tests/Security/CWE-614-InsecureCookie/InsecureCookie.expected b/python/ql/test/query-tests/Security/CWE-614-InsecureCookie/InsecureCookie.expected index 95cad5b954e7..3b07bc6d9ebb 100644 --- a/python/ql/test/query-tests/Security/CWE-614-InsecureCookie/InsecureCookie.expected +++ b/python/ql/test/query-tests/Security/CWE-614-InsecureCookie/InsecureCookie.expected @@ -1,10 +1,3 @@ -| test.py:10:5:10:37 | ControlFlowNode for Attribute() | Cookie is added without the Secure and HttpOnly attributes properly set. | -| test.py:11:5:11:50 | ControlFlowNode for Attribute() | Cookie is added without the HttpOnly attribute properly set. | -| test.py:12:5:12:52 | ControlFlowNode for Attribute() | Cookie is added without the Secure attribute properly set. | -| test.py:13:5:13:56 | ControlFlowNode for Attribute() | Cookie is added without the Secure and HttpOnly attributes properly set. | -| test.py:14:5:14:53 | ControlFlowNode for Attribute() | Cookie is added without the Secure and HttpOnly attributes properly set. | -| test.py:15:5:15:54 | ControlFlowNode for Attribute() | Cookie is added without the Secure, HttpOnly, and SameSite attributes properly set. | -| test.py:16:5:16:69 | ControlFlowNode for Attribute() | Cookie is added without the HttpOnly attribute properly set. | -| test.py:17:5:17:71 | ControlFlowNode for Attribute() | Cookie is added without the Secure attribute properly set. | -| test.py:18:5:18:67 | ControlFlowNode for Attribute() | Cookie is added without the HttpOnly and SameSite attributes properly set. | -| test.py:19:5:19:69 | ControlFlowNode for Attribute() | Cookie is added without the Secure and SameSite attributes properly set. | +| test.py:8:5:8:40 | ControlFlowNode for Attribute() | Cookie is added to response without the 'secure' flag being set. | +| test.py:10:5:10:57 | ControlFlowNode for Attribute() | Cookie is added to response without the 'secure' flag being set. | +| test.py:11:5:11:60 | ControlFlowNode for Attribute() | Cookie is added to response without the 'secure' flag being set. | diff --git a/python/ql/test/query-tests/Security/CWE-614-InsecureCookie/InsecureCookie.qlref b/python/ql/test/query-tests/Security/CWE-614-InsecureCookie/InsecureCookie.qlref index d5143f6eaae1..f70206677198 100644 --- a/python/ql/test/query-tests/Security/CWE-614-InsecureCookie/InsecureCookie.qlref +++ b/python/ql/test/query-tests/Security/CWE-614-InsecureCookie/InsecureCookie.qlref @@ -1 +1,2 @@ -Security/CWE-614/InsecureCookie.ql \ No newline at end of file +query: Security/CWE-614/InsecureCookie.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Security/CWE-614-InsecureCookie/test.py b/python/ql/test/query-tests/Security/CWE-614-InsecureCookie/test.py index 1a108fe41d49..214567275e5f 100644 --- a/python/ql/test/query-tests/Security/CWE-614-InsecureCookie/test.py +++ b/python/ql/test/query-tests/Security/CWE-614-InsecureCookie/test.py @@ -1,20 +1,12 @@ from flask import Flask, request, make_response -import lxml.etree -import markupsafe app = Flask(__name__) @app.route("/test") def test(): resp = make_response() - resp.set_cookie("key1", "value1") - resp.set_cookie("key2", "value2", secure=True) - resp.set_cookie("key2", "value2", httponly=True) - resp.set_cookie("key2", "value2", samesite="Strict") - resp.set_cookie("key2", "value2", samesite="Lax") - resp.set_cookie("key2", "value2", samesite="None") - resp.set_cookie("key2", "value2", secure=True, samesite="Strict") - resp.set_cookie("key2", "value2", httponly=True, samesite="Strict") - resp.set_cookie("key2", "value2", secure=True, samesite="None") - resp.set_cookie("key2", "value2", httponly=True, samesite="None") - resp.set_cookie("key2", "value2", secure=True, httponly=True, samesite="Strict") \ No newline at end of file + resp.set_cookie("authKey", "value1") # $Alert[py/insecure-cookie] + resp.set_cookie("authKey", "value2", secure=True) + resp.set_cookie("sessionID", "value2", httponly=True) # $Alert[py/insecure-cookie] + resp.set_cookie("password", "value2", samesite="Strict") # $Alert[py/insecure-cookie] + resp.set_cookie("notSensitive", "value3")