Skip to content

Commit a53cbc1

Browse files
committed
Update qldoc and make the query more readable
1 parent d33b04c commit a53cbc1

File tree

4 files changed

+101
-10
lines changed

4 files changed

+101
-10
lines changed

java/ql/src/experimental/Security/CWE/CWE-555/CredentialsInPropertiesFile.qhelp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<p>
1616
Credentials stored in properties files should be encrypted and recycled regularly.
1717
In a Java EE deployment scenario, utilities provided by application servers like
18-
keystore and password vault can be used to encrypt and manage credentials.
18+
keystores and password vaults can be used to encrypt and manage credentials.
1919
</p>
2020
</recommendation>
2121

@@ -27,7 +27,7 @@
2727

2828
<p>
2929
In the second example, the credentials of a LDAP and datasource properties are stored
30-
in the encrypted format.
30+
in an encrypted format.
3131
</p>
3232
<sample src="configuration.properties" />
3333
</example>

java/ql/src/experimental/Security/CWE/CWE-555/CredentialsInPropertiesFile.ql

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,18 @@
99
* external/cwe/cwe-260
1010
*/
1111

12+
/*
13+
* Note this query requires properties files to be indexed before it can produce results.
14+
* If creating your own database with the CodeQL CLI, you should run
15+
* `codeql database index-files --language=properties ...`
16+
* If using lgtm.com, you should add `properties_files: true` to the index block of your
17+
* lgtm.yml file (see https://lgtm.com/help/lgtm/java-extraction)
18+
*/
19+
1220
import java
1321
import semmle.code.configfiles.ConfigFiles
1422

15-
private string suspicious() {
23+
private string possibleSecretName() {
1624
result = "%password%" or
1725
result = "%passwd%" or
1826
result = "%account%" or
@@ -23,7 +31,7 @@ private string suspicious() {
2331
result = "%access%key%"
2432
}
2533

26-
private string nonSuspicious() {
34+
private string possibleEncryptedSecretName() {
2735
result = "%hashed%" or
2836
result = "%encrypted%" or
2937
result = "%crypt%"
@@ -48,7 +56,8 @@ predicate isNotCleartextCredentials(string value) {
4856
or
4957
value.matches("ENC(%)") // Encrypted value
5058
or
51-
value.toLowerCase().matches(suspicious()) // Could be message properties or fake passwords
59+
// Could be a message property for UI display or fake passwords, e.g. login.password_expired=Your current password has expired.
60+
value.toLowerCase().matches(possibleSecretName())
5261
}
5362

5463
/**
@@ -57,8 +66,7 @@ predicate isNotCleartextCredentials(string value) {
5766
* b) with a non-production file name
5867
*/
5968
predicate isNonProdCredentials(CredentialsConfig cc) {
60-
cc.getFile().getAbsolutePath().matches(["%dev%", "%test%", "%sample%"]) and
61-
not cc.getFile().getAbsolutePath().matches("%codeql%") // CodeQL test cases
69+
cc.getFile().getAbsolutePath().matches(["%dev%", "%test%", "%sample%"])
6270
}
6371

6472
/** The properties file with configuration key/value pairs. */
@@ -69,8 +77,8 @@ class ConfigProperties extends ConfigPair {
6977
/** The credentials configuration property. */
7078
class CredentialsConfig extends ConfigProperties {
7179
CredentialsConfig() {
72-
this.getNameElement().getName().trim().toLowerCase().matches(suspicious()) and
73-
not this.getNameElement().getName().trim().toLowerCase().matches(nonSuspicious())
80+
this.getNameElement().getName().trim().toLowerCase().matches(possibleSecretName()) and
81+
not this.getNameElement().getName().trim().toLowerCase().matches(possibleEncryptedSecretName())
7482
}
7583

7684
string getName() { result = this.getNameElement().getName().trim() }
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/**
2+
* @name Cleartext Credentials in Properties File
3+
* @description Finds cleartext credentials in Java properties files.
4+
* @kind problem
5+
* @id java/credentials-in-properties
6+
* @tags security
7+
* external/cwe/cwe-555
8+
* external/cwe/cwe-256
9+
* external/cwe/cwe-260
10+
*/
11+
12+
/*
13+
* Note this query requires properties files to be indexed before it can produce results.
14+
* If creating your own database with the CodeQL CLI, you should run
15+
* `codeql database index-files --language=properties ...`
16+
* If using lgtm.com, you should add `properties_files: true` to the index block of your
17+
* lgtm.yml file (see https://lgtm.com/help/lgtm/java-extraction)
18+
*/
19+
20+
import java
21+
import semmle.code.configfiles.ConfigFiles
22+
23+
private string possibleSecretName() {
24+
result = "%password%" or
25+
result = "%passwd%" or
26+
result = "%account%" or
27+
result = "%accnt%" or
28+
result = "%credential%" or
29+
result = "%token%" or
30+
result = "%secret%" or
31+
result = "%access%key%"
32+
}
33+
34+
private string possibleEncryptedSecretName() {
35+
result = "%hashed%" or
36+
result = "%encrypted%" or
37+
result = "%crypt%"
38+
}
39+
40+
/** Holds if the value is not cleartext credentials. */
41+
bindingset[value]
42+
predicate isNotCleartextCredentials(string value) {
43+
value = "" // Empty string
44+
or
45+
value.length() < 7 // Typical credentials are no less than 6 characters
46+
or
47+
value.matches("% %") // Sentences containing spaces
48+
or
49+
value.regexpMatch(".*[^a-zA-Z\\d]{3,}.*") // Contain repeated non-alphanumeric characters such as a fake password pass**** or ????
50+
or
51+
value.matches("@%") // Starts with the "@" sign
52+
or
53+
value.regexpMatch("\\$\\{.*\\}") // Variable placeholder ${credentials}
54+
or
55+
value.matches("%=") // A basic check of encrypted credentials ending with padding characters
56+
or
57+
value.matches("ENC(%)") // Encrypted value
58+
or
59+
// Could be a message property for UI display or fake passwords, e.g. login.password_expired=Your current password has expired.
60+
value.toLowerCase().matches(possibleSecretName())
61+
}
62+
63+
/** The properties file with configuration key/value pairs. */
64+
class ConfigProperties extends ConfigPair {
65+
ConfigProperties() { this.getFile().getBaseName().toLowerCase().matches("%.properties") }
66+
}
67+
68+
/** The credentials configuration property. */
69+
class CredentialsConfig extends ConfigProperties {
70+
CredentialsConfig() {
71+
this.getNameElement().getName().trim().toLowerCase().matches(possibleSecretName()) and
72+
not this.getNameElement().getName().trim().toLowerCase().matches(possibleEncryptedSecretName())
73+
}
74+
75+
string getName() { result = this.getNameElement().getName().trim() }
76+
77+
string getValue() { result = this.getValueElement().getValue().trim() }
78+
}
79+
80+
from CredentialsConfig cc
81+
where not isNotCleartextCredentials(cc.getValue())
82+
select cc,
83+
"Plaintext credentials " + cc.getName() + " have cleartext value " + cc.getValue() +
84+
" in properties file."

java/ql/test/experimental/query-tests/security/CWE-555/CredentialsInPropertiesFile.qlref

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)