diff --git a/plugin/src/main/java/org/owasp/benchmarkutils/score/BenchmarkScore.java b/plugin/src/main/java/org/owasp/benchmarkutils/score/BenchmarkScore.java index ca832fad..769a3cd7 100644 --- a/plugin/src/main/java/org/owasp/benchmarkutils/score/BenchmarkScore.java +++ b/plugin/src/main/java/org/owasp/benchmarkutils/score/BenchmarkScore.java @@ -387,10 +387,7 @@ public static void main(String[] args) { SecureRandom generator = SecureRandom.getInstance("SHA1PRNG"); while (files.size() > 0) { - int randomNum = generator.nextInt(); - // FIXME: Get Absolute Value better - if (randomNum < 0) randomNum *= -1; - int fileToGet = randomNum % files.size(); + int fileToGet = generator.nextInt(files.size()); File actual = files.remove(fileToGet); // Don't confuse the expected results file as an actual results file if // its in the same directory diff --git a/plugin/src/main/java/org/owasp/benchmarkutils/score/parsers/FaastReader.java b/plugin/src/main/java/org/owasp/benchmarkutils/score/parsers/FaastReader.java index ebbad18a..da9e1f4e 100644 --- a/plugin/src/main/java/org/owasp/benchmarkutils/score/parsers/FaastReader.java +++ b/plugin/src/main/java/org/owasp/benchmarkutils/score/parsers/FaastReader.java @@ -86,8 +86,7 @@ private TestCaseResult parseFaastFinding(JSONObject finding) { } private String getCategory(String url) { - // TODO: Use APPNAME constant rather than 'benchmark' here. - String flag = "benchmark/"; + String flag = BenchmarkScore.TESTSUITENAME.simpleName().toLowerCase() + "/"; int locator_start = url.lastIndexOf(flag) + flag.length(); int locator_end = url.lastIndexOf("/" + BenchmarkScore.TESTCASENAME); return url.substring(locator_start, locator_end); diff --git a/plugin/src/main/java/org/owasp/benchmarkutils/score/parsers/JuliaReader.java b/plugin/src/main/java/org/owasp/benchmarkutils/score/parsers/JuliaReader.java index c18df280..9f999fd9 100644 --- a/plugin/src/main/java/org/owasp/benchmarkutils/score/parsers/JuliaReader.java +++ b/plugin/src/main/java/org/owasp/benchmarkutils/score/parsers/JuliaReader.java @@ -20,7 +20,6 @@ import java.io.StringReader; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; -import org.owasp.benchmarkutils.score.BenchmarkScore; import org.owasp.benchmarkutils.score.ResultFile; import org.owasp.benchmarkutils.score.TestCaseResult; import org.owasp.benchmarkutils.score.TestSuiteResults; @@ -31,11 +30,6 @@ public class JuliaReader extends Reader { - // refactoring resilient - // TODO: Update to handle package paths from other test suites - private final String prefixOfTest = - "org.owasp.benchmark.testcode." + BenchmarkScore.TESTCASENAME; - @Override public boolean canRead(ResultFile resultFile) { return resultFile.filename().endsWith(".xml") diff --git a/plugin/src/main/java/org/owasp/benchmarkutils/score/parsers/KlocworkCSVReader.java b/plugin/src/main/java/org/owasp/benchmarkutils/score/parsers/KlocworkCSVReader.java index 54c4145c..f6d80e80 100644 --- a/plugin/src/main/java/org/owasp/benchmarkutils/score/parsers/KlocworkCSVReader.java +++ b/plugin/src/main/java/org/owasp/benchmarkutils/score/parsers/KlocworkCSVReader.java @@ -91,7 +91,8 @@ private int cweLookup(String checkerKey) { case "RLK.IN": // Input stream not closed on exit case "RLK.OUT": // Output stream not closed on exit - case "SV.DATA.DB": // Data Injection - what does that mean? TODO + case "SV.DATA.DB": + return CweNumber.SQL_INJECTION; case "SV.PASSWD.HC": // Hardcoded Password case "SV.PASSWD.HC.EMPTY": // Empty Password case "SV.PASSWD.PLAIN": // Plain-text Password diff --git a/plugin/src/main/java/org/owasp/benchmarkutils/score/parsers/SemgrepReader.java b/plugin/src/main/java/org/owasp/benchmarkutils/score/parsers/SemgrepReader.java index 9a3dd4e6..be369095 100644 --- a/plugin/src/main/java/org/owasp/benchmarkutils/score/parsers/SemgrepReader.java +++ b/plugin/src/main/java/org/owasp/benchmarkutils/score/parsers/SemgrepReader.java @@ -74,8 +74,6 @@ public static int translate(int cwe) { // Output Used by a Downstream Component ('Injection') case 75: // CWE vuln mapping DISCOURAGED: Failure to Sanitize Special Elements into a // Different Plane (Special Element Injection) - case 77: // Improper Neutralization of Special Elements used in a Command ('Command - // Injection') - TODO: Map to Command Injection? case 91: // XML Injection (aka Blind XPath Injection) case 93: // Improper Neutralization of CRLF Sequences ('CRLF Injection') case 94: // Improper Control of Generation of Code ('Code Injection') - Reported when it @@ -182,8 +180,6 @@ public static int translate(int cwe) { case 776: // XEE: Improper Restriction of Recursive Entity References in DTDs ('XML // Entity Expansion') case 778: // Insufficient Logging - case 780: // Use of RSA Algorithm without OAEP - // TODO: Map to Weak Crypto? case 787: // Out of bounds Write case 798: // Use of Hard-coded Credentials case 837: // Improper Enforcement of a Single, Unique Action @@ -197,16 +193,13 @@ public static int translate(int cwe) { case 926: // Improper Export of Android Application Components case 939: // Improper Authorization in Handler for Custom URL Scheme case 942: // Permissive Cross-domain Policy with Untrusted Domains - case 943: // Improper Neutralization of Special Elements in Data Query Logic - // TODO: Map this as parent of for various Injection flaw CWEs in Benchmark case 1021: // TapJacking: Improper Restriction of Rendered UI Layers or Frames case 1104: // Use of Unmaintained Third Party Components case 1204: // Generation of Weak Initialization Vector (IV) case 1275: // Sensitive Cookie with Improper SameSite Attribute case 1323: // Improper Management of Sensitive Trace Data case 1333: // Inefficient Regular Expression Complexity (e.g., RegexDOS) - case 1336: // Improper Neutralization of Special Elements Used in a Template Engine - // TODO: Map to some type of injection? + case 1336: // Template Engine Injection -- no Benchmark category exists yet case 1390: // Weak Authentication break; // Don't care - So return CWE 'as is' @@ -215,12 +208,14 @@ public static int translate(int cwe) { case 23: // Relative Path Traversal case 35: // Path Traversal: '.../...//' return CweNumber.PATH_TRAVERSAL; + case 77: // Command Injection parent CWE case 78: return CweNumber.COMMAND_INJECTION; case 79: case 80: // Basic XSS return CweNumber.XSS; case 89: + case 943: // Data Query Logic injection return CweNumber.SQL_INJECTION; case 90: return CweNumber.LDAP_INJECTION; @@ -229,6 +224,7 @@ public static int translate(int cwe) { case 329: // Generation of Predictable IV with CBC Mode - Has no affect on Benchmark - // but leaving mapping in anyway case 696: // Incorrect Behavior Order + case 780: // RSA without OAEP return CweNumber.WEAK_CRYPTO_ALGO; // weak encryption case 328: return CweNumber.WEAK_HASH_ALGO; diff --git a/plugin/src/main/java/org/owasp/benchmarkutils/score/report/Formats.java b/plugin/src/main/java/org/owasp/benchmarkutils/score/report/Formats.java index a3bf06e8..969bb66e 100644 --- a/plugin/src/main/java/org/owasp/benchmarkutils/score/report/Formats.java +++ b/plugin/src/main/java/org/owasp/benchmarkutils/score/report/Formats.java @@ -18,11 +18,19 @@ package org.owasp.benchmarkutils.score.report; import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.Locale; public class Formats { - public static final DecimalFormat twoDecimalPlacesPercentage = new DecimalFormat("#0.00%"); + private static final DecimalFormatSymbols US_SYMBOLS = + DecimalFormatSymbols.getInstance(Locale.US); - public static final DecimalFormat singleDecimalPlaceNumber = new DecimalFormat("0.0"); - public static final DecimalFormat fourDecimalPlacesNumber = new DecimalFormat("#0.0000"); + public static final DecimalFormat twoDecimalPlacesPercentage = + new DecimalFormat("#0.00%", US_SYMBOLS); + + public static final DecimalFormat singleDecimalPlaceNumber = + new DecimalFormat("0.0", US_SYMBOLS); + public static final DecimalFormat fourDecimalPlacesNumber = + new DecimalFormat("#0.0000", US_SYMBOLS); } diff --git a/plugin/src/main/java/org/owasp/benchmarkutils/score/report/ScatterVulns.java b/plugin/src/main/java/org/owasp/benchmarkutils/score/report/ScatterVulns.java index cd511a03..6c61248f 100644 --- a/plugin/src/main/java/org/owasp/benchmarkutils/score/report/ScatterVulns.java +++ b/plugin/src/main/java/org/owasp/benchmarkutils/score/report/ScatterVulns.java @@ -341,10 +341,10 @@ private void makeLegend( if (ch == 'Z') ch = 'a'; else ch++; - noncommercialTotalScore += or.getCategoryResults(category).score; - noncommercialTotalPrecision += or.getCategoryResults(category).precision; - noncommercialTotalTPR += or.getCategoryResults(category).truePositiveRate; - noncommercialTotalFPR += or.getCategoryResults(category).falsePositiveRate; + noncommercialTotalScore += or.getCategoryResults(category).score * 100; + noncommercialTotalPrecision += or.getCategoryResults(category).precision * 100; + noncommercialTotalTPR += or.getCategoryResults(category).truePositiveRate * 100; + noncommercialTotalFPR += or.getCategoryResults(category).falsePositiveRate * 100; double score = or.getCategoryResults(category).score * 100; if (score < noncommercialLow) { diff --git a/plugin/src/main/java/org/owasp/benchmarkutils/score/report/ToolReport.java b/plugin/src/main/java/org/owasp/benchmarkutils/score/report/ToolReport.java index 9f634f44..2c0f71b3 100644 --- a/plugin/src/main/java/org/owasp/benchmarkutils/score/report/ToolReport.java +++ b/plugin/src/main/java/org/owasp/benchmarkutils/score/report/ToolReport.java @@ -166,8 +166,6 @@ else if (r.truePositiveRate > .7 && r.falsePositiveRate < .3) CategoryResults currentCategoryResults = overallAveToolResults.get(categoryName); // default value hard spaces equal to triangle width String precisionBonus = "    "; - // r.precision has range 0-1, but currentCategoryResults.precision is 1 to 100. - // FIXME: Fix precision calculations so they are the same units double precisionDiff = 100 * r.precision - currentCategoryResults.precision; if (precisionDiff >= 5) precisionBonus = @@ -184,7 +182,6 @@ else if (precisionDiff <= -5) { // default value hard spaces equal to triangle width String fscoreBonus = "    "; - // FIXME: Fix fscore calculations so they are the same units double fscoreDiff = 100 * r.fscore - currentCategoryResults.fscore; if (fscoreDiff >= 5) fscoreBonus = ""; else if (fscoreDiff <= -5) { @@ -198,7 +195,6 @@ else if (fscoreDiff <= -5) { // default value hard spaces equal to triangle width recallBonus = "    "; - // FIXME: Fix truePositiveRate calculations so they are the same units double recallDiff = 100 * r.truePositiveRate - currentCategoryResults.truePositiveRate; if (recallDiff >= 5) recallBonus = ""; diff --git a/plugin/src/test/java/org/owasp/benchmarkutils/score/parsers/KlocworkCSVReaderTest.java b/plugin/src/test/java/org/owasp/benchmarkutils/score/parsers/KlocworkCSVReaderTest.java index 5f9f91df..87c8f997 100644 --- a/plugin/src/test/java/org/owasp/benchmarkutils/score/parsers/KlocworkCSVReaderTest.java +++ b/plugin/src/test/java/org/owasp/benchmarkutils/score/parsers/KlocworkCSVReaderTest.java @@ -52,9 +52,10 @@ void readerHandlesGivenResultFile() throws Exception { assertTrue(result.isCommercial()); assertEquals("Klocwork", result.getToolName()); - assertEquals(2, result.getTotalResults()); + assertEquals(3, result.getTotalResults()); assertEquals(CweNumber.SQL_INJECTION, result.get(1).get(0).getCWE()); assertEquals(CweNumber.PATH_TRAVERSAL, result.get(2).get(0).getCWE()); + assertEquals(CweNumber.SQL_INJECTION, result.get(3).get(0).getCWE()); } } diff --git a/plugin/src/test/java/org/owasp/benchmarkutils/score/parsers/SemgrepReaderTest.java b/plugin/src/test/java/org/owasp/benchmarkutils/score/parsers/SemgrepReaderTest.java index 97f2537c..b51d3a28 100644 --- a/plugin/src/test/java/org/owasp/benchmarkutils/score/parsers/SemgrepReaderTest.java +++ b/plugin/src/test/java/org/owasp/benchmarkutils/score/parsers/SemgrepReaderTest.java @@ -76,9 +76,44 @@ void readerHandlesGivenResultFileInV121() throws Exception { assertFalse(result.isCommercial()); assertEquals("Semgrep", result.getToolName()); - assertEquals(2, result.getTotalResults()); + assertEquals(5, result.getTotalResults()); assertEquals(CweNumber.COMMAND_INJECTION, result.get(3).get(0).getCWE()); assertEquals(CweNumber.COOKIE_WITHOUT_HTTPONLY, result.get(4).get(0).getCWE()); + assertEquals(CweNumber.COMMAND_INJECTION, result.get(5).get(0).getCWE()); + assertEquals(CweNumber.WEAK_CRYPTO_ALGO, result.get(6).get(0).getCWE()); + assertEquals(CweNumber.SQL_INJECTION, result.get(7).get(0).getCWE()); + } + + @Test + void translateCwe77ReturnsCommandInjection() { + assertEquals( + CweNumber.COMMAND_INJECTION, + SemgrepReader.translate(77), + "CWE-77 (Command Injection parent) should map to 78 (COMMAND_INJECTION)"); + } + + @Test + void translateCwe780ReturnsWeakCrypto() { + assertEquals( + CweNumber.WEAK_CRYPTO_ALGO, + SemgrepReader.translate(780), + "CWE-780 (RSA without OAEP) should map to 327 (WEAK_CRYPTO_ALGO)"); + } + + @Test + void translateCwe943ReturnsSqlInjection() { + assertEquals( + CweNumber.SQL_INJECTION, + SemgrepReader.translate(943), + "CWE-943 (Data Query Injection) should map to 89 (SQL_INJECTION)"); + } + + @Test + void translateDontCareCwesReturnAsIs() { + assertEquals(75, SemgrepReader.translate(75), "CWE-75 should pass through unchanged"); + assertEquals(778, SemgrepReader.translate(778), "CWE-778 should pass through unchanged"); + assertEquals(942, SemgrepReader.translate(942), "CWE-942 should pass through unchanged"); + assertEquals(502, SemgrepReader.translate(502), "CWE-502 should pass through unchanged"); } } diff --git a/plugin/src/test/java/org/owasp/benchmarkutils/score/report/FormatsTest.java b/plugin/src/test/java/org/owasp/benchmarkutils/score/report/FormatsTest.java index af569169..7abac7ac 100644 --- a/plugin/src/test/java/org/owasp/benchmarkutils/score/report/FormatsTest.java +++ b/plugin/src/test/java/org/owasp/benchmarkutils/score/report/FormatsTest.java @@ -22,10 +22,25 @@ import static org.owasp.benchmarkutils.score.report.Formats.singleDecimalPlaceNumber; import static org.owasp.benchmarkutils.score.report.Formats.twoDecimalPlacesPercentage; +import java.util.Locale; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; class FormatsTest { + private Locale originalLocale; + + @BeforeEach + void saveLocale() { + originalLocale = Locale.getDefault(); + } + + @AfterEach + void restoreLocale() { + Locale.setDefault(originalLocale); + } + @Test void hasFormatterForTwoDecimalPlacesPercentage() { assertEquals("1234.57%", twoDecimalPlacesPercentage.format(12.345678)); @@ -40,4 +55,22 @@ void hasFormatterForFourDecimalPlaces() { void hasFormatterForSingleDecimalPlace() { assertEquals("12.3", singleDecimalPlaceNumber.format(12.345678)); } + + @Test + void formatsUseDotDecimalSeparatorRegardlessOfLocale() { + Locale.setDefault(Locale.GERMANY); + + assertEquals( + "1234.57%", + twoDecimalPlacesPercentage.format(12.345678), + "Percentage formatter must use dot separator even under German locale"); + assertEquals( + "12.3", + singleDecimalPlaceNumber.format(12.345678), + "Single decimal formatter must use dot separator even under German locale"); + assertEquals( + "12.3457", + fourDecimalPlacesNumber.format(12.345678), + "Four decimal formatter must use dot separator even under German locale"); + } } diff --git a/plugin/src/test/resources/testfiles/Benchmark_Klocwork.csv b/plugin/src/test/resources/testfiles/Benchmark_Klocwork.csv index ee84160b..b7d97172 100644 --- a/plugin/src/test/resources/testfiles/Benchmark_Klocwork.csv +++ b/plugin/src/test/resources/testfiles/Benchmark_Klocwork.csv @@ -1,3 +1,4 @@ File,Path,Line,Method,Code,Severity,State,Status,Taxonomy,Owner BenchmarkTest00001.java,/opt/klocwork/projects_root/projects/BenchmarkJavaToo/src/main/java/org/owasp/benchmark/testcode/BenchmarkTest00001.java,58,doPost(),SV.SQL,Error (2),New,Analyze,Java,unowned -BenchmarkTest00002.java,/opt/klocwork/projects_root/projects/BenchmarkJavaToo/src/main/java/org/owasp/benchmark/testcode/BenchmarkTest00002.java,64,doPost(),SV.PATH,Warning (3),New,Analyze,Java,unowned +BenchmarkTest00002.java,/opt/klocwork/projects_root/projects/BenchmarkJavaToo/src/main/java/org/owasp/benchmark/testcode/BenchmarkTest00002.java,64,doPost(),SV.PATH,Warning (3),New,Analyze,Java,unowned +BenchmarkTest00003.java,/opt/klocwork/projects_root/projects/BenchmarkJavaToo/src/main/java/org/owasp/benchmark/testcode/BenchmarkTest00003.java,58,doPost(),SV.DATA.DB,Error (2),New,Analyze,Java,unowned diff --git a/plugin/src/test/resources/testfiles/Benchmark_semgrep-v0.121.0.json b/plugin/src/test/resources/testfiles/Benchmark_semgrep-v0.121.0.json index b22fabd4..e493798c 100644 --- a/plugin/src/test/resources/testfiles/Benchmark_semgrep-v0.121.0.json +++ b/plugin/src/test/resources/testfiles/Benchmark_semgrep-v0.121.0.json @@ -196,6 +196,84 @@ "line": 42, "offset": 1802 } + }, + { + "check_id": "java.lang.security.audit.command-injection-general", + "end": { "col": 4, "line": 62, "offset": 2300 }, + "extra": { + "fingerprint": "aaa111", + "is_ignored": false, + "lines": "Runtime.exec(cmd)", + "message": "Command injection finding", + "metadata": { + "category": "security", + "confidence": "LOW", + "cwe": [ + "CWE-77: Improper Neutralization of Special Elements used in a Command" + ], + "impact": "HIGH", + "likelihood": "LOW", + "owasp": [ + "A03:2021 - Injection" + ] + }, + "metavars": {}, + "severity": "WARNING" + }, + "path": "src/main/java/org/owasp/benchmark/testcode/BenchmarkTest00005.java", + "start": { "col": 3, "line": 48, "offset": 2000 } + }, + { + "check_id": "java.lang.security.audit.rsa-no-oaep", + "end": { "col": 4, "line": 62, "offset": 2300 }, + "extra": { + "fingerprint": "bbb222", + "is_ignored": false, + "lines": "Cipher.getInstance(RSA)", + "message": "RSA without OAEP finding", + "metadata": { + "category": "security", + "confidence": "LOW", + "cwe": [ + "CWE-780: Use of RSA Algorithm without OAEP" + ], + "impact": "HIGH", + "likelihood": "LOW", + "owasp": [ + "A02:2021 - Cryptographic Failures" + ] + }, + "metavars": {}, + "severity": "WARNING" + }, + "path": "src/main/java/org/owasp/benchmark/testcode/BenchmarkTest00006.java", + "start": { "col": 3, "line": 48, "offset": 2000 } + }, + { + "check_id": "java.lang.security.audit.data-query-injection", + "end": { "col": 4, "line": 62, "offset": 2300 }, + "extra": { + "fingerprint": "ccc333", + "is_ignored": false, + "lines": "query.filter(input)", + "message": "Data query injection finding", + "metadata": { + "category": "security", + "confidence": "LOW", + "cwe": [ + "CWE-943: Improper Neutralization of Special Elements in Data Query Logic" + ], + "impact": "HIGH", + "likelihood": "LOW", + "owasp": [ + "A03:2021 - Injection" + ] + }, + "metavars": {}, + "severity": "WARNING" + }, + "path": "src/main/java/org/owasp/benchmark/testcode/BenchmarkTest00007.java", + "start": { "col": 3, "line": 48, "offset": 2000 } } ], "version": "0.121.0"