Skip to content

Commit a9b39ff

Browse files
committed
add weak hash mapping and test
1 parent 2f2bce5 commit a9b39ff

File tree

7 files changed

+342
-0
lines changed

7 files changed

+342
-0
lines changed

core-codemods/src/main/java/io/codemodder/codemods/DefaultCodemods.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ public static List<Class<? extends CodeChanger>> asList() {
9797
SonarSQLInjectionCodemod.class,
9898
SonarSSRFCodemod.class,
9999
SonarUnsafeReflectionRemediationCodemod.class,
100+
SonarWeakHashingAlgorithmCodemod.class,
100101
SonarWeakRandomCodemod.class,
101102
SonarXXECodemod.class,
102103
SQLParameterizerCodemod.class,
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package io.codemodder.codemods.sonar;
2+
3+
import com.github.javaparser.ast.CompilationUnit;
4+
import io.codemodder.*;
5+
import io.codemodder.codetf.DetectorRule;
6+
import io.codemodder.providers.sonar.ProvidedSonarScan;
7+
import io.codemodder.providers.sonar.RuleHotspot;
8+
import io.codemodder.providers.sonar.SonarRemediatingJavaParserChanger;
9+
import io.codemodder.remediation.GenericRemediationMetadata;
10+
import io.codemodder.remediation.Remediator;
11+
import io.codemodder.remediation.weakcrypto.WeakCryptoAlgorithmRemediator;
12+
import io.codemodder.remediation.weakrandom.WeakRandomRemediator;
13+
import io.codemodder.sonar.model.Hotspot;
14+
import io.codemodder.sonar.model.SonarFinding;
15+
16+
import javax.inject.Inject;
17+
import java.util.List;
18+
import java.util.Objects;
19+
import java.util.Optional;
20+
21+
@Codemod(
22+
id = "sonar:java/weak-hash-2245",
23+
reviewGuidance = ReviewGuidance.MERGE_AFTER_CURSORY_REVIEW,
24+
importance = Importance.HIGH,
25+
executionPriority = CodemodExecutionPriority.HIGH)
26+
public final class SonarWeakHashingAlgorithmCodemod extends SonarRemediatingJavaParserChanger {
27+
28+
private final Remediator<Hotspot> remediationStrategy;
29+
private final RuleHotspot issues;
30+
31+
@Inject
32+
public SonarWeakHashingAlgorithmCodemod(
33+
@ProvidedSonarScan(ruleId = "java:S4790") final RuleHotspot hotspots) {
34+
super(GenericRemediationMetadata.WEAK_CRYPTO_ALGORITHM.reporter(), hotspots);
35+
this.issues = Objects.requireNonNull(hotspots);
36+
this.remediationStrategy = new WeakCryptoAlgorithmRemediator<>();
37+
}
38+
39+
@Override
40+
public DetectorRule detectorRule() {
41+
return new DetectorRule(
42+
"java:S4790",
43+
"Using weak hashing algorithms is security-sensitive",
44+
"https://rules.sonarsource.com/java/type/Security%20Hotspot/RSPEC-4790/");
45+
}
46+
47+
@Override
48+
public CodemodFileScanningResult visit(
49+
final CodemodInvocationContext context, final CompilationUnit cu) {
50+
List<Hotspot> issuesForFile = issues.getResultsByPath(context.path());
51+
return remediationStrategy.remediateAll(
52+
cu,
53+
context.path().toString(),
54+
detectorRule(),
55+
issuesForFile,
56+
SonarFinding::getKey,
57+
i -> i.getTextRange() != null ? i.getTextRange().getStartLine() : i.getLine(),
58+
i ->
59+
i.getTextRange() != null
60+
? Optional.of(i.getTextRange().getEndLine())
61+
: Optional.empty(),
62+
i -> Optional.empty());
63+
}
64+
}

core-codemods/src/test/java/io/codemodder/codemods/semgrep/SemgrepMissingSecureFlagCodemodTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@
88
testResourceDir = "semgrep-missing-secure-flag",
99
expectingFixesAtLines = {131},
1010
renameTestFile = "src/main/java/org/owasp/webgoat/lessons/jwt/JWTVotesEndpoint.java",
11+
doRetransformTest = false,
1112
dependencies = {})
1213
final class SemgrepMissingSecureFlagCodemodTest implements CodemodTestMixin {}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package io.codemodder.codemods.sonar;
2+
3+
import io.codemodder.testutils.CodemodTestMixin;
4+
import io.codemodder.testutils.Metadata;
5+
import org.junit.jupiter.api.Nested;
6+
7+
final class SonarWeakHashingAlgorithmCodemodTest {
8+
9+
@Nested
10+
@Metadata(
11+
codemodType = SonarWeakHashingAlgorithmCodemod.class,
12+
testResourceDir = "sonar-weak-hash-4790",
13+
renameTestFile = "src/main/java/org/owasp/webgoat/lessons/cryptography/HashingAssignment.java",
14+
expectingFixesAtLines = {55},
15+
doRetransformTest = false,
16+
dependencies = {})
17+
final class HashingAssignmentTest implements CodemodTestMixin {}
18+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/
3+
*
4+
* Copyright (c) 2002 - 2019 Bruce Mayhew
5+
*
6+
* This program is free software; you can redistribute it and/or modify it under the terms of the
7+
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
8+
* License, or (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
11+
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12+
* General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License along with this program; if
15+
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
16+
* 02111-1307, USA.
17+
*
18+
* Getting Source ==============
19+
*
20+
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.
21+
*/
22+
23+
package org.owasp.webgoat.lessons.cryptography;
24+
25+
import java.security.MessageDigest;
26+
import java.security.NoSuchAlgorithmException;
27+
import java.util.Random;
28+
import javax.servlet.http.HttpServletRequest;
29+
import javax.xml.bind.DatatypeConverter;
30+
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
31+
import org.owasp.webgoat.container.assignments.AssignmentHints;
32+
import org.owasp.webgoat.container.assignments.AttackResult;
33+
import org.springframework.http.MediaType;
34+
import org.springframework.web.bind.annotation.PostMapping;
35+
import org.springframework.web.bind.annotation.RequestMapping;
36+
import org.springframework.web.bind.annotation.RequestParam;
37+
import org.springframework.web.bind.annotation.ResponseBody;
38+
import org.springframework.web.bind.annotation.RestController;
39+
40+
@RestController
41+
@AssignmentHints({"crypto-hashing.hints.1", "crypto-hashing.hints.2"})
42+
public class HashingAssignment extends AssignmentEndpoint {
43+
44+
public static final String[] SECRETS = {"secret", "admin", "password", "123456", "passw0rd"};
45+
46+
@RequestMapping(path = "/crypto/hashing/md5", produces = MediaType.TEXT_HTML_VALUE)
47+
@ResponseBody
48+
public String getMd5(HttpServletRequest request) throws NoSuchAlgorithmException {
49+
50+
String md5Hash = (String) request.getSession().getAttribute("md5Hash");
51+
if (md5Hash == null) {
52+
53+
String secret = SECRETS[new Random().nextInt(SECRETS.length)];
54+
55+
MessageDigest md = MessageDigest.getInstance("SHA-256");
56+
md.update(secret.getBytes());
57+
byte[] digest = md.digest();
58+
md5Hash = DatatypeConverter.printHexBinary(digest).toUpperCase();
59+
request.getSession().setAttribute("md5Hash", md5Hash);
60+
request.getSession().setAttribute("md5Secret", secret);
61+
}
62+
return md5Hash;
63+
}
64+
65+
@RequestMapping(path = "/crypto/hashing/sha256", produces = MediaType.TEXT_HTML_VALUE)
66+
@ResponseBody
67+
public String getSha256(HttpServletRequest request) throws NoSuchAlgorithmException {
68+
69+
String sha256 = (String) request.getSession().getAttribute("sha256");
70+
if (sha256 == null) {
71+
String secret = SECRETS[new Random().nextInt(SECRETS.length)];
72+
sha256 = getHash(secret, "SHA-256");
73+
request.getSession().setAttribute("sha256Hash", sha256);
74+
request.getSession().setAttribute("sha256Secret", secret);
75+
}
76+
return sha256;
77+
}
78+
79+
@PostMapping("/crypto/hashing")
80+
@ResponseBody
81+
public AttackResult completed(
82+
HttpServletRequest request,
83+
@RequestParam String answer_pwd1,
84+
@RequestParam String answer_pwd2) {
85+
86+
String md5Secret = (String) request.getSession().getAttribute("md5Secret");
87+
String sha256Secret = (String) request.getSession().getAttribute("sha256Secret");
88+
89+
if (answer_pwd1 != null && answer_pwd2 != null) {
90+
if (answer_pwd1.equals(md5Secret) && answer_pwd2.equals(sha256Secret)) {
91+
return success(this).feedback("crypto-hashing.success").build();
92+
} else if (answer_pwd1.equals(md5Secret) || answer_pwd2.equals(sha256Secret)) {
93+
return failed(this).feedback("crypto-hashing.oneok").build();
94+
}
95+
}
96+
return failed(this).feedback("crypto-hashing.empty").build();
97+
}
98+
99+
public static String getHash(String secret, String algorithm) throws NoSuchAlgorithmException {
100+
MessageDigest md = MessageDigest.getInstance(algorithm);
101+
md.update(secret.getBytes());
102+
byte[] digest = md.digest();
103+
return DatatypeConverter.printHexBinary(digest).toUpperCase();
104+
}
105+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/
3+
*
4+
* Copyright (c) 2002 - 2019 Bruce Mayhew
5+
*
6+
* This program is free software; you can redistribute it and/or modify it under the terms of the
7+
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
8+
* License, or (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
11+
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12+
* General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License along with this program; if
15+
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
16+
* 02111-1307, USA.
17+
*
18+
* Getting Source ==============
19+
*
20+
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.
21+
*/
22+
23+
package org.owasp.webgoat.lessons.cryptography;
24+
25+
import java.security.MessageDigest;
26+
import java.security.NoSuchAlgorithmException;
27+
import java.util.Random;
28+
import javax.servlet.http.HttpServletRequest;
29+
import javax.xml.bind.DatatypeConverter;
30+
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
31+
import org.owasp.webgoat.container.assignments.AssignmentHints;
32+
import org.owasp.webgoat.container.assignments.AttackResult;
33+
import org.springframework.http.MediaType;
34+
import org.springframework.web.bind.annotation.PostMapping;
35+
import org.springframework.web.bind.annotation.RequestMapping;
36+
import org.springframework.web.bind.annotation.RequestParam;
37+
import org.springframework.web.bind.annotation.ResponseBody;
38+
import org.springframework.web.bind.annotation.RestController;
39+
40+
@RestController
41+
@AssignmentHints({"crypto-hashing.hints.1", "crypto-hashing.hints.2"})
42+
public class HashingAssignment extends AssignmentEndpoint {
43+
44+
public static final String[] SECRETS = {"secret", "admin", "password", "123456", "passw0rd"};
45+
46+
@RequestMapping(path = "/crypto/hashing/md5", produces = MediaType.TEXT_HTML_VALUE)
47+
@ResponseBody
48+
public String getMd5(HttpServletRequest request) throws NoSuchAlgorithmException {
49+
50+
String md5Hash = (String) request.getSession().getAttribute("md5Hash");
51+
if (md5Hash == null) {
52+
53+
String secret = SECRETS[new Random().nextInt(SECRETS.length)];
54+
55+
MessageDigest md = MessageDigest.getInstance("MD5");
56+
md.update(secret.getBytes());
57+
byte[] digest = md.digest();
58+
md5Hash = DatatypeConverter.printHexBinary(digest).toUpperCase();
59+
request.getSession().setAttribute("md5Hash", md5Hash);
60+
request.getSession().setAttribute("md5Secret", secret);
61+
}
62+
return md5Hash;
63+
}
64+
65+
@RequestMapping(path = "/crypto/hashing/sha256", produces = MediaType.TEXT_HTML_VALUE)
66+
@ResponseBody
67+
public String getSha256(HttpServletRequest request) throws NoSuchAlgorithmException {
68+
69+
String sha256 = (String) request.getSession().getAttribute("sha256");
70+
if (sha256 == null) {
71+
String secret = SECRETS[new Random().nextInt(SECRETS.length)];
72+
sha256 = getHash(secret, "SHA-256");
73+
request.getSession().setAttribute("sha256Hash", sha256);
74+
request.getSession().setAttribute("sha256Secret", secret);
75+
}
76+
return sha256;
77+
}
78+
79+
@PostMapping("/crypto/hashing")
80+
@ResponseBody
81+
public AttackResult completed(
82+
HttpServletRequest request,
83+
@RequestParam String answer_pwd1,
84+
@RequestParam String answer_pwd2) {
85+
86+
String md5Secret = (String) request.getSession().getAttribute("md5Secret");
87+
String sha256Secret = (String) request.getSession().getAttribute("sha256Secret");
88+
89+
if (answer_pwd1 != null && answer_pwd2 != null) {
90+
if (answer_pwd1.equals(md5Secret) && answer_pwd2.equals(sha256Secret)) {
91+
return success(this).feedback("crypto-hashing.success").build();
92+
} else if (answer_pwd1.equals(md5Secret) || answer_pwd2.equals(sha256Secret)) {
93+
return failed(this).feedback("crypto-hashing.oneok").build();
94+
}
95+
}
96+
return failed(this).feedback("crypto-hashing.empty").build();
97+
}
98+
99+
public static String getHash(String secret, String algorithm) throws NoSuchAlgorithmException {
100+
MessageDigest md = MessageDigest.getInstance(algorithm);
101+
md.update(secret.getBytes());
102+
byte[] digest = md.digest();
103+
return DatatypeConverter.printHexBinary(digest).toUpperCase();
104+
}
105+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{
2+
"paging": {
3+
"pageIndex": 1,
4+
"pageSize": 100,
5+
"total": 1
6+
},
7+
"hotspots": [
8+
{
9+
"key": "AZPB23cAwGhA7VQ2Ui-j",
10+
"component": "nahsra_WebGoatSonarDemo:src/main/java/org/owasp/webgoat/lessons/cryptography/HashingAssignment.java",
11+
"project": "nahsra_WebGoatSonarDemo",
12+
"securityCategory": "others",
13+
"vulnerabilityProbability": "LOW",
14+
"status": "TO_REVIEW",
15+
"line": 55,
16+
"message": "Make sure this weak hash algorithm is not used in a sensitive context here.",
17+
"assignee": "AYu2RswFLuhbfWU895e4",
18+
"author": "[email protected]",
19+
"creationDate": "2024-12-13T22:06:37+0100",
20+
"updateDate": "2024-12-13T22:09:25+0100",
21+
"textRange": {
22+
"startLine": 55,
23+
"endLine": 55,
24+
"startOffset": 39,
25+
"endOffset": 50
26+
},
27+
"flows": [],
28+
"ruleKey": "java:S4790"
29+
}
30+
],
31+
"components": [
32+
{
33+
"organization": "nahsra",
34+
"key": "nahsra_WebGoatSonarDemo:src/main/java/org/owasp/webgoat/lessons/cryptography/HashingAssignment.java",
35+
"qualifier": "FIL",
36+
"name": "HashingAssignment.java",
37+
"longName": "src/main/java/org/owasp/webgoat/lessons/cryptography/HashingAssignment.java",
38+
"path": "src/main/java/org/owasp/webgoat/lessons/cryptography/HashingAssignment.java"
39+
},
40+
{
41+
"organization": "nahsra",
42+
"key": "nahsra_WebGoatSonarDemo",
43+
"qualifier": "TRK",
44+
"name": "WebGoatSonarDemo",
45+
"longName": "WebGoatSonarDemo"
46+
}
47+
]
48+
}

0 commit comments

Comments
 (0)