From bcf438249ae7df41764f9ea98f104d6a65709557 Mon Sep 17 00:00:00 2001 From: Tomasz Tylenda Date: Mon, 27 Oct 2025 14:22:11 +0100 Subject: [PATCH 1/5] Fix FN on S2068 repored in USER-1192 --- .../security/HardcodedCredentialsCheck.java | 16 ++++++++++++++++ .../security/HardcodedCredentialsCheckTest.java | 5 +++++ .../app-settings/web.config | 15 +++++++++++++++ 3 files changed, 36 insertions(+) create mode 100644 sonar-xml-plugin/src/test/resources/checks/HardcodedCredentialsCheck/app-settings/web.config diff --git a/sonar-xml-plugin/src/main/java/org/sonar/plugins/xml/checks/security/HardcodedCredentialsCheck.java b/sonar-xml-plugin/src/main/java/org/sonar/plugins/xml/checks/security/HardcodedCredentialsCheck.java index ae2ad6f19..293b90ebc 100644 --- a/sonar-xml-plugin/src/main/java/org/sonar/plugins/xml/checks/security/HardcodedCredentialsCheck.java +++ b/sonar-xml-plugin/src/main/java/org/sonar/plugins/xml/checks/security/HardcodedCredentialsCheck.java @@ -47,6 +47,9 @@ public class HardcodedCredentialsCheck extends SimpleXPathBasedCheck { private static final XPathExpression WEB_CONFIG_CREDENTIALS_PATH = XPathBuilder .forExpression("/configuration/system.web/authentication[@mode=\"Forms\"]/forms/credentials[@passwordFormat=\"Clear\"]/user/@password[string-length(.) > 0]").build(); + private static final XPathExpression WEB_CONFIG_APP_SETTINGS_ADD_PATH = + XPathBuilder.forExpression("//appSettings/add").build(); + private static final Pattern VALID_CREDENTIAL_VALUES = Pattern.compile("[\\{$#]\\{"); private static final Pattern VALID_WEB_CONFIG_CREDENTIAL_VALUES = Pattern.compile("^__.*__$"); @@ -80,6 +83,9 @@ public void scanFile(XmlFile file) { evaluateAsList(WEB_CONFIG_CREDENTIALS_PATH, file.getDocument()).stream() .filter(passwordAttrNode -> !isValidWebConfigCredential(passwordAttrNode.getNodeValue())) .forEach(this::reportIssue); + evaluateAsList(WEB_CONFIG_APP_SETTINGS_ADD_PATH, file.getDocument()).stream() + .filter(HardcodedCredentialsCheck::isAddWithPassword) + .forEach(this::reportIssue); } else { checkElements(file.getDocument()); checkSpecialCases(file); @@ -152,6 +158,16 @@ private static boolean isValidWebConfigCredential(String candidate) { return isValidCredential(candidate) || VALID_WEB_CONFIG_CREDENTIAL_VALUES.matcher(candidate).matches(); } + /** Detects nodes with 'key="password"' and 'value' attributes. */ + private static boolean isAddWithPassword(Node node) { + NamedNodeMap attributes = node.getAttributes(); + Optional keyValueLowerCase = + Optional.ofNullable(attributes.getNamedItem("key")) + .map(Node::getNodeValue) + .map(String::toLowerCase); + return keyValueLowerCase.equals(Optional.of("password")) && attributes.getNamedItem("value") != null; + } + private void checkSpecialCases(XmlFile file) { specialCases.forEach(specialCase -> specialCase.accept(file)); } diff --git a/sonar-xml-plugin/src/test/java/org/sonar/plugins/xml/checks/security/HardcodedCredentialsCheckTest.java b/sonar-xml-plugin/src/test/java/org/sonar/plugins/xml/checks/security/HardcodedCredentialsCheckTest.java index b7bb4e138..322ee743d 100644 --- a/sonar-xml-plugin/src/test/java/org/sonar/plugins/xml/checks/security/HardcodedCredentialsCheckTest.java +++ b/sonar-xml-plugin/src/test/java/org/sonar/plugins/xml/checks/security/HardcodedCredentialsCheckTest.java @@ -75,4 +75,9 @@ void web_application() { SonarXmlCheckVerifier.verifyIssues(Paths.get("web-application", "web.config").toString(), CHECK); SonarXmlCheckVerifier.verifyIssues(Paths.get("web-application", "Machine.config").toString(), CHECK); } + + @Test + void web_application_app_settings() { + SonarXmlCheckVerifier.verifyIssues(Paths.get("app-settings", "web.config").toString(), CHECK); + } } diff --git a/sonar-xml-plugin/src/test/resources/checks/HardcodedCredentialsCheck/app-settings/web.config b/sonar-xml-plugin/src/test/resources/checks/HardcodedCredentialsCheck/app-settings/web.config new file mode 100644 index 000000000..b3e6551b0 --- /dev/null +++ b/sonar-xml-plugin/src/test/resources/checks/HardcodedCredentialsCheck/app-settings/web.config @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + From a5479b9e544282ce9468cd0d8dbd9189548f2903 Mon Sep 17 00:00:00 2001 From: Tomasz Tylenda Date: Mon, 24 Nov 2025 14:30:32 +0100 Subject: [PATCH 2/5] VALUE --- .../plugins/xml/checks/security/HardcodedCredentialsCheck.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sonar-xml-plugin/src/main/java/org/sonar/plugins/xml/checks/security/HardcodedCredentialsCheck.java b/sonar-xml-plugin/src/main/java/org/sonar/plugins/xml/checks/security/HardcodedCredentialsCheck.java index 293b90ebc..26aa13d78 100644 --- a/sonar-xml-plugin/src/main/java/org/sonar/plugins/xml/checks/security/HardcodedCredentialsCheck.java +++ b/sonar-xml-plugin/src/main/java/org/sonar/plugins/xml/checks/security/HardcodedCredentialsCheck.java @@ -165,7 +165,7 @@ private static boolean isAddWithPassword(Node node) { Optional.ofNullable(attributes.getNamedItem("key")) .map(Node::getNodeValue) .map(String::toLowerCase); - return keyValueLowerCase.equals(Optional.of("password")) && attributes.getNamedItem("value") != null; + return keyValueLowerCase.equals(Optional.of("password")) && attributes.getNamedItem(VALUE) != null; } private void checkSpecialCases(XmlFile file) { From af0151f6d437e5a3d2ceb03137b20cb8559bd284 Mon Sep 17 00:00:00 2001 From: tomasz-tylenda-sonarsource Date: Tue, 25 Nov 2025 17:28:17 +0100 Subject: [PATCH 3/5] Update sonar-xml-plugin/src/test/resources/checks/HardcodedCredentialsCheck/app-settings/web.config Co-authored-by: Pavel Mikula <57188685+pavel-mikula-sonarsource@users.noreply.github.com> --- .../checks/HardcodedCredentialsCheck/app-settings/web.config | 5 ----- 1 file changed, 5 deletions(-) diff --git a/sonar-xml-plugin/src/test/resources/checks/HardcodedCredentialsCheck/app-settings/web.config b/sonar-xml-plugin/src/test/resources/checks/HardcodedCredentialsCheck/app-settings/web.config index b3e6551b0..499b4ab4d 100644 --- a/sonar-xml-plugin/src/test/resources/checks/HardcodedCredentialsCheck/app-settings/web.config +++ b/sonar-xml-plugin/src/test/resources/checks/HardcodedCredentialsCheck/app-settings/web.config @@ -1,10 +1,5 @@ - - - - - From 307febeb2cf485ff3ef7e1218ec0b469eebd0328 Mon Sep 17 00:00:00 2001 From: Tomasz Tylenda Date: Tue, 25 Nov 2025 17:30:47 +0100 Subject: [PATCH 4/5] * Location on entire node. * More tests. * Message. --- .../security/HardcodedCredentialsCheck.java | 18 +++++++++++------- .../app-settings/web.config | 14 +++++++++++--- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/sonar-xml-plugin/src/main/java/org/sonar/plugins/xml/checks/security/HardcodedCredentialsCheck.java b/sonar-xml-plugin/src/main/java/org/sonar/plugins/xml/checks/security/HardcodedCredentialsCheck.java index 26aa13d78..2d68559b4 100644 --- a/sonar-xml-plugin/src/main/java/org/sonar/plugins/xml/checks/security/HardcodedCredentialsCheck.java +++ b/sonar-xml-plugin/src/main/java/org/sonar/plugins/xml/checks/security/HardcodedCredentialsCheck.java @@ -84,8 +84,8 @@ public void scanFile(XmlFile file) { .filter(passwordAttrNode -> !isValidWebConfigCredential(passwordAttrNode.getNodeValue())) .forEach(this::reportIssue); evaluateAsList(WEB_CONFIG_APP_SETTINGS_ADD_PATH, file.getDocument()).stream() - .filter(HardcodedCredentialsCheck::isAddWithPassword) - .forEach(this::reportIssue); + .filter(this::isAddWithPassword) + .forEach(node -> reportIssue(node, "Review the hard-coded credential, which may be sensitive.")); } else { checkElements(file.getDocument()); checkSpecialCases(file); @@ -158,14 +158,18 @@ private static boolean isValidWebConfigCredential(String candidate) { return isValidCredential(candidate) || VALID_WEB_CONFIG_CREDENTIAL_VALUES.matcher(candidate).matches(); } - /** Detects nodes with 'key="password"' and 'value' attributes. */ - private static boolean isAddWithPassword(Node node) { + /** + * Detects nodes with 'key="password"' and 'value' attributes. + */ + private boolean isAddWithPassword(Node node) { NamedNodeMap attributes = node.getAttributes(); Optional keyValueLowerCase = Optional.ofNullable(attributes.getNamedItem("key")) - .map(Node::getNodeValue) - .map(String::toLowerCase); - return keyValueLowerCase.equals(Optional.of("password")) && attributes.getNamedItem(VALUE) != null; + .map(Node::getNodeValue) + .map(String::toLowerCase); + + boolean keyIsCredentialWord = keyValueLowerCase.map(credentialWordsSet()::contains).orElse(false); + return keyIsCredentialWord && attributes.getNamedItem(VALUE) != null; } private void checkSpecialCases(XmlFile file) { diff --git a/sonar-xml-plugin/src/test/resources/checks/HardcodedCredentialsCheck/app-settings/web.config b/sonar-xml-plugin/src/test/resources/checks/HardcodedCredentialsCheck/app-settings/web.config index 499b4ab4d..d251a35d9 100644 --- a/sonar-xml-plugin/src/test/resources/checks/HardcodedCredentialsCheck/app-settings/web.config +++ b/sonar-xml-plugin/src/test/resources/checks/HardcodedCredentialsCheck/app-settings/web.config @@ -1,10 +1,18 @@ - + + - + + - + + + + + + + From 7765ddbc7a8c1b5bf3f5e3f5cedebc9fac072437 Mon Sep 17 00:00:00 2001 From: Tomasz Tylenda Date: Wed, 26 Nov 2025 14:37:29 +0100 Subject: [PATCH 5/5] flatten comment --- .../xml/checks/security/HardcodedCredentialsCheck.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sonar-xml-plugin/src/main/java/org/sonar/plugins/xml/checks/security/HardcodedCredentialsCheck.java b/sonar-xml-plugin/src/main/java/org/sonar/plugins/xml/checks/security/HardcodedCredentialsCheck.java index 2d68559b4..5dc463057 100644 --- a/sonar-xml-plugin/src/main/java/org/sonar/plugins/xml/checks/security/HardcodedCredentialsCheck.java +++ b/sonar-xml-plugin/src/main/java/org/sonar/plugins/xml/checks/security/HardcodedCredentialsCheck.java @@ -158,9 +158,7 @@ private static boolean isValidWebConfigCredential(String candidate) { return isValidCredential(candidate) || VALID_WEB_CONFIG_CREDENTIAL_VALUES.matcher(candidate).matches(); } - /** - * Detects nodes with 'key="password"' and 'value' attributes. - */ + /** Detects nodes with 'key="password"' and 'value' attributes. */ private boolean isAddWithPassword(Node node) { NamedNodeMap attributes = node.getAttributes(); Optional keyValueLowerCase =