Skip to content

Conversation

joefarebrother
Copy link
Contributor

Promotes java/sensitive-cookie-not-httponly query from experimental.

@joefarebrother joefarebrother requested a review from a team as a code owner October 2, 2025 09:42
@Copilot Copilot AI review requested due to automatic review settings October 2, 2025 09:42
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR promotes the Java query java/sensitive-cookie-not-httponly from experimental status to production, moving it from the experimental to the main security query suite. The query detects sensitive cookies that are created without the HttpOnly flag set, which can expose them to client-side script access.

Key Changes

  • Removed the "experimental" tag from the query metadata
  • Moved test files from the experimental directory to the main query-tests directory
  • Updated query structure and replaced deprecated predicates with the current select statement format

Reviewed Changes

Copilot reviewed 9 out of 10 changed files in this pull request and generated 3 comments.

File Description
java/ql/src/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql Promoted query from experimental, removed deprecated predicate, updated class/module names
java/ql/src/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.qhelp Updated documentation with clearer explanation and additional MDN reference
java/ql/test/query-tests/security/CWE-1004/* New test files moved from experimental directory with updated annotations
java/ql/test/experimental/query-tests/security/CWE-1004/* Removed experimental test files

* (e.g. `session` or `token`) to a `ServletResponse.addHeader(...)` or `.addCookie(...)`
* method that does not set the `httpOnly` flag. Subsidiary configurations
* `MatchesHttpOnlyConfiguration` and `SetHttpOnlyInCookieConfiguration` are used to establish
* `MatchesHttpOnlyToRawHeaderConfiguration` and `SetHttpOnlyInCookieConfiguration` are used to establish
Copy link
Preview

Copilot AI Oct 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment references MatchesHttpOnlyToRawHeaderConfiguration and SetHttpOnlyInCookieConfiguration, but the actual module names in the code are MatchesHttpOnlyToRawHeaderConfig and SetHttpOnlyOrRemovesCookieToAddCookieConfig. The documentation should match the actual implementation.

Suggested change
* `MatchesHttpOnlyToRawHeaderConfiguration` and `SetHttpOnlyInCookieConfiguration` are used to establish
* `MatchesHttpOnlyToRawHeaderConfig` and `SetHttpOnlyOrRemovesCookieToAddCookieConfig` are used to establish

Copilot uses AI. Check for mistakes.

/**
* A taint configuration tracking flow from a sensitive cookie without the `HttpOnly` flag
* set to its HTTP response.
* Tracks string literals containing sensitive names (`SensitiveNameExpr`), to an `addCookie` call (as a `Cookie` object)
Copy link
Preview

Copilot AI Oct 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment references SensitiveNameExpr but the actual class name is SensitiveCookieNameExpr. The documentation should use the correct class name.

Suggested change
* Tracks string literals containing sensitive names (`SensitiveNameExpr`), to an `addCookie` call (as a `Cookie` object)
* Tracks string literals containing sensitive names (`SensitiveCookieNameExpr`), to an `addCookie` call (as a `Cookie` object)

Copilot uses AI. Check for mistakes.

* A taint configuration tracking flow from a sensitive cookie without the `HttpOnly` flag
* set to its HTTP response.
* Tracks string literals containing sensitive names (`SensitiveNameExpr`), to an `addCookie` call (as a `Cookie` object)
* or an `addHeader` call (as a string) (`CookieResponseWithoutHttpOnly`).
Copy link
Preview

Copilot AI Oct 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment references CookieResponseWithoutHttpOnly but the actual class name is CookieResponseWithoutHttpOnlySink. The documentation should use the correct class name.

Suggested change
* or an `addHeader` call (as a string) (`CookieResponseWithoutHttpOnly`).
* or an `addHeader` call (as a string) (`CookieResponseWithoutHttpOnlySink`).

Copilot uses AI. Check for mistakes.

Copy link
Contributor

github-actions bot commented Oct 2, 2025

QHelp previews:

java/ql/src/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.qhelp

Sensitive cookies without the HttpOnly response header set

Cookies without the HttpOnly flag set are accessible to client-side scripts (such as 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 scripts, the HttpOnly flag should be set.

Recommendation

Use the HttpOnly flag when generating a cookie containing sensitive information to help mitigate the risk of client-side scripts accessing the protected cookie.

Example

The following example shows two ways of generating sensitive cookies. In the 'BAD' cases, the HttpOnly flag is not set. In the 'GOOD' cases, the HttpOnly flag is set.

class SensitiveCookieNotHttpOnly {
    // GOOD - Create a sensitive cookie with the `HttpOnly` flag set.
    public void addCookie(String jwt_token, HttpServletRequest request, HttpServletResponse response) {
        Cookie jwtCookie =new Cookie("jwt_token", jwt_token);
        jwtCookie.setPath("/");
        jwtCookie.setMaxAge(3600*24*7);
        jwtCookie.setHttpOnly(true);
        response.addCookie(jwtCookie);
    }

    // BAD - Create a sensitive cookie without the `HttpOnly` flag set.
    public void addCookie2(String jwt_token, String userId, HttpServletRequest request, HttpServletResponse response) {
        Cookie jwtCookie =new Cookie("jwt_token", jwt_token);
        jwtCookie.setPath("/");
        jwtCookie.setMaxAge(3600*24*7);
        response.addCookie(jwtCookie);
    }

    // GOOD - Set a sensitive cookie header with the `HttpOnly` flag set.
    public void addCookie3(String authId, HttpServletRequest request, HttpServletResponse response) {
        response.addHeader("Set-Cookie", "token=" +authId + ";HttpOnly;Secure");
    }

    // BAD - Set a sensitive cookie header without the `HttpOnly` flag set.
    public void addCookie4(String authId, HttpServletRequest request, HttpServletResponse response) {
        response.addHeader("Set-Cookie", "token=" +authId + ";Secure");
    }
    
    // GOOD - Set a sensitive cookie header using the class `javax.ws.rs.core.Cookie` with the `HttpOnly` flag set through string concatenation.
    public void addCookie5(String accessKey, HttpServletRequest request, HttpServletResponse response) {
        response.setHeader("Set-Cookie", new NewCookie("session-access-key", accessKey, "/", null, null, 0, true) + ";HttpOnly");
    }

    // BAD - Set a sensitive cookie header using the class `javax.ws.rs.core.Cookie` without the `HttpOnly` flag set.
    public void addCookie6(String accessKey, HttpServletRequest request, HttpServletResponse response) {
        response.setHeader("Set-Cookie", new NewCookie("session-access-key", accessKey, "/", null, null, 0, true).toString());
    }

    // GOOD - Set a sensitive cookie header using the class `javax.ws.rs.core.Cookie` with the `HttpOnly` flag set through the constructor.
    public void addCookie7(String accessKey, HttpServletRequest request, HttpServletResponse response) {
        NewCookie accessKeyCookie = new NewCookie("session-access-key", accessKey, "/", null, null, 0, true, true);
        response.setHeader("Set-Cookie", accessKeyCookie.toString());
    }
}

References

Copy link
Contributor

@owen-mc owen-mc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And the suite inclusion test expectations need to be updated.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a little bit weird that for the last way of making a cookie we only have the good example and not the bad one. I mean the same thing but with false as the last argument. Is there a reason not to include that? I guess just that you wouldn't use that constructor, you'd use the one with one less argument. I guess there's no need to change it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants