Skip to content

Commit c91b5b3

Browse files
authored
Merge pull request #21650 from MarkLee131/fix/sensitive-log-fp-regex
Java: reduce false positives in sensitive-log
2 parents 58e9bad + 92d205d commit c91b5b3

File tree

4 files changed

+101
-2
lines changed

4 files changed

+101
-2
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* The `java/sensitive-log` query now excludes additional common variable naming patterns that do not hold sensitive data, reducing false positives. This includes pagination/iteration tokens (`nextToken`, `pageToken`, `continuationToken`), token metadata (`tokenType`, `tokenEndpoint`, `tokenCount`), and secret metadata (`secretName`, `secretId`, `secretVersion`).

java/ql/lib/semmle/code/java/security/SensitiveActions.qll

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,26 @@ string getCommonSensitiveInfoRegex() {
4040

4141
/**
4242
* Gets a regular expression for matching common names of variables that
43-
* indicate the value being held does not contains sensitive information,
43+
* indicate the value being held does not contain sensitive information,
4444
* but is a false positive for `getCommonSensitiveInfoRegex`.
4545
*
4646
* - "tokenizer" is often used for java.util.StringTokenizer.
4747
* - "tokenImage" appears in parser code generated by JavaCC.
48+
* - Pagination/iteration tokens: "nextToken" (AWS SDK), "pageToken" (GCP), etc.
49+
* - Token metadata: "tokenType" (OAuth), "tokenEndpoint" (OIDC), "tokenCount", etc.
50+
* - Secret metadata: "secretName" (K8s/AWS), "secretId" (Azure), "secretVersion", etc.
4851
*/
4952
string getCommonSensitiveInfoFPRegex() {
50-
result = "(?i).*(null|tokenizer).*" or result = "tokenImage"
53+
result =
54+
[
55+
"(?i).*(null|tokenizer).*", "tokenImage",
56+
// Pagination/iteration tokens (e.g., AWS SDK pagination cursors, parser tokens)
57+
"(?i).*(next|previous|current|page|continuation|cursor)tokens?.*",
58+
// Token metadata/infrastructure (token followed by a non-value descriptor)
59+
"(?i).*tokens?(type|kind|count|index|position|length|offset|endpoint|url|uri|bucket|rate|delimiter|separator|format|number|name|id|prefix|suffix|pattern|class|style).*",
60+
// Secret metadata (secret followed by a non-value descriptor)
61+
"(?i).*secrets?(name|id|version|ref|arn|path|type|label|description|manager|client|provider|store|factory|properties).*"
62+
]
5163
}
5264

5365
/** An expression that might contain sensitive data. */

java/ql/test/query-tests/security/CWE-532/SensitiveLogInfo.expected

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,29 @@
33
| Test.java:12:22:12:52 | ... + ... | Test.java:12:44:12:52 | authToken : String | Test.java:12:22:12:52 | ... + ... | This $@ is written to a log file. | Test.java:12:44:12:52 | authToken | potentially sensitive information |
44
| Test.java:21:22:21:75 | ... + ... | Test.java:21:44:21:52 | authToken : String | Test.java:21:22:21:75 | ... + ... | This $@ is written to a log file. | Test.java:21:44:21:52 | authToken | potentially sensitive information |
55
| Test.java:22:22:22:75 | ... + ... | Test.java:22:44:22:52 | authToken : String | Test.java:22:22:22:75 | ... + ... | This $@ is written to a log file. | Test.java:22:44:22:52 | authToken | potentially sensitive information |
6+
| Test.java:66:21:66:43 | ... + ... | Test.java:66:33:66:43 | accessToken : String | Test.java:66:21:66:43 | ... + ... | This $@ is written to a log file. | Test.java:66:33:66:43 | accessToken | potentially sensitive information |
7+
| Test.java:67:21:67:45 | ... + ... | Test.java:67:34:67:45 | clientSecret : String | Test.java:67:21:67:45 | ... + ... | This $@ is written to a log file. | Test.java:67:34:67:45 | clientSecret | potentially sensitive information |
8+
| Test.java:68:21:68:42 | ... + ... | Test.java:68:34:68:42 | apiSecret : String | Test.java:68:21:68:42 | ... + ... | This $@ is written to a log file. | Test.java:68:34:68:42 | apiSecret | potentially sensitive information |
9+
| Test.java:69:21:69:44 | ... + ... | Test.java:69:33:69:44 | sessionToken : String | Test.java:69:21:69:44 | ... + ... | This $@ is written to a log file. | Test.java:69:33:69:44 | sessionToken | potentially sensitive information |
10+
| Test.java:70:21:70:43 | ... + ... | Test.java:70:33:70:43 | bearerToken : String | Test.java:70:21:70:43 | ... + ... | This $@ is written to a log file. | Test.java:70:33:70:43 | bearerToken | potentially sensitive information |
11+
| Test.java:71:21:71:39 | ... + ... | Test.java:71:31:71:39 | secretKey : String | Test.java:71:21:71:39 | ... + ... | This $@ is written to a log file. | Test.java:71:31:71:39 | secretKey | potentially sensitive information |
12+
| Test.java:72:21:72:44 | ... + ... | Test.java:72:33:72:44 | refreshToken : String | Test.java:72:21:72:44 | ... + ... | This $@ is written to a log file. | Test.java:72:33:72:44 | refreshToken | potentially sensitive information |
13+
| Test.java:73:21:73:43 | ... + ... | Test.java:73:33:73:43 | secretValue : String | Test.java:73:21:73:43 | ... + ... | This $@ is written to a log file. | Test.java:73:33:73:43 | secretValue | potentially sensitive information |
614
edges
715
| Test.java:11:46:11:53 | password : String | Test.java:11:21:11:53 | ... + ... | provenance | Sink:MaD:2 |
816
| Test.java:12:44:12:52 | authToken : String | Test.java:12:22:12:52 | ... + ... | provenance | Sink:MaD:1 |
917
| Test.java:21:44:21:52 | authToken : String | Test.java:21:44:21:67 | substring(...) : String | provenance | MaD:3 |
1018
| Test.java:21:44:21:67 | substring(...) : String | Test.java:21:22:21:75 | ... + ... | provenance | Sink:MaD:1 |
1119
| Test.java:22:44:22:52 | authToken : String | Test.java:22:44:22:67 | substring(...) : String | provenance | MaD:3 |
1220
| Test.java:22:44:22:67 | substring(...) : String | Test.java:22:22:22:75 | ... + ... | provenance | Sink:MaD:1 |
21+
| Test.java:66:33:66:43 | accessToken : String | Test.java:66:21:66:43 | ... + ... | provenance | Sink:MaD:2 |
22+
| Test.java:67:34:67:45 | clientSecret : String | Test.java:67:21:67:45 | ... + ... | provenance | Sink:MaD:2 |
23+
| Test.java:68:34:68:42 | apiSecret : String | Test.java:68:21:68:42 | ... + ... | provenance | Sink:MaD:2 |
24+
| Test.java:69:33:69:44 | sessionToken : String | Test.java:69:21:69:44 | ... + ... | provenance | Sink:MaD:2 |
25+
| Test.java:70:33:70:43 | bearerToken : String | Test.java:70:21:70:43 | ... + ... | provenance | Sink:MaD:2 |
26+
| Test.java:71:31:71:39 | secretKey : String | Test.java:71:21:71:39 | ... + ... | provenance | Sink:MaD:2 |
27+
| Test.java:72:33:72:44 | refreshToken : String | Test.java:72:21:72:44 | ... + ... | provenance | Sink:MaD:2 |
28+
| Test.java:73:33:73:43 | secretValue : String | Test.java:73:21:73:43 | ... + ... | provenance | Sink:MaD:2 |
1329
models
1430
| 1 | Sink: org.apache.logging.log4j; Logger; true; error; (String); ; Argument[0]; log-injection; manual |
1531
| 2 | Sink: org.apache.logging.log4j; Logger; true; info; (String); ; Argument[0]; log-injection; manual |
@@ -25,4 +41,20 @@ nodes
2541
| Test.java:22:22:22:75 | ... + ... | semmle.label | ... + ... |
2642
| Test.java:22:44:22:52 | authToken : String | semmle.label | authToken : String |
2743
| Test.java:22:44:22:67 | substring(...) : String | semmle.label | substring(...) : String |
44+
| Test.java:66:21:66:43 | ... + ... | semmle.label | ... + ... |
45+
| Test.java:66:33:66:43 | accessToken : String | semmle.label | accessToken : String |
46+
| Test.java:67:21:67:45 | ... + ... | semmle.label | ... + ... |
47+
| Test.java:67:34:67:45 | clientSecret : String | semmle.label | clientSecret : String |
48+
| Test.java:68:21:68:42 | ... + ... | semmle.label | ... + ... |
49+
| Test.java:68:34:68:42 | apiSecret : String | semmle.label | apiSecret : String |
50+
| Test.java:69:21:69:44 | ... + ... | semmle.label | ... + ... |
51+
| Test.java:69:33:69:44 | sessionToken : String | semmle.label | sessionToken : String |
52+
| Test.java:70:21:70:43 | ... + ... | semmle.label | ... + ... |
53+
| Test.java:70:33:70:43 | bearerToken : String | semmle.label | bearerToken : String |
54+
| Test.java:71:21:71:39 | ... + ... | semmle.label | ... + ... |
55+
| Test.java:71:31:71:39 | secretKey : String | semmle.label | secretKey : String |
56+
| Test.java:72:21:72:44 | ... + ... | semmle.label | ... + ... |
57+
| Test.java:72:33:72:44 | refreshToken : String | semmle.label | refreshToken : String |
58+
| Test.java:73:21:73:43 | ... + ... | semmle.label | ... + ... |
59+
| Test.java:73:33:73:43 | secretValue : String | semmle.label | secretValue : String |
2860
subpaths

java/ql/test/query-tests/security/CWE-532/Test.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,55 @@ void test(String password, String authToken, String username, String nullToken,
2121
logger.error("Auth failed for: " + authToken.substring(1,5) + "..."); // $ Alert
2222
logger.error("Auth failed for: " + authToken.substring(0,8) + "..."); // $ Alert
2323
}
24+
25+
// Tests for false positive exclusions: variables with "token" or "secret" in the name
26+
// that do not hold sensitive data.
27+
void testFalsePositiveExclusions(
28+
String nextToken, String pageToken, String continuationToken, String cursorToken,
29+
String tokenType, String tokenEndpoint, String tokenCount, String tokenUrl,
30+
String tokenIndex, String tokenLength, String tokenName, String tokenId,
31+
String secretName, String secretId, String secretVersion, String secretArn,
32+
String secretPath, String secretType,
33+
String secretManager, String secretProperties
34+
) {
35+
Logger logger = null;
36+
// Pagination/iteration tokens (e.g., AWS SDK, GCP, Azure pagination cursors)
37+
logger.info("cursor: " + nextToken); // Safe
38+
logger.info("cursor: " + pageToken); // Safe
39+
logger.info("cursor: " + continuationToken); // Safe
40+
logger.info("cursor: " + cursorToken); // Safe
41+
// Token metadata (e.g., OAuth token type, OIDC discovery endpoint)
42+
logger.info("type: " + tokenType); // Safe
43+
logger.info("endpoint: " + tokenEndpoint); // Safe
44+
logger.info("count: " + tokenCount); // Safe
45+
logger.info("url: " + tokenUrl); // Safe
46+
logger.info("index: " + tokenIndex); // Safe
47+
logger.info("length: " + tokenLength); // Safe
48+
logger.info("name: " + tokenName); // Safe
49+
logger.info("id: " + tokenId); // Safe
50+
// Secret metadata (e.g., K8s secret name, AWS Secrets Manager identifiers)
51+
logger.info("name: " + secretName); // Safe
52+
logger.info("id: " + secretId); // Safe
53+
logger.info("version: " + secretVersion); // Safe
54+
logger.info("arn: " + secretArn); // Safe
55+
logger.info("path: " + secretPath); // Safe
56+
logger.info("type: " + secretType); // Safe
57+
logger.info("manager: " + secretManager); // Safe
58+
logger.info("properties: " + secretProperties); // Safe
59+
}
60+
61+
// These should still be flagged as sensitive
62+
void testTruePositives(String accessToken, String clientSecret, String apiSecret,
63+
String sessionToken, String bearerToken, String secretKey,
64+
String refreshToken, String secretValue) {
65+
Logger logger = null;
66+
logger.info("token: " + accessToken); // $ Alert
67+
logger.info("secret: " + clientSecret); // $ Alert
68+
logger.info("secret: " + apiSecret); // $ Alert
69+
logger.info("token: " + sessionToken); // $ Alert
70+
logger.info("token: " + bearerToken); // $ Alert
71+
logger.info("key: " + secretKey); // $ Alert
72+
logger.info("token: " + refreshToken); // $ Alert
73+
logger.info("value: " + secretValue); // $ Alert
74+
}
2475
}

0 commit comments

Comments
 (0)