Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## v2.1.3
### fix: Update insecure cookie and command injection rules
- fix: Require insecure cookie to flow in response and add tests
- fix: Add ProcessBuilder creation with tainted List sink
## v2.1.2
### fix: Add sanitizer for redirects and clean sources
- fix: Add a sanitizer for `unvalidated-redirect` and clean sources
Expand Down
16 changes: 16 additions & 0 deletions rules/java/lib/generic/command-injection-sinks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,22 @@ rules:
(ProcessBuilder $PB).command(..., $UNTRUSTED, ...);
- pattern:
new ProcessBuilder(..., $UNTRUSTED, ...);
- pattern:
(java.util.List $ARGS).add($UNTRUSTED);
...
new ProcessBuilder(..., $ARGS, ...);
- pattern:
(java.util.List $ARGS) = List.of(..., $UNTRUSTED, ...);
...
new ProcessBuilder(..., $ARGS, ...);
- pattern:
(java.util.List $ARGS).add($UNTRUSTED);
...
(ProcessBuilder $PB).command($ARGS);
- pattern:
(java.util.List $ARGS) = List.of(..., $UNTRUSTED, ...);
...
(ProcessBuilder $PB).command($ARGS);
- patterns:
- pattern: |
(ProcessBuilder $PB).command().$ADD(..., $UNTRUSTED, ...);
Expand Down
21 changes: 15 additions & 6 deletions rules/java/security/insecure-design.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -107,16 +107,25 @@ rules:
pattern-either:
- patterns:
- pattern: |
javax.servlet.http.Cookie $C = new Cookie(..., ...);
($X.servlet.http.Cookie $C) = new Cookie(...);
...
(HttpServletResponse $RESP).addCookie($C);
($X.servlet.http.HttpServletResponse $RESP).addCookie($C);
- pattern-not-inside: |
javax.servlet.http.Cookie $C = new Cookie(..., ...);
($X.servlet.http.Cookie $C) = new Cookie(...);
...
$C.setHttpOnly(true);
...
(HttpServletResponse $RESP).addCookie($C);
- pattern: (javax.servlet.http.Cookie $C).setHttpOnly(false);
- pattern: (org.springframework.http.ResponseCookie.ResponseCookieBuilder $B).httpOnly(false);
- patterns:
- pattern: |
$G = new org.springframework.web.util.CookieGenerator();
...
$G.addCookie($RESP, $VAL);
- pattern-not-inside: |
$G = new org.springframework.web.util.CookieGenerator();
...
$G.setCookieHttpOnly(true);
- pattern: (org.springframework.web.util.CookieGenerator $G).setCookieHttpOnly(false);

- id: persistent-cookie
severity: NOTE
Expand All @@ -134,7 +143,7 @@ rules:
- java
patterns:
- pattern-inside: |
(javax.servlet.http.Cookie $C).setMaxAge($AGE);
($X.servlet.http.Cookie $C).setMaxAge($AGE);
- metavariable-comparison:
comparison: $AGE >= 31536000
metavariable: $AGE
Expand Down
41 changes: 33 additions & 8 deletions rules/java/security/sensitive-data-exposure.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,39 @@ rules:
provenance: https://github.com/semgrep/semgrep-rules/blob/develop/java/servlets/security/cookie-issecure-false.yaml
languages:
- java
patterns:
- pattern: $COOKIE = new Cookie(...);
- pattern-not-inside: |
$COOKIE = new Cookie(...);
...
$COOKIE.setSecure(true);
- pattern-not-inside: |
$COOKIE.setValue("");
pattern-either:
- patterns:
- pattern: |
($X.servlet.http.Cookie $COOKIE) = new Cookie(...);
...
($X.servlet.http.HttpServletResponse $RESPONSE).addCookie($COOKIE)
- pattern-not-inside: |
($X.servlet.http.Cookie $COOKIE) = new Cookie(...);
...
$COOKIE.setSecure(true);
- pattern-not-inside: |
($X.servlet.http.Cookie $COOKIE) = new Cookie(...);
...
$COOKIE.setValue("");
- pattern: ($X.servlet.http.Cookie $C).setSecure(false);
- pattern: (org.springframework.http.ResponseCookie.ResponseCookieBuilder $B).secure(false);
- patterns:
- pattern: |
$G = new org.springframework.web.util.CookieGenerator();
...
$G.addCookie($RESP, $VAL);
- pattern-not-inside: |
$G = new org.springframework.web.util.CookieGenerator();
...
$G.setCookieSecure(true);
- pattern: (org.springframework.web.util.CookieGenerator $G).setCookieSecure(false);
- patterns:
- pattern-either:
- pattern: ($X.servlet.http.HttpServletResponse $R).addHeader("Set-Cookie", "$VAL");
- pattern: ($X.servlet.http.HttpServletResponse $R).setHeader("Set-Cookie", "$VAL");
- metavariable-regex:
metavariable: $VAL
regex: ^(?!.*Secure).*$

- id: unencrypted-socket
severity: NOTE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@
import org.seqra.sast.test.util.NegativeRuleSample;
import org.seqra.sast.test.util.PositiveRuleSample;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseCookie;
import org.springframework.http.ResponseEntity;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.CookieGenerator;

/**
* Samples for rules in java/security/insecure-design.yaml.
Expand Down Expand Up @@ -78,6 +80,53 @@ public void createSessionCookieWithHttpOnlySecure(HttpServletResponse response)
response.addCookie(sessionCookie);
}

@PositiveRuleSample(value = "java/security/insecure-design.yaml", id = "cookie-missing-httponly")
public void explicitSetHttpOnlyFalse(HttpServletResponse response) {
Cookie cookie = new Cookie("SESSION", "secret-token");
// VULNERABLE: explicitly setting HttpOnly to false
cookie.setHttpOnly(false);
response.addCookie(cookie);
}

@PositiveRuleSample(value = "java/security/insecure-design.yaml", id = "cookie-missing-httponly")
public void springResponseCookieHttpOnlyFalse() {
// VULNERABLE: explicitly setting httpOnly(false) on ResponseCookie builder
ResponseCookie.ResponseCookieBuilder builder = ResponseCookie.from("SESSION", "value");
builder.httpOnly(false);
}

@NegativeRuleSample(value = "java/security/insecure-design.yaml", id = "cookie-missing-httponly")
public void springResponseCookieHttpOnlyTrue() {
// SAFE: setting httpOnly(true) on ResponseCookie builder
ResponseCookie.ResponseCookieBuilder builder = ResponseCookie.from("SESSION", "value");
builder.httpOnly(true);
}

@PositiveRuleSample(value = "java/security/insecure-design.yaml", id = "cookie-missing-httponly")
public void cookieGeneratorWithoutHttpOnly(HttpServletResponse response) {
// VULNERABLE: CookieGenerator without setCookieHttpOnly(true)
CookieGenerator gen = new CookieGenerator();
gen.setCookieName("SESSION");
gen.addCookie(response, "value");
}

@NegativeRuleSample(value = "java/security/insecure-design.yaml", id = "cookie-missing-httponly")
public void cookieGeneratorWithHttpOnly(HttpServletResponse response) {
// SAFE: CookieGenerator with setCookieHttpOnly(true)
CookieGenerator gen = new CookieGenerator();
gen.setCookieName("SESSION");
gen.setCookieHttpOnly(true);
gen.addCookie(response, "value");
}

@PositiveRuleSample(value = "java/security/insecure-design.yaml", id = "cookie-missing-httponly")
public void cookieGeneratorExplicitHttpOnlyFalse(HttpServletResponse response) {
// VULNERABLE: explicitly setting setCookieHttpOnly(false)
CookieGenerator gen = new CookieGenerator();
gen.setCookieHttpOnly(false);
gen.addCookie(response, "value");
}

// === persistent-cookie ===

@PositiveRuleSample(value = "java/security/insecure-design.yaml", id = "persistent-cookie")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import java.io.PrintWriter;
import java.net.Socket;
import java.net.ServerSocket;
import org.springframework.http.ResponseCookie;
import org.springframework.web.util.CookieGenerator;

/**
* Samples for sensitive-data-exposure rules.
Expand All @@ -21,19 +23,89 @@ public class SensitiveDataExposureSamples {

// cookie-issecure-false

@org.springframework.web.bind.annotation.GetMapping("/insecureCookie")
@PositiveRuleSample(value = "java/security/sensitive-data-exposure.yaml", id = "cookie-issecure-false")
public Cookie insecureSessionCookie() {
public void insecureSessionCookie(HttpServletResponse response) {
// VULNERABLE: create a cookie without setting Secure, allowing cleartext transport
Cookie session = new Cookie("SESSIONID", "sensitive-session-id");
return session;
response.addCookie(session);
}

@org.springframework.web.bind.annotation.GetMapping("/secureCookie")
@NegativeRuleSample(value = "java/security/sensitive-data-exposure.yaml", id = "cookie-issecure-false")
public Cookie secureSessionCookie() {
public void secureSessionCookie(HttpServletResponse response) {
Cookie session = new Cookie("SESSIONID", "sensitive-session-id");
// SAFE: explicitly mark cookie as Secure (and typically HttpOnly, but rule focuses on Secure)
session.setSecure(true);
return session;
response.addCookie(session);
}

@org.springframework.web.bind.annotation.GetMapping("/secureEmptyCookie")
@NegativeRuleSample(value = "java/security/sensitive-data-exposure.yaml", id = "cookie-issecure-false")
public void secureEmptySessionCookie(HttpServletResponse response) {
Cookie session = new Cookie("SESSIONID", "sensitive-session-id");
// SAFE: cookie value is empty, no sensitive data to expose
session.setValue("");
response.addCookie(session);
}

@PositiveRuleSample(value = "java/security/sensitive-data-exposure.yaml", id = "cookie-issecure-false")
public void explicitSetSecureFalse(HttpServletResponse response) {
Cookie cookie = new Cookie("TOKEN", "value");
// VULNERABLE: explicitly setting Secure to false
cookie.setSecure(false);
response.addCookie(cookie);
}

@PositiveRuleSample(value = "java/security/sensitive-data-exposure.yaml", id = "cookie-issecure-false")
public void springResponseCookieSecureFalse() {
// VULNERABLE: explicitly setting secure(false) on ResponseCookie builder
ResponseCookie.ResponseCookieBuilder builder = ResponseCookie.from("TOKEN", "value");
builder.secure(false);
}

@NegativeRuleSample(value = "java/security/sensitive-data-exposure.yaml", id = "cookie-issecure-false")
public void springResponseCookieSecureTrue() {
// SAFE: setting secure(true) on ResponseCookie builder
ResponseCookie.ResponseCookieBuilder builder = ResponseCookie.from("TOKEN", "value");
builder.secure(true);
}

@PositiveRuleSample(value = "java/security/sensitive-data-exposure.yaml", id = "cookie-issecure-false")
public void cookieGeneratorWithoutSecure(HttpServletResponse response) {
// VULNERABLE: CookieGenerator without setCookieSecure(true)
CookieGenerator gen = new CookieGenerator();
gen.setCookieName("TOKEN");
gen.addCookie(response, "value");
}

@NegativeRuleSample(value = "java/security/sensitive-data-exposure.yaml", id = "cookie-issecure-false")
public void cookieGeneratorWithSecure(HttpServletResponse response) {
// SAFE: CookieGenerator with setCookieSecure(true)
CookieGenerator gen = new CookieGenerator();
gen.setCookieName("TOKEN");
gen.setCookieSecure(true);
gen.addCookie(response, "value");
}

@PositiveRuleSample(value = "java/security/sensitive-data-exposure.yaml", id = "cookie-issecure-false")
public void cookieGeneratorExplicitSecureFalse(HttpServletResponse response) {
// VULNERABLE: explicitly setting setCookieSecure(false)
CookieGenerator gen = new CookieGenerator();
gen.setCookieSecure(false);
gen.addCookie(response, "value");
}

@PositiveRuleSample(value = "java/security/sensitive-data-exposure.yaml", id = "cookie-issecure-false")
public void rawSetCookieHeaderWithoutSecure(HttpServletResponse response) {
// VULNERABLE: raw Set-Cookie header without Secure flag
response.addHeader("Set-Cookie", "TOKEN=value; HttpOnly; Path=/");
}

@NegativeRuleSample(value = "java/security/sensitive-data-exposure.yaml", id = "cookie-issecure-false")
public void rawSetCookieHeaderWithSecure(HttpServletResponse response) {
// SAFE: raw Set-Cookie header with Secure flag
response.addHeader("Set-Cookie", "TOKEN=value; HttpOnly; Secure; Path=/");
}

// unencrypted-socket
Expand Down