From d6658b5c4aeaa394fec1063e212c94bc304541a4 Mon Sep 17 00:00:00 2001 From: Pierre-Loup Date: Thu, 2 Jul 2026 16:40:10 +0200 Subject: [PATCH] S2068, S6418, and S6437: Make use of common secret exclusion filter --- .../resources/autoscan/diffs/diff_S6437.json | 2 +- .../jboss-ejb3-tutorial/java-S2068.json | 5 - .../resources/regex-examples/java-S2068.json | 6 +- java-checks-aws/pom.xml | 4 + ...dCodedCredentialsShouldNotBeUsedCheck.java | 6 + ...CredentialsShouldNotBeUsedCheckSample.java | 101 ++++++---- .../checks/HardCodedPasswordCheckSample.java | 4 +- .../checks/HardCodedPasswordCheckCustom.java | 41 +++-- .../checks/HardCodedPasswordCheckSample.java | 170 ++++++++++------- .../checks/HardCodedSecretCheckCustom.java | 7 +- .../checks/HardCodedSecretCheckSample.java | 174 +++++++++--------- java-checks/pom.xml | 4 + .../AbstractHardCodedCredentialChecker.java | 20 +- .../java/checks/HardCodedPasswordCheck.java | 22 ++- .../java/checks/HardCodedSecretCheck.java | 11 +- pom.xml | 2 +- 16 files changed, 312 insertions(+), 267 deletions(-) delete mode 100644 its/ruling/src/test/resources/jboss-ejb3-tutorial/java-S2068.json diff --git a/its/autoscan/src/test/resources/autoscan/diffs/diff_S6437.json b/its/autoscan/src/test/resources/autoscan/diffs/diff_S6437.json index 608dbff770f..0962a59b2b9 100644 --- a/its/autoscan/src/test/resources/autoscan/diffs/diff_S6437.json +++ b/its/autoscan/src/test/resources/autoscan/diffs/diff_S6437.json @@ -1,6 +1,6 @@ { "ruleKey": "S6437", "hasTruePositives": true, - "falseNegatives": 63, + "falseNegatives": 62, "falsePositives": 0 } diff --git a/its/ruling/src/test/resources/jboss-ejb3-tutorial/java-S2068.json b/its/ruling/src/test/resources/jboss-ejb3-tutorial/java-S2068.json deleted file mode 100644 index 28e6d4dee9d..00000000000 --- a/its/ruling/src/test/resources/jboss-ejb3-tutorial/java-S2068.json +++ /dev/null @@ -1,5 +0,0 @@ -{ -"jboss-ejb3-tutorial:interceptor/src/org/jboss/tutorial/interceptor/bean/EmailSystemBean.java": [ -49 -] -} diff --git a/its/ruling/src/test/resources/regex-examples/java-S2068.json b/its/ruling/src/test/resources/regex-examples/java-S2068.json index 9e11e919170..68d1fe631f6 100644 --- a/its/ruling/src/test/resources/regex-examples/java-S2068.json +++ b/its/ruling/src/test/resources/regex-examples/java-S2068.json @@ -1,9 +1,5 @@ { -"org.regex-examples:regex-examples:src/main/java/org/regex/examples/RegexDatabase1.java": [ -749 -], "org.regex-examples:regex-examples:src/main/java/org/regex/examples/RegexDatabase8.java": [ -727, -1257 +727 ] } diff --git a/java-checks-aws/pom.xml b/java-checks-aws/pom.xml index 66175d83e48..08e5bc28e67 100644 --- a/java-checks-aws/pom.xml +++ b/java-checks-aws/pom.xml @@ -62,6 +62,10 @@ commons-io test + + org.sonarsource.analyzer-commons + sonar-analyzer-commons + org.sonarsource.analyzer-commons sonar-analyzer-recognizers diff --git a/java-checks-aws/src/main/java/org/sonar/java/checks/security/HardCodedCredentialsShouldNotBeUsedCheck.java b/java-checks-aws/src/main/java/org/sonar/java/checks/security/HardCodedCredentialsShouldNotBeUsedCheck.java index 4e3046bf7bc..3a463607b95 100644 --- a/java-checks-aws/src/main/java/org/sonar/java/checks/security/HardCodedCredentialsShouldNotBeUsedCheck.java +++ b/java-checks-aws/src/main/java/org/sonar/java/checks/security/HardCodedCredentialsShouldNotBeUsedCheck.java @@ -28,7 +28,9 @@ import org.sonar.java.annotations.VisibleForTesting; import org.sonar.java.checks.helpers.CredentialMethod; import org.sonar.java.checks.helpers.CredentialMethodsLoader; +import org.sonar.java.checks.helpers.ExpressionsHelper; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; +import org.sonarsource.analyzer.commons.appsec.SecretClassifier; import org.sonar.plugins.java.api.JavaFileScannerContext; import org.sonar.plugins.java.api.semantic.MethodMatchers; import org.sonar.plugins.java.api.tree.Arguments; @@ -108,6 +110,10 @@ private void checkArguments(Arguments arguments, CredentialMethod method) { ExpressionTree argument = arguments.get(targetArgumentIndex); var secondaryLocations = new ArrayList(); if (isExpressionDerivedFromPlainText(argument, secondaryLocations, new HashSet<>())) { + String value = ExpressionsHelper.getConstantValueAsString(argument).value(); + if (value != null && SecretClassifier.isKnownNonSecret(value)) { + continue; + } reportIssue(argument, ISSUE_MESSAGE, secondaryLocations, null); } } diff --git a/java-checks-test-sources/aws/src/main/java/checks/security/HardCodedCredentialsShouldNotBeUsedCheckSample.java b/java-checks-test-sources/aws/src/main/java/checks/security/HardCodedCredentialsShouldNotBeUsedCheckSample.java index 05196aa4f6d..8db986123e4 100644 --- a/java-checks-test-sources/aws/src/main/java/checks/security/HardCodedCredentialsShouldNotBeUsedCheckSample.java +++ b/java-checks-test-sources/aws/src/main/java/checks/security/HardCodedCredentialsShouldNotBeUsedCheckSample.java @@ -32,10 +32,11 @@ import static java.lang.System.getProperty; public class HardCodedCredentialsShouldNotBeUsedCheckSample { - static final String FINAL_SECRET_STRING = "hunter2"; -// ^^^^^^^^^> - static final byte[] FINAL_SECRET_BYTE_ARRAY = FINAL_SECRET_STRING.getBytes(StandardCharsets.UTF_8); -// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^> + static final String FINAL_SECRET_STRING = "hunter2"; // Compliant, fake value filter + static final String FINAL_SECRET_STRING_2 = "xvxf6_gaa"; +// ^^^^^^^^^^^> + static final byte[] FINAL_SECRET_BYTE_ARRAY = FINAL_SECRET_STRING_2.getBytes(StandardCharsets.UTF_8); +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^> private static String secretStringField = "hunter2"; private static byte[] secretByteArrayField = new byte[]{0xC, 0xA, 0xF, 0xE}; private static char[] secretCharArrayField = new char[]{0xC, 0xA, 0xF, 0xE}; @@ -54,39 +55,44 @@ public static void nonCompliant(byte[] message, boolean condition, Charset encod // ^^^ SHA256.getHMAC(effectivelyConstantString.getBytes(), message); // Noncompliant SHA256.getHMAC("anotherS3cr37".getBytes(), message); // Noncompliant + // FINAL_SECRET_STRING.getBytes() cannot be resolved to the value "hunter2" that should be filtered -> FPs SHA256.getHMAC(FINAL_SECRET_STRING.getBytes(), message); // Noncompliant SHA256.getHMAC(FINAL_SECRET_STRING.getBytes(StandardCharsets.UTF_8), message); // Noncompliant SHA256.getHMAC(FINAL_SECRET_STRING.getBytes("UTF-8"), message); // Noncompliant SHA256.getHMAC((FINAL_SECRET_STRING).getBytes("UTF-8"), message); // Noncompliant - SHA256.getHMAC(new byte[]{(byte) 0xC, (byte) 0xA, (byte) 0xF, (byte) 0xE}, message); // Noncompliant + // FP should be filtered as short string value + SHA256.getHMAC(new byte[]{(byte) 0xC, (byte) 0xA, (byte) 0xF, (byte) 0xE}, message); // Noncompliant // String based HttpServletRequest request = new HttpServletRequestWrapper(null); - request.login("user", "password"); // Noncompliant + request.login("user", "password"); // Compliant, fake value filter + request.login("user", "xvxf6_gaa"); // Noncompliant request.login("user", effectivelyConstantString); // Noncompliant // ^^^^^^^^^^^^^^^^^^^^^^^^^ - request.login("user", FINAL_SECRET_STRING); // Noncompliant -// ^^^^^^^^^^^^^^^^^^^ - String plainTextSecret = new String("BOOM"); -// ^^^^^^^^^^^^^^^^^^> - request.login("user", plainTextSecret); // Noncompliant -// ^^^^^^^^^^^^^^^ + request.login("user", FINAL_SECRET_STRING); // Compliant, fake values filter + request.login("user", FINAL_SECRET_STRING_2); // Noncompliant +// ^^^^^^^^^^^^^^^^^^^^^ + String plainTextSecret = new String("BOOM"); // Compliant, short values filter + String plainTextSecret2 = new String("BOOMBOOM"); +// ^^^^^^^^^^^^^^^^^^^^^^> + request.login("user", plainTextSecret2); // Noncompliant +// ^^^^^^^^^^^^^^^^ request.login("user", new String("secret")); // Noncompliant request.login("user", new String(FINAL_SECRET_BYTE_ARRAY, 0, 7)); // Noncompliant // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@37< -// ^^^^^^^^^@35< +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@38< +// ^^^^^^^^^^^@36< request.login("user", new String(FINAL_SECRET_BYTE_ARRAY, encoding)); // Noncompliant // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@37< -// ^^^^^^^^^@35< +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@38< +// ^^^^^^^^^^^@36< - String conditionalButPredictable = condition ? FINAL_SECRET_STRING : plainTextSecret; -// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^> + String conditionalButPredictable = condition ? FINAL_SECRET_STRING_2 : plainTextSecret2; +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^> request.login("user", conditionalButPredictable); // Noncompliant // ^^^^^^^^^^^^^^^^^^^^^^^^^ -// ^^^^^^^^^@35< -// ^^^^^^^^^^^^^^^^^^@70< +// ^^^^^^^^^^^@36< +// ^^^^^^^^^^^^^^^^^^^^^^@76< request.login("user", Json.MEDIA_TYPE); // Noncompliant // ^^^^^^^^^^^^^^^ String concatenatedPassword = "abc" + true + ":" + 12 + ":" + 43L + ":" + 'a' + ":" + 0.2f + ":" + 0.2d; @@ -95,7 +101,8 @@ public static void nonCompliant(byte[] message, boolean condition, Charset encod // ^^^^^^^^^^^^^^^^^^^^ jakarta.servlet.http.HttpServletRequest requestJakarta = new jakarta.servlet.http.HttpServletRequestWrapper(null); - requestJakarta.login("user", "password"); // Noncompliant + requestJakarta.login("user", "password"); // Compliant + requestJakarta.login("user", "xvxf6_gaa"); // Noncompliant KeyStore store = KeyStore.getInstance(null); store.getKey("", new char[]{0xC, 0xA, 0xF, 0xE}); // Noncompliant @@ -118,30 +125,42 @@ public static void nonCompliant(byte[] message, boolean condition, Charset encod Encryptors.delux(effectivelyConstantString.subSequence(0, effectivelyConstantString.length()), effectivelyConstantString); // Noncompliant // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + // "password".subSequence(0, 0) cannot be resolved to the value "password" that should be filtered -> FPs Encryptors.delux("password".subSequence(0, 0), "salt"); // Noncompliant + // TP + Encryptors.delux("xvxf6_gaa".subSequence(0, 0), "salt"); // Noncompliant - new Pbkdf2PasswordEncoder("secret"); // Noncompliant - new Pbkdf2PasswordEncoder(("secret")); // Noncompliant + new Pbkdf2PasswordEncoder("secret"); // Compliant, fake value filter + new Pbkdf2PasswordEncoder("xvxf6_gaa"); // Noncompliant + new Pbkdf2PasswordEncoder(("xvxf6_gaa")); // Noncompliant + String notInitialized; notInitialized = "abc"; - new Pbkdf2PasswordEncoder(notInitialized); // Noncompliant + new Pbkdf2PasswordEncoder(notInitialized); // Compliant, short value filter + + String notInitialized2; + notInitialized2 = "xvxf6_gaa"; + new Pbkdf2PasswordEncoder(notInitialized2); // Noncompliant - String longString = "abcdefghiklmnop"; + String longString = "abcdefghiklmnop"; // Cpmpliant fake value contains 'abcd' + // longString.substring(2) cannot be resolved to the value "abcdefghiklmnop" that should be filtered -> FPs new Pbkdf2PasswordEncoder(longString.substring(2)); // Noncompliant - new Pbkdf2PasswordEncoder(longString.substring(0, 2)); // Noncompliant - new Pbkdf2PasswordEncoder(longString.trim()); // Noncompliant - new Pbkdf2PasswordEncoder(longString.strip()); // Noncompliant - new Pbkdf2PasswordEncoder(longString.stripIndent()); // Noncompliant - new Pbkdf2PasswordEncoder(longString.stripLeading()); // Noncompliant - new Pbkdf2PasswordEncoder(longString.stripTrailing()); // Noncompliant - new Pbkdf2PasswordEncoder(longString.translateEscapes()); // Noncompliant - new Pbkdf2PasswordEncoder(longString.intern()); // Noncompliant - new Pbkdf2PasswordEncoder(longString.toLowerCase()); // Noncompliant - new Pbkdf2PasswordEncoder(longString.toLowerCase(Locale.ROOT)); // Noncompliant - new Pbkdf2PasswordEncoder(longString.toUpperCase()); // Noncompliant - new Pbkdf2PasswordEncoder(longString.toUpperCase(Locale.ROOT)); // Noncompliant - new Pbkdf2PasswordEncoder(longString.toString()); // Noncompliant + String longString2 = "67uLmZeieGxDtp!cUm324D*7vji294"; + new Pbkdf2PasswordEncoder(longString2.substring(2)); // Noncompliant + new Pbkdf2PasswordEncoder(longString2.substring(0, 2)); // Noncompliant + new Pbkdf2PasswordEncoder(longString2.trim()); // Noncompliant + new Pbkdf2PasswordEncoder(longString2.strip()); // Noncompliant + new Pbkdf2PasswordEncoder(longString2.stripIndent()); // Noncompliant + new Pbkdf2PasswordEncoder(longString2.stripLeading()); // Noncompliant + new Pbkdf2PasswordEncoder(longString2.stripTrailing()); // Noncompliant + new Pbkdf2PasswordEncoder(longString2.translateEscapes()); // Noncompliant + new Pbkdf2PasswordEncoder(longString2.intern()); // Noncompliant + new Pbkdf2PasswordEncoder(longString2.toLowerCase()); // Noncompliant + new Pbkdf2PasswordEncoder(longString2.toLowerCase(Locale.ROOT)); // Noncompliant + new Pbkdf2PasswordEncoder(longString2.toUpperCase()); // Noncompliant + new Pbkdf2PasswordEncoder(longString2.toUpperCase(Locale.ROOT)); // Noncompliant + new Pbkdf2PasswordEncoder(longString2.toString()); // Noncompliant Object stringAsObject = "abc"; new Pbkdf2PasswordEncoder(stringAsObject.toString()); // Noncompliant @@ -149,7 +168,9 @@ public static void nonCompliant(byte[] message, boolean condition, Charset encod java.util.function.Consumer lambda = (arg) -> { new Pbkdf2PasswordEncoder(arg); String variableWithNullOwner = "abc"; - new Pbkdf2PasswordEncoder(variableWithNullOwner); // Noncompliant + new Pbkdf2PasswordEncoder(variableWithNullOwner); // Compliant, short value filter + String variableWithNullOwner2 = "xvxf6_gaa"; + new Pbkdf2PasswordEncoder(variableWithNullOwner2); // Noncompliant }; byte[] secretBytes = FINAL_SECRET_STRING.getBytes("UTF-8"); @@ -163,7 +184,7 @@ public static void nonCompliant(byte[] message, boolean condition, Charset encod .signWith(paremSignatureAlgorithm, secretBytes); // Noncompliant Jwts.builder() - .signWith(SignatureAlgorithm.HS256, FINAL_SECRET_STRING); // Noncompliant + .signWith(SignatureAlgorithm.HS256, FINAL_SECRET_STRING); // Compliant, "hunter2" rejected by the fake value filter Jwts.builder() .signWith(SignatureAlgorithm.HS256, TextCodec.BASE64.encode(FINAL_SECRET_STRING)); // Noncompliant diff --git a/java-checks-test-sources/default/src/main/files/non-compiling/checks/HardCodedPasswordCheckSample.java b/java-checks-test-sources/default/src/main/files/non-compiling/checks/HardCodedPasswordCheckSample.java index 38b75557e2b..703b2d2bf4e 100644 --- a/java-checks-test-sources/default/src/main/files/non-compiling/checks/HardCodedPasswordCheckSample.java +++ b/java-checks-test-sources/default/src/main/files/non-compiling/checks/HardCodedPasswordCheckSample.java @@ -8,8 +8,8 @@ class HardCodedPasswordCheckSample { private void a(char[] pwd, String var) throws SQLException { - MyUnknownClass.myUnknownMethod("password", "xxxxx"); // Noncompliant - MyUnknownClass.myUnknownMethod("other", "xxxxx"); // Compliant + MyUnknownClass.myUnknownMethod("password", "xvxf6_gaa"); // Noncompliant + MyUnknownClass.myUnknownMethod("other", "xvxf6_gaa"); // Compliant } } diff --git a/java-checks-test-sources/default/src/main/java/checks/HardCodedPasswordCheckCustom.java b/java-checks-test-sources/default/src/main/java/checks/HardCodedPasswordCheckCustom.java index e5478812b7a..d5cb02e696b 100644 --- a/java-checks-test-sources/default/src/main/java/checks/HardCodedPasswordCheckCustom.java +++ b/java-checks-test-sources/default/src/main/java/checks/HardCodedPasswordCheckCustom.java @@ -8,35 +8,40 @@ class HardCodedPasswordCheckCustom { String fieldNameWithBazookaInIt; private void a(char[] pwd, String var) { - String variable2 = "login=a&password=xxx"; // Compliant - String variable3 = "login=a&passwd=xxx"; // Compliant - String variable4 = "login=a&pwd=xxx"; // Compliant - String variable5 = "login=a&marmalade=xxx"; // Noncompliant {{'marmalade' detected in this expression, review this potentially hard-coded password.}} -// ^^^^^^^^^^^^^^^^^^^^^^^ - String variable6 = "login=a&bazooka=xxx "; // Noncompliant - - String variableNameWithBazookaInIt = "xxx"; // Noncompliant -// ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - String variableNameWithmarMalAdeInIt = "xxx"; // Noncompliant + String variable2 = "login=a&password=xvxf6_gaa"; // Compliant + String variable3 = "login=a&passwd=xvxf6_gaa"; // Compliant + String variable4 = "login=a&pwd=xvxf6_gaa"; // Compliant + String variable5 = "login=a&marmalade=xxx"; // Compliant, short value filter + String variable5_2 = "login=a&marmalade=xvxf6_gaa"; // Noncompliant {{'marmalade' detected in this expression, review this potentially hard-coded password.}} +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + String variable6 = "login=a&bazooka=xxx"; // Compliant, short value filter + String variable6_2 = "login=a&bazooka=xvxf6_gaa"; // Noncompliant + + String variableNameWithBazookaInIt = "xxx"; // Compliant, short value filter + String variableNameWithBazookaInIt_2 = "xvxf6_gaa"; // Noncompliant // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - String variableNameWithPwdInIt = "xxx"; // Compliant + String variableNameWithmarMalAdeInIt = "xvxf6_gaa"; // Noncompliant +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + String variableNameWithPwdInIt = "xvxf6_gaa"; // Compliant String otherVariableNameWithPasswordInIt; - fieldNameWithPasswordInIt = "xx"; // Compliant - this.fieldNameWithBazookaInIt = "xx"; // Noncompliant + fieldNameWithPasswordInIt = "xx"; + this.fieldNameWithBazookaInIt = "xx"; // Compliant, , short value filter HardCodedPasswordCheckCustom myA = new HardCodedPasswordCheckCustom(); - myA.setProperty("marmalade", "xxxxx"); // Noncompliant - myA.setProperty("passwd", "xxxxx"); //Compliant - myA.setProperty("pwd", "xxxxx"); // Compliant + myA.setProperty("marmalade", "xxxxx"); // Compliant, short value filter + myA.setProperty("marmalade", "xvxf6_gaa"); // Noncompliant + myA.setProperty("marmalade", "marmalade"); // Compliant + myA.setProperty("passwd", "xvxf6_gaa"); //Compliant + myA.setProperty("pwd", "xvxf6_gaa"); // Compliant - new PasswordAuthentication("userName", "1234".toCharArray()); // Compliant, handled by S6437 + new PasswordAuthentication("userName", "xvxf6_gaa".toCharArray()); // Compliant, handled by S6437 new PasswordAuthentication("userName", pwd); // Compliant new PasswordAuthentication("userName", getPwd(var)); // Compliant new PasswordAuthentication("userName", var.toCharArray()); // Compliant - new OtherPasswordAuthentication("userName", "1234".toCharArray()); // Compliant + new OtherPasswordAuthentication("userName", "xvxf6_gaa".toCharArray()); // Compliant } private void setProperty(Object property, Object Value) { } diff --git a/java-checks-test-sources/default/src/main/java/checks/HardCodedPasswordCheckSample.java b/java-checks-test-sources/default/src/main/java/checks/HardCodedPasswordCheckSample.java index 52b0500fe6d..4d4c81b1436 100644 --- a/java-checks-test-sources/default/src/main/java/checks/HardCodedPasswordCheckSample.java +++ b/java-checks-test-sources/default/src/main/java/checks/HardCodedPasswordCheckSample.java @@ -28,30 +28,33 @@ private void a(char[] pwd, String var) throws SQLException { // ========== 1. String literal ========== // The variable name does not influence the issue, only the string is considered. String variable1 = "blabla"; - String variable2 = "login=a&password=xxx"; // Noncompliant {{'password' detected in this expression, review this potentially hard-coded password.}} -// ^^^^^^^^^^^^^^^^^^^^^^ - String variable3 = "login=a&passwd=xxx"; // Noncompliant - String variable4 = "login=a&pwd=xxx"; // Noncompliant + String variable2 = "login=a&password=xxx"; // Compliant, short value filter + String variable2_2 = "login=a&password=xvxf6_gaa"; // Noncompliant {{'password' detected in this expression, review this potentially hard-coded password.}} +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + String variable3 = "login=a&passwd=xxx"; // Compliant: short value filter + String variable3_2 = "login=a&passwd=xvxf6_gaa"; // Noncompliant {{'passwd' detected in this expression, review this potentially hard-coded password.}} + String variable4 = "login=a&pwd=xxx"; // Compliant, short value filter + String variable4_2 = "login=a&pwd=xvxf6_gaa"; // Noncompliant {{'pwd' detected in this expression, review this potentially hard-coded password.}} String variable5 = "login=a&password="; String variable6 = "login=a&password= "; - String params = "user=admin&password=Password123"; // Noncompliant + String params = "user=admin&password=Password123"; // Compliant, value contains the keyword password + String params2 = "user=admin&password=sonar123"; // Noncompliant String sqlserver = "pgsql:host=localhost port=5432 dbname=test user=postgres password=postgres"; // Noncompliant - // Password starting with "?" are ignored - String query1 = "password=?hard-to-find"; // Compliant + String query1 = "password=?hard-to-find"; // Noncompliant String query1_1 = "password=???"; // Compliant // Password starting with "\"" are ignored String query7 = "\"password=\""; // Compliant - // Password starting with ":" are ignored - String query2 = "password=:password"; // Compliant - String query3 = "password=:param"; // Compliant + // Password starting with ":" are reported + String query2 = "password=:password"; // Noncompliant + String query3 = "password=:param"; // Noncompliant // Password containing "%s" are ignored String query5 = "password=%s"; // Compliant String query6 = "password=\"%s\""; // Compliant // Password shorter than 2 characters are ignored String query1_2 = "password=X"; // Compliant - // "anonymous" is explicitly ignored - String query1_3 = "password=anonymous"; // Compliant + // "anonymous" is not explicitly ignored any more + String query1_3 = "password=anonymous"; // Noncompliant // Not hardcoded String query4 = "password='" + pwd + "'"; // Compliant @@ -60,22 +63,25 @@ private void a(char[] pwd, String var) throws SQLException { // Password are correctly extracted String query10 = "password=something&user=user"; // Noncompliant - String query11 = "password=anonymous&user=user"; // Compliant, the password is the excluded "anonymous" and not "anonymous&user=user" - String query17 = "password=anonymous,user=user"; // Compliant - String query18 = "password=anonymous|user=user"; // Compliant - String query12 = "password=anonymous user=user"; // Compliant - String query121 = "password=anonymous;user=user"; // Compliant - String query122 = "password=anonymous#user=user"; // Compliant - String query13 = "password=anonymous\tuser=user"; // Compliant - String query14 = "password=anonymous\nuser=user"; // Compliant + // "anonymous" is not explicitly ignored any more + String query11 = "password=anonymous&user=user"; // Noncompliant + String query17 = "password=anonymous,user=user"; // Noncompliant + String query18 = "password=anonymous|user=user"; // Noncompliant + String query12 = "password=anonymous user=user"; // Noncompliant + String query121 = "password=anonymous;user=user"; // Noncompliant + String query122 = "password=anonymous#user=user"; // Noncompliant + String query13 = "password=anonymous\tuser=user"; // Noncompliant + String query14 = "password=anonymous\nuser=user"; // Noncompliant String query15 = "password=something&user=user%s"; // Noncompliant - String query16 = "passwordProtected password=:notAPassword"; // Compliant, the password is starting with ":" and therefore excluded + // Password starting with ":" are reported + String query16 = "passwordProtected password=:notAPassword"; // Noncompliant // ========== 1.2 Urls ========== - // No exclusion is made when the password if found in an url + // Exclusions also apply when the password if found in an url String[] urls = { - "http://user:123456@server.com/path", // Noncompliant {{Review this hard-coded URL, which may contain a password.}} -// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + "http://user:123456@server.com/path", // Compliant, fake value filter + "http://user:xvxf6_gaa@server.com/path", // Noncompliant {{Review this hard-coded URL, which may contain a password.}} +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ "ftp://anonymous:anonymous@wustl.edu", // OK, user == password "ftp://:anonymous@wustl.edu", // Noncompliant "http://admin:admin@server.com/path", // OK, user == password @@ -83,7 +89,8 @@ private void a(char[] pwd, String var) throws SQLException { "http://user@server.com/path", // OK, no password "https://server:80/path", // OK, no user and password "http://server.com/path", // OK, no user and password - "http://:123456@server.com/path", // Noncompliant + "http://:123456@server.com/path", // Compliant, fake value filter + "http://:xvxf6_gaa@server.com/path", // Noncompliant "HTTPS://:token0932448209@server.com", // Noncompliant "https://invalid::url::format", "too-long-url-scheme://user:123456@server.com", @@ -91,25 +98,29 @@ private void a(char[] pwd, String var) throws SQLException { // ========== 2. Variable declaration ========== // The variable name should contain a password word - final String MY_PASSWORD = "1234"; // Noncompliant - String variableNameWithPasswordInIt = "xxx"; // Noncompliant {{'Password' detected in this expression, review this potentially hard-coded password.}} -// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - String variableNameWithPassphraseInIt = "xxx"; // Noncompliant {{'Passphrase' detected in this expression, review this potentially hard-coded password.}} -// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - String variableNameWithPasswdInIt = "xxx"; // Noncompliant {{'Passwd' detected in this expression, review this potentially hard-coded password.}} -// ^^^^^^^^^^^^^^^^^^^^^^^^^^ - String variableNameWithPwdInIt = "xxx"; // Noncompliant {{'Pwd' detected in this expression, review this potentially hard-coded password.}} -// ^^^^^^^^^^^^^^^^^^^^^^^ + final String MY_PASSWORD = "1234"; // Compliant, fake value filter + final String MY_PASSWORD_2 = "xvxf6_gaa"; // Noncompliant + String variableNameWithPasswordInIt = "1234"; // Compliant, fake value filter + String variableNameWithPasswordInIt2 = "xvxf6_gaa"; // Noncompliant {{'Password' detected in this expression, review this potentially hard-coded password.}} +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + String variableNameWithPassphraseInIt = "xxx"; // Compliant, short value filter + String variableNameWithPassphraseInIt2 = "xvxf6_gaa"; // Noncompliant {{'Passphrase' detected in this expression, review this potentially hard-coded password.}} +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + String variableNameWithPasswdInIt = "xxx"; // Compliant, short value filter + String variableNameWithPasswdInIt2 = "xvxf6_gaa"; // Noncompliant {{'Passwd' detected in this expression, review this potentially hard-coded password.}} +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + String variableNameWithPwdInIt = "xxx"; // Compliant, short value filter + String variableNameWithPwdInIt2 = "xvxf6_gaa"; // Noncompliant {{'Pwd' detected in this expression, review this potentially hard-coded password.}} +// ^^^^^^^^^^^^^^^^^^^^^^^^ String passwordToString = "SuperSecure".toString(); // Compliant, FN, but will be reported when we remove "toString()" // Same constraints when "toCharArray" is called on the password char[] passwordToChar = "SuperSecure".toCharArray(); // Noncompliant // ^^^^^^^^^^^^^^ - // Password with less than 2 characters are ignored - String variableNameWithPasswordInItEmpty = ""; - String variableNameWithPasswordInItOneChar = "X"; + // Short passwords are ignored + String variableNameWithPasswordInItEmpty = ""; // Compliant + String variableNameWithPasswordInItOneChar = "X"; // Compliant char[] smallPasswordToChar = "x".toCharArray(); // Compliant - // "anonymous" is explicitly ignored - String variableNameWithPasswordInItAnonymous = "anonymous"; + // When the password contains a password word, we consider it as constant used to avoid duplicated string String passwordConst = "Password"; // Compliant String pwds = "pwd"; // Compliant @@ -131,50 +142,58 @@ private void a(char[] pwd, String var) throws SQLException { // ========== 3. Assignment ========== // The variable name should contain a password word - fieldNameWithPasswordInIt = "xx"; // Noncompliant {{'Password' detected in this expression, review this potentially hard-coded password.}} + + fieldNameWithPasswordInIt = "xx"; // Compliant, short value filter + fieldNameWithPasswordInIt = "xvxf6_gaa"; // Noncompliant {{'Password' detected in this expression, review this potentially hard-coded password.}} // ^^^^^^^^^^^^^^^^^^^^^^^^^ - this.fieldNameWithPasswordInIt = "xx"; // Noncompliant - // Password with less than 2 characters are ignored + this.fieldNameWithPasswordInIt = "xx"; // Compliant, short value filter + this.fieldNameWithPasswordInIt = "xvxf6_gaa"; // Noncompliant + // Short password are ignored fieldNameWithPasswordInIt = "X"; - // "anonymous" is explicitly ignored - fieldNameWithPasswordInIt = "anonymous"; + // "anonymous" is not explicitly ignored any more + fieldNameWithPasswordInIt = "anonymous"; // Noncompliant // Not a hardcoded password fieldNameWithPasswordInIt = retrievePassword(); this.fieldNameWithPasswordInIt = retrievePassword(); // Not a password name variable1 = "xx"; // Same constraint when "toCharArray" is called on the password - char[] passphrase = "whatever".toCharArray(); // Noncompliant - passphrase = "whatever".toCharArray(); // Noncompliant + char[] passphrase = "whatever".toCharArray(); // Compliant, fake value filter + passphrase = "xvxf6_gaa".toCharArray(); // Noncompliant + passphrase = "whatever".toCharArray(); // Compliant, fake value filter + passphrase = "xvxf6_gaa".toCharArray(); // Noncompliant passphrase = PASSED.toCharArray(); // Noncompliant passphrase = "".toCharArray(); passphrase = "X".toCharArray(); - passphrase = "anonymous".toCharArray(); - // Unlike variable declaration, when the password contains a password word, it is not a constant, we still report an issue. - fieldNameWithPasswordInIt = "password"; // Noncompliant + // Contains a fake password keyword: password + fieldNameWithPasswordInIt = "password"; // Compliant // ========== 4. Method invocations ========== // ========== 4.1 Equals ========== // When one side of the equals contains a password word, report an issue - String password = "123"; // Noncompliant - if(password.equals("whatever")) { // Noncompliant -// ^^^^^^^^ + String password = "123"; // Compliant, short value filter + if(password.equals("whatever")) { // Compliant, fake value filter + + } + String password2 = "xvxf6_gaa"; // Noncompliant + if(password2.equals("xvxf6_gaa")) { // Noncompliant +// ^^^^^^^^^ } - if("whatever".equals(password)) { // Noncompliant -// ^^^^^^^^ + if("whatever".equals(password)) { // Compliant, fake value filter + + } + if("xvxf6_gaa".equals(password)) { // Noncompliant +// ^^^^^^^^ } if(PASSED.equals(password)) { // Noncompliant } - // Password with less than 2 characters are ignored + // Short password are ignored if(password.equals("X")) { } if(password.equals("")) { } if("".equals(password)) { } - // "anonymous" is explicitly ignored - if(password.equals("anonymous")) { - } // When the actual password contains a password word, we don't report an issue if(password.equals("password")) { } @@ -197,19 +216,23 @@ private void a(char[] pwd, String var) throws SQLException { conn = DriverManager.getConnection("jdbc:mysql://xxx/", "root", PASSED); // Compliant, handled by S6437 conn = DriverManager.getConnection("jdbc:mysql://xxx/"); // Password not set as argument, but it is still detected in the string itself is detected thanks to (1.) - conn = DriverManager.getConnection("jdbc:db2://myhost:5021/mydb:user=dbadm;password=foo"); // Noncompliant -// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + conn = DriverManager.getConnection("jdbc:db2://myhost:5021/mydb:user=dbadm;password=foo"); // Compliant, short value filter + conn = DriverManager.getConnection("jdbc:db2://myhost:5021/mydb:user=dbadm;password=xvxf6_gaa"); // Noncompliant +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // ========== 4.3 Setting password ========== // When a method call has two arguments potentially containing String, we report an issue the same way we would with a variable declaration A myA = new A(); - myA.setProperty("password", "xxxxx"); // Noncompliant - myA.setProperty("passwd", "xxxxx"); // Noncompliant - myA.setProperty("pwd", "xxxxx"); // Noncompliant - // Password with less than 2 characters are ignored + myA.setProperty("password", "xxxxx"); /// Compliant, short value filter + myA.setProperty("password", "xvxf6_gaa"); // Noncompliant + myA.setProperty("passwd", "xxxxx"); // Compliant, short value filter + myA.setProperty("passwd", "xvxf6_gaa"); // Noncompliant + myA.setProperty("pwd", "xxxxx"); // Compliant, short value filter + myA.setProperty("pwd", "xvxf6_gaa"); // Noncompliant + // Short password are ignored myA.setProperty("pwd", "X"); - // "anonymous" is explicitly ignored - myA.setProperty("pwd", "anonymous"); + // "anonymous" is not explicitly ignored any more + myA.setProperty("pwd", "anonymous"); // Noncompliant // Not hardcoded myA.setProperty("password", new Object()); // We only consider the second argument as containing a password @@ -220,19 +243,23 @@ private void a(char[] pwd, String var) throws SQLException { // Other test cases myA.setProperty(12, "xxxxx"); myA.setProperty(new Object(), new Object()); - myA.setProperty("something", "else").setProperty("password", "xxxxx"); // Noncompliant + myA.setProperty("something", "else").setProperty("password", "xxxxx"); // Compliant, short value filter + myA.setProperty("something", "else").setProperty("password", "xvxf6_gaa"); // Noncompliant // ^^^^^^^^^^^ // Same for code not user defined java.util.Properties props = new java.util.Properties(); - props.put(Context.SECURITY_CREDENTIALS, "1234"); // Noncompliant + props.put(Context.SECURITY_CREDENTIALS, "1234"); // Compliant, short value filter + props.put(Context.SECURITY_CREDENTIALS, "xvxf6_gaa"); // Noncompliant // ^^^ - props.put("java.naming.security.credentials", "1234"); // Noncompliant + props.put("java.naming.security.credentials", "1234"); // Compliant, short value filter + props.put("java.naming.security.credentials", "xvxf6_gaa"); // Noncompliant props.put("password", "whateverpassword"); // Compliant java.util.Hashtable env = new java.util.Hashtable<>(); - env.put(Context.SECURITY_CREDENTIALS, "1234"); // Noncompliant - env.put("", "1234"); + env.put(Context.SECURITY_CREDENTIALS, "1234"); // Compliant, short value filter + env.put(Context.SECURITY_CREDENTIALS, "xvxf6_gaa"); // Noncompliant + env.put("", "xvxf6_gaa"); env.put(Context.SECURITY_CREDENTIALS, ""); env.put("password", "whateverpassword"); // Compliant @@ -242,6 +269,7 @@ private void a(char[] pwd, String var) throws SQLException { // ========== 5. Constructors ========== // Second argument of "PasswordAuthentication" is setting explicitly a password PasswordAuthentication pa; + pa = new PasswordAuthentication("userName", "1234".toCharArray()); // Compliant, handled by S6437 // Not hardcoded pa = new PasswordAuthentication("userName", var.toCharArray()); diff --git a/java-checks-test-sources/default/src/main/java/checks/HardCodedSecretCheckCustom.java b/java-checks-test-sources/default/src/main/java/checks/HardCodedSecretCheckCustom.java index de3033ae536..3ecd72fac3c 100644 --- a/java-checks-test-sources/default/src/main/java/checks/HardCodedSecretCheckCustom.java +++ b/java-checks-test-sources/default/src/main/java/checks/HardCodedSecretCheckCustom.java @@ -2,8 +2,9 @@ class HardCodedSecretCheckCustom { private void test() { - String variable2 = "login=a&secret=aaaaaaaaaaaaaabcdefg"; // Compliant - String variable5 = "login=a&marmalade=aaaaaaaaaaaaaabcdefg"; // Noncompliant {{'marmalade' detected in this expression, review this potentially hard-coded secret.}} -// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + String variable2 = "login=a&secret=sk_live_xf2fh0Hu3LqXlqqUg2DEWhEz"; // Compliant + String variable5 = "login=a&marmalade=aaaaaaaaaaaaaabcdefg"; // Compliant, fake value filter + String variable5_2 = "login=a&marmalade=sk_live_xf2fh0Hu3LqXlqqUg2DEWhEz"; // Noncompliant {{'marmalade' detected in this expression, review this potentially hard-coded secret.}} +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ } } diff --git a/java-checks-test-sources/default/src/main/java/checks/HardCodedSecretCheckSample.java b/java-checks-test-sources/default/src/main/java/checks/HardCodedSecretCheckSample.java index 366b054468e..a05cea71d25 100644 --- a/java-checks-test-sources/default/src/main/java/checks/HardCodedSecretCheckSample.java +++ b/java-checks-test-sources/default/src/main/java/checks/HardCodedSecretCheckSample.java @@ -15,43 +15,43 @@ class HardCodedSecretCheckSample { String fieldNameWithSecretInIt = retrieveSecret(); - private static final String PASSED = "abcdefghijklmnopqrs"; // compliant nothing to do with secrets + private static final String PASSED = "bacdefghijklmnopqrs"; // compliant nothing to do with secrets private static final String EMPTY = ""; private void a(char[] secret, String var) throws SQLException { // ========== 1. String literal ========== // The variable name does not influence the issue, only the string is considered. String variable1 = "blabla"; - String variable2 = "login=a&secret=abcdefghijklmnopqrs"; // Noncompliant {{'secret' detected in this expression, review this potentially hard-coded secret.}} + String variable2 = "login=a&secret=bacdefghijklmnopqrs"; // Noncompliant {{'secret' detected in this expression, review this potentially hard-coded secret.}} // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - String variable3 = "login=a&token=abcdefghijklmnopqrs"; // Noncompliant - String variable4 = "login=a&api_key=abcdefghijklmnopqrs"; // Noncompliant - String variable5 = "login=a&api.key=abcdefghijklmnopqrs"; // Noncompliant - String variable6 = "login=a&api-key=abcdefghijklmnopqrs"; // Noncompliant - String variable7 = "login=a&credential=abcdefghijklmnopqrs"; // Noncompliant - String variable8 = "login=a&auth=abcdefghijklmnopqrs"; // Noncompliant + String variable3 = "login=a&token=bacdefghijklmnopqrs"; // Noncompliant + String variable4 = "login=a&api_key=bacdefghijklmnopqrs"; // Noncompliant + String variable5 = "login=a&api.key=bacdefghijklmnopqrs"; // Noncompliant + String variable6 = "login=a&api-key=bacdefghijklmnopqrs"; // Noncompliant + String variable7 = "login=a&credential=bacdefghijklmnopqrs"; // Noncompliant + String variable8 = "login=a&auth=bacdefghijklmnopqrs"; // Noncompliant String variable9 = "login=a&secret="; String variableA = "login=a&secret= "; - String variableB = "secret=&login=abcdefghijklmnopqrs"; // Compliant + String variableB = "secret=&login=bacdefghijklmnopqrs"; // Compliant String variableC = "Okapi-key=42, Okapia Johnstoni, Forest/Zebra Giraffe"; // Compliant - String variableD = "gran-papi-key=Known by everybody in the world like PWD123456"; // Compliant + String variableD = "gran-papi-key=Known by everybody in the world like PWD213456"; // Compliant String variableE = """ login=a - secret=abcdefghijklmnopqrs + secret=bacdefghijklmnopqrs """; // false-negative, we should support text block lines, report precise location inside String variableF = """ -
+

-
+

"""; // false-negative, we should support text block lines and several issues inside - // Secrets starting with "?", ":", "\"", containing "%s" or with less than 2 characters are ignored - String query1 = "secret=?abcdefghijklmnopqrs"; // Compliant + // Secrets starting with "?", ":", "\"", containing "%s" or with short lenght + String query1 = "secret=?bacdefghijklmnopqrs"; // Noncompliant String query1_1 = "secret=???"; // Compliant String query1_2 = "secret=X"; // Compliant String query1_3 = "secret=anonymous"; // Compliant @@ -62,72 +62,71 @@ private void a(char[] secret, String var) throws SQLException { String query6 = "secret=\"%s\""; // Compliant String query7 = "\"secret=\""; // Compliant - String params1 = "user=admin&secret=Secret0123456789012345678"; // Noncompliant - String params2 = "secret=no\nuser=admin0123456789"; // Compliant - String sqlserver1= "pgsql:host=localhost port=5432 dbname=test user=postgres secret=abcdefghijklmnopqrs"; // Noncompliant - String sqlserver2 = "pgsql:host=localhost port=5432 dbname=test secret=no user=abcdefghijklmnopqrs"; // Compliant + String params1 = "user=admin&secret=Secret1023456789021345678"; // Noncompliant + String params2 = "secret=no\nuser=admin0213456789"; // Compliant + String sqlserver1= "pgsql:host=localhost port=5432 dbname=test user=postgres secret=bacdefghijklmnopqrs"; // Noncompliant + String sqlserver2 = "pgsql:host=localhost port=5432 dbname=test secret=no user=bacdefghijklmnopqrs"; // Compliant // Spaces and & are not included into the token, it shows us the end of the token. - String params3 = "token=abcdefghijklmnopqrs user=admin"; // Noncompliant - String params4 = "token=abcdefghijklmnopqrs&user=admin"; // Noncompliant + String params3 = "token=bacdefghijklmnopqrs user=admin"; // Noncompliant + String params4 = "token=bacdefghijklmnopqrs&user=admin"; // Noncompliant - String params5 = "token=123456&abcdefghijklmnopqrs"; // Compliant, FN, even if "&" is accepted in a password, it also indicates a cut in a string literal - String params6 = "token=123456:abcdefghijklmnopqrs"; // Noncompliant + String params5 = "token=213456&bacdefghijklmnopqrs"; // Compliant, FN, even if "&" is accepted in a password, it also indicates a cut in a string literal + String params6 = "token=213456:bacdefghijklmnopqrs"; // Noncompliant // URLs are reported by S2068 only. String[] urls = { - "http://user:123456@server.com/path", // Compliant + "http://user:213456@server.com/path", // Compliant }; // ========== 2. Variable declaration ========== // The variable name should contain a secret word - final String MY_SECRET = "abcdefghijklmnopqrs"; // Noncompliant - String variableNameWithSecretInIt = "abcdefghijklmnopqrs"; // Noncompliant + final String MY_SECRET = "bacdefghijklmnopqrs"; // Noncompliant + String variableNameWithSecretInIt = "bacdefghijklmnopqrs"; // Noncompliant // ^^^^^^^^^^^^^^^^^^^^^^^^^^ - String variableNameWithSecretaryInIt = "abcdefghijklmnopqrs"; // Noncompliant - String variableNameWithAuthorshipInIt = "abcdefghijklmnopqrs"; // Noncompliant - String variableNameWithTokenInIt = "abcdefghijklmnopqrs"; // Noncompliant - String variableNameWithApiKeyInIt = "abcdefghijklmnopqrs"; // Noncompliant + String variableNameWithSecretaryInIt = "bacdefghijklmnopqrs"; // Noncompliant + String variableNameWithAuthorshipInIt = "bacdefghijklmnopqrs"; // Noncompliant + String variableNameWithTokenInIt = "bacdefghijklmnopqrs"; // Noncompliant + String variableNameWithApiKeyInIt = "bacdefghijklmnopqrs"; // Noncompliant // ^^^^^^^^^^^^^^^^^^^^^^^^^^ - String variableNameWithCredentialInIt = "abcdefghijklmnopqrs"; // Noncompliant + String variableNameWithCredentialInIt = "bacdefghijklmnopqrs"; // Noncompliant // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - String variableNameWithAuthInIt = "abcdefghijklmnopqrs"; // Noncompliant + String variableNameWithAuthInIt = "bacdefghijklmnopqrs"; // Noncompliant // ^^^^^^^^^^^^^^^^^^^^^^^^ - // Secrets with less than 2 characters, explicitly "anonymous", are ignored + // Shtrong strings are ignored String variableNameWithSecretInItEmpty = ""; String variableNameWithSecretInItOneChar = "X"; - String variableNameWithSecretInItAnonymous = "anonymous"; String otherVariableNameWithAuthInIt; // Secret containing words and random characters should be filtered String secret001 = "sk_live_xf2fh0Hu3LqXlqqUg2DEWhEz"; // Noncompliant String secret002 = "examples/commit/16ad89c4172c259f15bce56e"; - String secret003 = "examples/commit/8e1d746900f5411e9700fea0"; // Noncompliant + String secret003 = "examples/commit/8e1d746900f5411e9700fea0"; // Compliant, excluded by the classifier (matches the "example" substring), not by the human-language check String secret004 = "examples/commit/revision/469001e9700fea0"; String secret005 = "xml/src/main/java/org/xwiki/xml/html/file"; - String secret006 = "abcdefghijklmnop"; // Compliant - String secret007 = "abcdefghijklmnopq"; // Noncompliant - String secret008 = "0123456789abcdef0"; // Noncompliant - String secret009 = "012345678901234567890123456789"; // Noncompliant - String secret010 = "abcdefghijklmnopabcdefghijkl"; // Noncompliant - String secret011 = "012345670123456701234567012345"; - String secret012 = "012345678012345678012345678012"; // Noncompliant - String secret013 = "234.167.076.123"; + String secret006 = "bacdefghijklmnop"; // Compliant + String secret007 = "bacdefghijklmnopq"; // Noncompliant + String secret008 = "0213456789bacdef0"; // Noncompliant + String secret009 = "021345678902134567890213456789"; // Noncompliant + String secret010 = "bacdefghijklmnopbacdefghijkl"; // Noncompliant + String secret011 = "021345670213456702134567021345"; + String secret012 = "021345678021345678021345678012"; // Noncompliant + String secret013 = "234.167.076.213"; String ip_secret1 = "bfee:e3e1:9a92:6617:02d5:256a:b87a:fbcc"; // Compliant: ipv6 format String ip_secret2 = "2001:db8:1::ab9:C0A8:102"; // Compliant: ipv6 format String ip_secret3 = "::ab9:C0A8:102"; // Compliant: ipv6 format String secret015 = "org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH"; // Example of Telegram bot token - String secret016 = "bot123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11"; // Noncompliant + String secret016 = "bot213456:BAC-DEF2134ghIkl-zyx57W2v1u213ew11"; // Noncompliant // Secret with "&" - String secret017 = "012&345678012345678012345&678012"; // Noncompliant - String secret018 = "&12&345678012345678012345&67801&"; // Noncompliant + String secret017 = "012&345678021345678021345&678012"; // Noncompliant + String secret018 = "&12&345678021345678021345&67801&"; // Noncompliant // Don't filter when the secret is containing any of the secret word. - String secretConst = "Secret_0123456789012345678"; // Noncompliant - String secrets = "secret_0123456789012345678"; // Noncompliant - final String SECRET = "Secret_0123456789012345678"; // Noncompliant + String secretConst = "Secret_0213456789021345678"; // Noncompliant + String secrets = "secret_0213456789021345678"; // Noncompliant + final String SECRET = "Secret_0213456789021345678"; // Noncompliant // Simple constants will be filtered thanks to the entropy check final String SECRET_INPUT = "[id='secret']"; // Compliant final String SECRET_PROPERTY = "custom.secret"; // Compliant @@ -143,44 +142,39 @@ private void a(char[] secret, String var) throws SQLException { String CA_SECRET = "ca-secret"; // Compliant String caSecret = CA_SECRET; // Compliant - // Backslashes are filtered further: - // \n, \t, \r, \" are excluded - String secretWithBackSlashes = "abcdefghij\nklmnopqrs"; // Compliant - String secretWithBackSlashes2 = "abcdefghij\tklmnopqrs"; // Compliant - String secretWithBackSlashes3 = "abcdefghij\rklmnopqrs"; // Compliant - String secretWithBackSlashes4 = "abcdefghij\"klmnopqrs"; // Compliant - // When the secret is starting or ending with a backslash - String secretWithBackSlashes5 = "\\abcdefghijklmnopqrs"; // Compliant - String secretWithBackSlashes6 = "abcdefghijklmnopqrs\\"; // Compliant - // When the secret is starting with = - String secretWithBackSlashes7 = "=abcdefghijklmnopqrs"; - // = in the middle or end is okay - String secretWithBackSlashes8 = "abcdefghijklmnopqrs="; // Noncompliant - String secretWithBackSlashes9 = "abcdefghijklmnopqrs=="; // Noncompliant - String secretWithBackSlashes10 = "abcdefghij=klmnopqrs"; // Noncompliant - - // Only [a-zA-Z0-9_.+/~$-] are accepted as secrets characters - String OkapiKeyboard = "what a strange QWERTY keyboard for animals"; // Compliant - String OKAPI_KEYBOARD = "what a strange QWERTY keyboard for animals"; // Compliant + // Backslashed, \n, \t, \r, \" not excluded + String secretWithBackSlashes = "bacdefghij\nklmnopqrs"; // Noncompliant + String secretWithBackSlashes2 = "bacdefghij\tklmnopqrs"; // Noncompliant + String secretWithBackSlashes3 = "bacdefghij\rklmnopqrs"; // Noncompliant + String secretWithBackSlashes4 = "bacdefghij\"klmnopqrs"; // Noncompliant + String secretWithBackSlashes5 = "\\bacdefghijklmnopqrs"; // Noncompliant + String secretWithBackSlashes6 = "bacdefghijklmnopqrs\\"; // Noncompliant + // Starting or containing = + String secretWithBackSlashes7 = "=bacdefghijklmnopqrs"; // Noncompliant + String secretWithBackSlashes8 = "bacdefghijklmnopqrs="; // Noncompliant + String secretWithBackSlashes9 = "bacdefghijklmnopqrs=="; // Noncompliant + String secretWithBackSlashes10 = "bacdefghij=klmnopqrs"; // Noncompliant + + // Long strings but low entropy + String OkapiKeyboard = "what a strange keyboard for animals"; // Compliant + String OKAPI_KEYBOARD = "what a strange keyboard for animals"; // Compliant String okApiKeyValue = "Spaces are UNEXPECTED 012 345 678"; // Compliant String tokenism = "(Queen's Partner's Stored Knowledge is a Minimal Sham)"; // Compliant - String tokenWithExcludedCharacters2 = "abcdefghij|klmnopqrs"; // Compliant + String tokenWithExcludedCharacters2 = "bacdefghij|klmnopqrs"; // Noncompliant // ========== 3. Assignment ========== - fieldNameWithSecretInIt = "abcdefghijklmnopqrs"; // Noncompliant - this.fieldNameWithSecretInIt = "abcdefghijklmnopqrs"; // Noncompliant - // Secrets with less than 2 chars are explicitly ignored + fieldNameWithSecretInIt = "bacdefghijklmnopqrs"; // Noncompliant + this.fieldNameWithSecretInIt = "bacdefghijklmnopqrs"; // Noncompliant + // Short lenght strings are explicitly ignored fieldNameWithSecretInIt = "X"; - // "anonymous" is explicitly ignored - fieldNameWithSecretInIt = "anonymous"; // Not hardcoded fieldNameWithSecretInIt = retrieveSecret(); this.fieldNameWithSecretInIt = retrieveSecret(); - variable1 = "abcdefghijklmnopqrs"; + variable1 = "bacdefghijklmnopqrs"; // Same constraints apply to "toCharArray" called on a String - char[] credential = "abcdefghijklmnopqrs".toCharArray(); // Noncompliant - credential = "abcdefghijklmnopqrs".toCharArray(); // Noncompliant + char[] credential = "bacdefghijklmnopqrs".toCharArray(); // Noncompliant + credential = "bacdefghijklmnopqrs".toCharArray(); // Noncompliant credential = PASSED.toCharArray(); // Noncompliant credential = "".toCharArray(); credential = "X".toCharArray(); @@ -188,10 +182,10 @@ private void a(char[] secret, String var) throws SQLException { // ========== 4. Method invocations ========== // ========== 4.1 Equals ========== - String auth = "abcdefghijklmnopqrs"; // Noncompliant - if(auth.equals("abcdefghijklmnopqrs")) { // Noncompliant + String auth = "bacdefghijklmnopqrs"; // Noncompliant + if(auth.equals("bacdefghijklmnopqrs")) { // Noncompliant } - if("abcdefghijklmnopqrs".equals(auth)) { // Noncompliant + if("bacdefghijklmnopqrs".equals(auth)) { // Noncompliant } if(PASSED.equals(auth)) { // Noncompliant } @@ -203,7 +197,7 @@ private void a(char[] secret, String var) throws SQLException { } if("password".equals(auth)) { } - if(auth.equals("password-1234")) { + if(auth.equals("password-2134")) { } if(auth.equals("")) { } @@ -222,21 +216,21 @@ private void a(char[] secret, String var) throws SQLException { // ========== 4.2 Setting secrets ========== // When a method call has two arguments potentially containing String, we report an issue the same way we would with a variable declaration A myA = new A(); - myA.setProperty("secret", "abcdefghijklmnopqrs"); // Noncompliant - myA.setProperty("secretary", "abcdefghijklmnopqrs"); // Compliant - myA.setProperty("token", "abcdefghijklmnopqrs"); // Noncompliant - myA.setProperty("tokenization", "abcdefghijklmnopqrs"); // Compliant - myA.setProperty("api-key", "abcdefghijklmnopqrs"); // Noncompliant - myA.setProperty("okapi-keyboard", "abcdefghijklmnopqrs"); // Compliant + myA.setProperty("secret", "bacdefghijklmnopqrs"); // Noncompliant + myA.setProperty("secretary", "bacdefghijklmnopqrs"); // Compliant + myA.setProperty("token", "bacdefghijklmnopqrs"); // Noncompliant + myA.setProperty("tokenization", "bacdefghijklmnopqrs"); // Compliant + myA.setProperty("api-key", "bacdefghijklmnopqrs"); // Noncompliant + myA.setProperty("okapi-keyboard", "bacdefghijklmnopqrs"); // Compliant myA.setProperty("secret", "X"); myA.setProperty("secret", "anonymous"); myA.setProperty("secret", new Object()); - myA.setProperty("abcdefghijklmnopqrs", "secret"); - myA.setProperty(12, "abcdefghijklmnopqrs"); + myA.setProperty("bacdefghijklmnopqrs", "secret"); + myA.setProperty(12, "bacdefghijklmnopqrs"); myA.setProperty(new Object(), new Object()); myA.setProperty("secret", "secret"); // Compliant myA.setProperty("secret", "auth"); // Compliant - myA.setProperty("something", "else").setProperty("secret", "abcdefghijklmnopqrs"); // Noncompliant + myA.setProperty("something", "else").setProperty("secret", "bacdefghijklmnopqrs"); // Noncompliant // ^^^^^^^^^^^ } diff --git a/java-checks/pom.xml b/java-checks/pom.xml index 130a5762dc5..8183c6303ba 100644 --- a/java-checks/pom.xml +++ b/java-checks/pom.xml @@ -66,6 +66,10 @@ org.sonarsource.analyzer-commons sonar-analyzer-recognizers
+ + org.sonarsource.analyzer-commons + sonar-analyzer-commons + org.apache.commons commons-lang3 diff --git a/java-checks/src/main/java/org/sonar/java/checks/AbstractHardCodedCredentialChecker.java b/java-checks/src/main/java/org/sonar/java/checks/AbstractHardCodedCredentialChecker.java index 14e33e4b38d..6c582d6f1f8 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/AbstractHardCodedCredentialChecker.java +++ b/java-checks/src/main/java/org/sonar/java/checks/AbstractHardCodedCredentialChecker.java @@ -16,10 +16,8 @@ */ package org.sonar.java.checks; -import java.util.Collections; import java.util.List; import java.util.Optional; -import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Stream; @@ -36,10 +34,10 @@ import org.sonar.plugins.java.api.tree.MethodInvocationTree; import org.sonar.plugins.java.api.tree.Tree; import org.sonar.plugins.java.api.tree.VariableTree; +import org.sonarsource.analyzer.commons.appsec.SecretClassifier; public abstract class AbstractHardCodedCredentialChecker extends IssuableSubscriptionVisitor { - private static final Set ALLOW_LIST = Collections.singleton("anonymous"); private static final String JAVA_LANG_STRING = "java.lang.String"; private static final String JAVA_LANG_OBJECT = "java.lang.Object"; @@ -58,8 +56,6 @@ public abstract class AbstractHardCodedCredentialChecker extends IssuableSubscri private List variablePatterns = null; private List literalPatterns = null; - private static final int MINIMUM_CREDENTIAL_LENGTH = 2; - protected abstract String getCredentialWords(); /** @@ -145,7 +141,7 @@ protected void handleStringLiteral(LiteralTree tree) { literalPatterns().map(pattern -> pattern.matcher(cleanedLiteral)) // contains "pwd=" or similar .filter(Matcher::find) - .filter(matcher -> !isExcludedLiteral(matcher.group(2))) + .filter(matcher -> isPotentialCredential(matcher.group(2))) .findAny() .ifPresent(matcher -> report(tree, matcher.group(1))); } @@ -156,16 +152,12 @@ private boolean isPartOfConstantCredentialDeclaration(LiteralTree tree) { return parent != null && parent.is(Tree.Kind.VARIABLE) && isCredentialVariableName(((VariableTree) parent).simpleName()).isPresent(); } - protected boolean isPotentialCredential(String literal) { - String trimmed = literal.trim(); - return trimmed.length() >= MINIMUM_CREDENTIAL_LENGTH && !ALLOW_LIST.contains(trimmed); + protected static boolean isKnownNonSecret(String candidate) { + return SecretClassifier.isKnownNonSecret(candidate); } - private boolean isExcludedLiteral(String followingString) { - return !isPotentialCredential(followingString) - || followingString.startsWith("?") - || followingString.startsWith(":") - || followingString.contains("%s"); + protected boolean isPotentialCredential(String literal) { + return !isKnownNonSecret(literal.trim()); } protected void handleVariable(VariableTree tree) { diff --git a/java-checks/src/main/java/org/sonar/java/checks/HardCodedPasswordCheck.java b/java-checks/src/main/java/org/sonar/java/checks/HardCodedPasswordCheck.java index cdc842b80e1..27efcee8388 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/HardCodedPasswordCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/HardCodedPasswordCheck.java @@ -20,6 +20,7 @@ import java.net.URL; import java.util.Arrays; import java.util.List; +import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.sonar.check.Rule; @@ -75,26 +76,31 @@ public void visitNode(Tree tree) { @Override protected void handleStringLiteral(LiteralTree tree) { String cleanedLiteral = LiteralUtils.trimQuotes(tree.value()); - if (isURLWithCredentials(cleanedLiteral)) { - reportIssue(tree, "Review this hard-coded URL, which may contain a password."); + Optional urlPassword = extractURLPassword(cleanedLiteral); + if (urlPassword.isPresent()) { + if (!isKnownNonSecret(urlPassword.get())) { + reportIssue(tree, "Review this hard-coded URL, which may contain a password."); + } } else { super.handleStringLiteral(tree); } } - private static boolean isURLWithCredentials(String stringLiteral) { - if (URL_PREFIX.matcher(stringLiteral).find()) { + private static Optional extractURLPassword(String url) { + if (URL_PREFIX.matcher(url).find()) { try { - String userInfo = new URL(stringLiteral).getUserInfo(); + String userInfo = new URL(url).getUserInfo(); if (userInfo != null) { Matcher matcher = NON_EMPTY_URL_CREDENTIAL.matcher(userInfo); - return matcher.matches() && !matcher.group("user").equals(matcher.group("password")); + if (matcher.matches() && !matcher.group("user").equals(matcher.group("password"))) { + return Optional.of(matcher.group("password")); + } } } catch (MalformedURLException e) { - // ignore, stringLiteral is not a valid URL + // ignore, url is not a valid URL } } - return false; + return Optional.empty(); } private void handleMethodInvocation(MethodInvocationTree mit) { diff --git a/java-checks/src/main/java/org/sonar/java/checks/HardCodedSecretCheck.java b/java-checks/src/main/java/org/sonar/java/checks/HardCodedSecretCheck.java index fad6dd33138..19cef5bfb1e 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/HardCodedSecretCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/HardCodedSecretCheck.java @@ -39,12 +39,7 @@ public class HardCodedSecretCheck extends AbstractHardCodedCredentialChecker { private static final String DEFAULT_SECRET_WORDS = "api[_.-]?key,auth,credential,secret,token"; private static final String DEFAULT_RANDOMNESS_SENSIBILITY= "5.0"; - private static final int MINIMUM_CREDENTIAL_LENGTH = 17; - private static final String FIRST_ACCEPTED_CHARACTER = "[\\w.+/~$:&-]"; - private static final String FOLLOWING_ACCEPTED_CHARACTER = "[=\\w.+/~$:&-]"; - private static final Pattern SECRET_PATTERN = - Pattern.compile(FIRST_ACCEPTED_CHARACTER + "(" + FOLLOWING_ACCEPTED_CHARACTER + "|\\\\\\\\" + FOLLOWING_ACCEPTED_CHARACTER + ")++"); private static final Pattern IPV_6_PATTERN = Pattern.compile(IP_V6_ALONE); private RandomnessDetector randomnessDetector; @@ -102,11 +97,9 @@ private void handleMethodInvocation(MethodInvocationTree mit) { @Override protected boolean isPotentialCredential(String literal) { - if (literal.length() < MINIMUM_CREDENTIAL_LENGTH || !SECRET_PATTERN.matcher(literal).matches()) { - return false; - } return getRandomnessDetector().isRandom(literal) - && isNotIpV6(literal); + && isNotIpV6(literal) + && !isKnownNonSecret(literal); } private RandomnessDetector getRandomnessDetector() { diff --git a/pom.xml b/pom.xml index 5ac45155d71..58b078af2e5 100644 --- a/pom.xml +++ b/pom.xml @@ -96,7 +96,7 @@ 13.0.0.3026 11.3.0.85510 its/**,java-checks-test-sources/** - 2.22.0.4796 + 2.25.0.4954 6.1.0.3962 1.25.1.3886 -Xmx512m