Skip to content

Commit

Permalink
Fix #73 Credentials should not be hard-coded(jproperties:S2068) - Add…
Browse files Browse the repository at this point in the history
… the ability to ignore encrypted values
  • Loading branch information
racodond committed Dec 11, 2016
1 parent 4533291 commit 1acbaf0
Show file tree
Hide file tree
Showing 21 changed files with 132 additions and 57 deletions.
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
# Noncompliant {{Remove this hard-coded username.}}
myloginzzz=blabla
hcc.myloginzzz=hcc1

# Noncompliant {{Remove this hard-coded username.}}
username=blabla
hcc.username=hcc2

# Noncompliant {{Remove this hard-coded password.}}
my_password=blabla
hcc.password=hcc13

# Noncompliant {{Remove this hard-coded password.}}
mypasswd=blabla
hcc.mypasswd=hcc4

# Noncompliant {{Remove this hard-coded password.}}
pwd=blabla
hcc.pwd=hcc5

# Compliant
abc=blabla
pass=blabla
hcc.abc=hcc6
hcc.pass=hcc7
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
hccvi.myloginzzz=ENC(hccvi1)
hccvi.username=OBF:hccvi2
hccvi.password=ENC(hccvi3)
hccvi.mypasswd=OBF:hccvi4
hccvi.pwd=OBF:hccvi5
2 changes: 1 addition & 1 deletion its/ruling/tests/src/test/expected/jproperties-S2068.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
'project:custom/hardCodedCredentials.properties':[
'project:custom/hard-coded-credentials/hardCodedCredentials.properties':[
2,
5,
8,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@
'project:custom/emptyElement.properties':[
1,
],
'project:custom/hardCodedCredentials.properties':[
2,
],
'project:custom/indentation/indentation.properties':[
1,
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@
27,
28,
],
'project:custom/hardCodedCredentials.properties':[
8,
],
'project:custom/keyNamingConvention.properties':[
6,
10,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public class ProfileGenerator {

private static Multimap<String, Parameter> parameters = ImmutableListMultimap.<String, Parameter>builder()
.put("maximum-number-keys", new Parameter("numberKeys", "40"))
.put("S2068", new Parameter("encryptedCredentialsToIgnore", "^(ENC\\(|OBF:).+$"))
.build();

public static void generateProfile(Orchestrator orchestrator) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public class CheckUtils {
private CheckUtils() {
}

public static String paramsErrorMessage(Class<? extends JavaPropertiesCheck> clazz, String message) {
public static String paramErrorMessage(Class<? extends JavaPropertiesCheck> clazz, String message) {
return "Check jproperties:" + clazz.getAnnotation(Rule.class).key()
+ " (" + clazz.getAnnotation(Rule.class).name() + "): "
+ message;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public void visitComment(SyntaxTrivia trivia) {
@Override
public void validateParameters() {
if (!Arrays.asList("#", "!").contains(startingCommentToken)) {
throw new IllegalStateException(paramsErrorMessage());
throw new IllegalStateException(paramErrorMessage());
}
}

Expand All @@ -83,8 +83,8 @@ void setStartingCommentToken(String startingCommentToken) {
this.startingCommentToken = startingCommentToken;
}

private String paramsErrorMessage() {
return CheckUtils.paramsErrorMessage(
private String paramErrorMessage() {
return CheckUtils.paramErrorMessage(
this.getClass(),
"startingCommentToken parameter is not valid.\nActual: '" + startingCommentToken + "'\n" + "Expected: '#' or '!'");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,12 @@ public void validateParameters() {
try {
Pattern.compile(regularExpression);
} catch (PatternSyntaxException exception) {
throw new IllegalStateException(paramsErrorMessage(), exception);
throw new IllegalStateException(paramErrorMessage(), exception);
}
}

private String paramsErrorMessage() {
return CheckUtils.paramsErrorMessage(
private String paramErrorMessage() {
return CheckUtils.paramErrorMessage(
this.getClass(),
"regularExpression parameter \"" + regularExpression + "\" is not a valid regular expression.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public void validateParameters() {
try {
Pattern.compile(valuesToIgnore);
} catch (PatternSyntaxException exception) {
throw new IllegalStateException(paramsErrorMessage(), exception);
throw new IllegalStateException(paramErrorMessage(), exception);
}
}

Expand Down Expand Up @@ -127,8 +127,8 @@ private static String valueWithoutLineBreak(String value) {
return value.replaceAll("\\\\\\n\\s*", "");
}

private String paramsErrorMessage() {
return CheckUtils.paramsErrorMessage(
private String paramErrorMessage() {
return CheckUtils.paramErrorMessage(
this.getClass(),
"valuesToIgnore parameter \"" + valuesToIgnore + "\" is not a valid regular expression.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public void visitProperties(PropertiesTree tree) {
@Override
public void validateParameters() {
if (!Arrays.asList("CRLF", "CR", "LF").contains(endLineCharacters)) {
throw new IllegalStateException(paramsErrorMessage());
throw new IllegalStateException(paramErrorMessage());
}
}

Expand All @@ -90,8 +90,8 @@ private boolean fileContainsIllegalEndLineCharacters() {
}
}

private String paramsErrorMessage() {
return CheckUtils.paramsErrorMessage(
private String paramErrorMessage() {
return CheckUtils.paramErrorMessage(
this.getClass(),
"endLineCharacters parameter is not valid.\nActual: '" + endLineCharacters + "'\nExpected: 'CR' or 'CRLF' or 'LF'");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public void validateParameters() {
Pattern.compile(format);
} catch (PatternSyntaxException exception) {
throw new IllegalStateException(
CheckUtils.paramsErrorMessage(this.getClass(), "format parameter \"" + format + "\" is not a valid regular expression."),
CheckUtils.paramErrorMessage(this.getClass(), "format parameter \"" + format + "\" is not a valid regular expression."),
exception);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,20 @@
*/
package org.sonar.jproperties.checks.generic;

import java.util.regex.Pattern;

import com.google.common.annotations.VisibleForTesting;
import org.sonar.check.Priority;
import org.sonar.check.Rule;
import org.sonar.check.RuleProperty;
import org.sonar.jproperties.checks.CheckUtils;
import org.sonar.jproperties.checks.Tags;
import org.sonar.plugins.jproperties.api.tree.KeyTree;
import org.sonar.plugins.jproperties.api.tree.PropertyTree;
import org.sonar.plugins.jproperties.api.visitors.DoubleDispatchVisitorCheck;
import org.sonar.squidbridge.annotations.ActivatedByDefault;
import org.sonar.squidbridge.annotations.SqaleConstantRemediation;

import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

@Rule(
key = "S2068",
name = "Credentials should not be hard-coded",
Expand All @@ -38,18 +42,56 @@
@ActivatedByDefault
public class HardCodedCredentialsCheck extends DoubleDispatchVisitorCheck {

private static final String DEFAULT_ENCRYPTED_CREDENTIALS_TO_IGNORE = "";

private static final Pattern HARD_CODED_USERNAME = Pattern.compile(".*(login|username).*", Pattern.CASE_INSENSITIVE);
private static final Pattern HARD_CODED_PASSWORD = Pattern.compile(".*(password|passwd|pwd).*", Pattern.CASE_INSENSITIVE);

@RuleProperty(
key = "encryptedCredentialsToIgnore",
description = "Regular expression of encrypted credentials to ignore. "
+ "See " + CheckUtils.LINK_TO_JAVA_REGEX_PATTERN_DOC + " for detailed regular expression syntax. "
+ "For example, to ignore encrypted credentials starting with 'ENC(' and 'OBF:', set the parameter to '^(ENC\\(|OBF:).+$'. "
+ "Leave empty if encrypted credentials should not be ignored.",
defaultValue = DEFAULT_ENCRYPTED_CREDENTIALS_TO_IGNORE)
private String encryptedCredentialsToIgnore = DEFAULT_ENCRYPTED_CREDENTIALS_TO_IGNORE;

@Override
public void visitKey(KeyTree tree) {
if (HARD_CODED_USERNAME.matcher(tree.text()).matches()) {
addPreciseIssue(tree, "Remove this hard-coded username.");
public void visitProperty(PropertyTree tree) {
if (tree.value() == null) {
return;
}
if (HARD_CODED_PASSWORD.matcher(tree.text()).matches()) {

if (HARD_CODED_USERNAME.matcher(tree.key().text()).matches()
&& ("".equals(encryptedCredentialsToIgnore) || (!"".equals(encryptedCredentialsToIgnore) && !tree.value().text().matches(encryptedCredentialsToIgnore)))) {
addPreciseIssue(tree, "Remove this hard-coded username.");

} else if (HARD_CODED_PASSWORD.matcher(tree.key().text()).matches()
&& ("".equals(encryptedCredentialsToIgnore) || (!"".equals(encryptedCredentialsToIgnore) && !tree.value().text().matches(encryptedCredentialsToIgnore)))) {
addPreciseIssue(tree, "Remove this hard-coded password.");
}
super.visitKey(tree);
super.visitProperty(tree);

}

@Override
public void validateParameters() {
try {
Pattern.compile(encryptedCredentialsToIgnore);
} catch (PatternSyntaxException exception) {
throw new IllegalStateException(paramErrorMessage(), exception);
}
}

@VisibleForTesting
void setEncryptedCredentialsToIgnore(String encryptedCredentialsToIgnore) {
this.encryptedCredentialsToIgnore = encryptedCredentialsToIgnore;
}

private String paramErrorMessage() {
return CheckUtils.paramErrorMessage(
this.getClass(),
"encryptedCredentialsToIgnore parameter \"" + encryptedCredentialsToIgnore + "\" is not a valid regular expression.");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public void validateParameters() {
try {
Pattern.compile(format);
} catch (PatternSyntaxException exception) {
throw new IllegalStateException(paramsErrorMessage(), exception);
throw new IllegalStateException(paramErrorMessage(), exception);
}
}

Expand All @@ -73,8 +73,8 @@ void setFormat(String format) {
this.format = format;
}

private String paramsErrorMessage() {
return CheckUtils.paramsErrorMessage(
private String paramErrorMessage() {
return CheckUtils.paramErrorMessage(
this.getClass(),
"format parameter \"" + format + "\" is not a valid regular expression.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,12 @@ public void validateParameters() {
try {
Pattern.compile(regularExpression);
} catch (PatternSyntaxException exception) {
throw new IllegalStateException(paramsErrorMessage(), exception);
throw new IllegalStateException(paramErrorMessage(), exception);
}
}

private String paramsErrorMessage() {
return CheckUtils.paramsErrorMessage(
private String paramErrorMessage() {
return CheckUtils.paramErrorMessage(
this.getClass(),
"regularExpression parameter \"" + regularExpression + "\" is not a valid regular expression.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public void visitProperty(PropertyTree tree) {
@Override
public void validateParameters() {
if (!Arrays.asList("=", ":").contains(separator)) {
throw new IllegalStateException(paramsErrorMessage());
throw new IllegalStateException(paramErrorMessage());
}
}

Expand All @@ -98,8 +98,8 @@ void setSeparator(String separator) {
this.separator = separator;
}

private String paramsErrorMessage() {
return CheckUtils.paramsErrorMessage(
private String paramErrorMessage() {
return CheckUtils.paramErrorMessage(
this.getClass(),
"separator parameter is not valid.\nActual: \"" + separator + "\"\nExpected: '=' or ':'");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,12 @@ public void validateParameters() {
try {
Pattern.compile(regularExpression);
} catch (PatternSyntaxException exception) {
throw new IllegalStateException(paramsErrorMessage(), exception);
throw new IllegalStateException(paramErrorMessage(), exception);
}
}

private String paramsErrorMessage() {
return CheckUtils.paramsErrorMessage(
private String paramErrorMessage() {
return CheckUtils.paramErrorMessage(
this.getClass(),
"regularExpression parameter \"" + regularExpression + "\" is not a valid regular expression.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@
This rule flags instances of hard-coded credentials. It checks for keys containing <code>login</code>,
<code>username</code>, <code>password</code>, <code>passwd</code> or <code>pwd</code>.
</p>
<p>
Even if it is not very secure, encrypted credentials can be set in Java .properties files. By default, issues are
raised on those encrypted credentials. But, if you don't want issues to be raised on encrypted credentials, you can
set the <code>encryptedCredentialsToIgnore</code> parameter.
</p>

<h2>Noncompliant Code Example</h2>
<pre>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,37 @@

import org.junit.Test;
import org.sonar.jproperties.checks.CheckTestUtils;
import org.sonar.jproperties.checks.generic.HardCodedCredentialsCheck;
import org.sonar.jproperties.checks.verifier.JavaPropertiesCheckVerifier;

import static org.fest.assertions.Assertions.assertThat;

public class HardCodedCredentialsCheckTest {

@Test
public void should_find_some_hard_coded_credentials_and_raise_some_issues() {
JavaPropertiesCheckVerifier.verify(new HardCodedCredentialsCheck(), CheckTestUtils.getTestFile("hardCodedCredentials.properties"));
JavaPropertiesCheckVerifier.verify(new HardCodedCredentialsCheck(), CheckTestUtils.getTestFile("hard-coded-credentials/hardCodedCredentials.properties"));
}

@Test
public void should_not_find_some_hard_coded_credentials_because_values_to_ignore_are_set() {
HardCodedCredentialsCheck check = new HardCodedCredentialsCheck();
check.setEncryptedCredentialsToIgnore("^(ENC\\(|OBF:).+$");
JavaPropertiesCheckVerifier.verify(check, CheckTestUtils.getTestFile("hard-coded-credentials/hardCodedCredentialsValuesToIgnore.properties"));
}

@Test
public void should_throw_an_illegal_state_exception_as_the_encryptedCredentialsToIgnore_parameter_is_not_a_valid_regular_expression() {
try {
HardCodedCredentialsCheck check = new HardCodedCredentialsCheck();
check.setEncryptedCredentialsToIgnore("(");

JavaPropertiesCheckVerifier.issues(check, CheckTestUtils.getTestFile("hard-coded-credentials/hardCodedCredentials.properties")).noMore();

} catch (IllegalStateException e) {
assertThat(e.getMessage()).isEqualTo("Check jproperties:S2068 (Credentials should not be hard-coded): "
+ "encryptedCredentialsToIgnore parameter \"(\" is not a valid regular expression.");
}
}


}
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
# Noncompliant {{Remove this hard-coded username.}}
myloginzzz=blabla
hcc.myloginzzz=hcc1

# Noncompliant {{Remove this hard-coded username.}}
username=blabla
hcc.username=hcc2

# Noncompliant {{Remove this hard-coded password.}}
my_password=blabla
hcc.password=hcc13

# Noncompliant {{Remove this hard-coded password.}}
mypasswd=blabla
hcc.mypasswd=hcc4

# Noncompliant {{Remove this hard-coded password.}}
pwd=blabla
hcc.pwd=hcc5

# Compliant
abc=blabla
pass=blabla
hcc.abc=hcc6
hcc.pass=hcc7
Loading

0 comments on commit 1acbaf0

Please sign in to comment.