Files
codeql/java/ql/lib/semmle/code/java/security/SensitiveActions.qll
MarkLee131 92d205d1a8 Use set literal for getCommonSensitiveInfoFPRegex
Replace the five-way result = ... or result = ... disjunction with a
single equality on a set literal. Addresses the CodeQL style alert
"Use a set literal in place of or" reported by the self-scan on this
PR. Pure refactor, no semantic change.
2026-04-19 23:29:07 -04:00

126 lines
4.4 KiB
Plaintext

/**
* Sensitive data and methods for security.
*
* 'Sensitive' data in general is anything that should not be
* sent around in unencrypted form. This library tries to guess
* where sensitive data may either be stored in a variable or
* produced by a method.
*
* In addition, there are methods that ought not to be executed or not
* in a fashion that the user can control. This includes authorization
* methods such as logins, and sending of data, etc.
*/
overlay[local?]
module;
import java
private string suspicious() {
result =
[
"%password%", "%passwd%", "pwd", "%account%", "%accnt%", "%trusted%", "%refresh%token%",
"%secret%token"
]
}
private string nonSuspicious() {
result = "%hashed%" or
result = "%encrypted%" or
result = "%crypt%"
}
/**
* Gets a regular expression for matching common names of variables that
* indicate the value being held contains sensitive information.
*/
string getCommonSensitiveInfoRegex() {
result = "(?i).*(challenge|pass(wd|word|code|phrase))(?!.*question).*" or
result = "(?i).*(token|secret).*"
}
/**
* Gets a regular expression for matching common names of variables that
* indicate the value being held does not contain sensitive information,
* but is a false positive for `getCommonSensitiveInfoRegex`.
*
* - "tokenizer" is often used for java.util.StringTokenizer.
* - "tokenImage" appears in parser code generated by JavaCC.
* - Pagination/iteration tokens: "nextToken" (AWS SDK), "pageToken" (GCP), etc.
* - Token metadata: "tokenType" (OAuth), "tokenEndpoint" (OIDC), "tokenCount", etc.
* - Secret metadata: "secretName" (K8s/AWS), "secretId" (Azure), "secretVersion", etc.
*/
string getCommonSensitiveInfoFPRegex() {
result =
[
"(?i).*(null|tokenizer).*", "tokenImage",
// Pagination/iteration tokens (e.g., AWS SDK pagination cursors, parser tokens)
"(?i).*(next|previous|current|page|continuation|cursor)tokens?.*",
// Token metadata/infrastructure (token followed by a non-value descriptor)
"(?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).*",
// Secret metadata (secret followed by a non-value descriptor)
"(?i).*secrets?(name|id|version|ref|arn|path|type|label|description|manager|client|provider|store|factory|properties).*"
]
}
/** An expression that might contain sensitive data. */
abstract class SensitiveExpr extends Expr { }
/** A method access that might produce sensitive data. */
class SensitiveMethodCall extends SensitiveExpr, MethodCall {
SensitiveMethodCall() {
this.getMethod() instanceof SensitiveDataMethod
or
// This is particularly to pick up methods with an argument like "password", which
// may indicate a lookup.
exists(string s | this.getAnArgument().(StringLiteral).getValue().toLowerCase() = s |
s.matches(suspicious()) and
not s.matches(nonSuspicious())
)
}
}
/** Access to a variable that might contain sensitive data. */
class SensitiveVarAccess extends SensitiveExpr, VarAccess {
SensitiveVarAccess() {
exists(string s | this.getVariable().getName().toLowerCase() = s |
s.matches(suspicious()) and
not s.matches(nonSuspicious())
)
}
}
/** A method that may produce sensitive data. */
abstract class SensitiveDataMethod extends Method { }
class CredentialsMethod extends SensitiveDataMethod {
CredentialsMethod() {
exists(string s | s = this.getName().toLowerCase() | s.matches(suspicious()))
}
}
/** A method whose execution may be sensitive. */
abstract class SensitiveExecutionMethod extends Method { }
/** A method that may perform authorization. */
class AuthMethod extends SensitiveExecutionMethod {
AuthMethod() {
exists(string s | s = this.getName().toLowerCase() |
s.matches(["%login%", "%auth%"]) and
not s.matches(["get%", "set%", "parse%", "%loginfo%", "remove%", "clean%", "%unauth%"]) and
// exclude "author", but not "authorize" or "authority"
not s.regexpMatch(".*[aA]uthors?([A-Z0-9_].*|$)")
) and
not this.getDeclaringType().getAnAncestor() instanceof TypeException
}
}
/** A method that sends data, and so should not be run conditionally on user input. */
class SendingMethod extends SensitiveExecutionMethod {
SendingMethod() {
exists(string s | s.matches("%Socket") |
this.getDeclaringType().hasQualifiedName("java.net", s) and
this.hasName("send")
)
}
}