-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* add Datadog SAST support * add comments * update * update @darkspirit510 says its good so merging.
- Loading branch information
Showing
4 changed files
with
284 additions
and
0 deletions.
There are no files selected for viewing
224 changes: 224 additions & 0 deletions
224
plugin/src/main/java/org/owasp/benchmarkutils/score/parsers/DatadogSastReader.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,224 @@ | ||
/** | ||
* OWASP Benchmark Project | ||
* | ||
* <p>This file is part of the Open Web Application Security Project (OWASP) Benchmark Project For | ||
* details, please see <a | ||
* href="https://owasp.org/www-project-benchmark/">https://owasp.org/www-project-benchmark/</a>. | ||
* | ||
* <p>The OWASP Benchmark is free software: you can redistribute it and/or modify it under the terms | ||
* of the GNU General Public License as published by the Free Software Foundation, version 2. | ||
* | ||
* <p>The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY | ||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR | ||
* PURPOSE. See the GNU General Public License for more details. | ||
* | ||
* @author Julien Delange | ||
* @created 2024 | ||
*/ | ||
package org.owasp.benchmarkutils.score.parsers; | ||
|
||
import org.json.JSONArray; | ||
import org.json.JSONObject; | ||
import org.owasp.benchmarkutils.score.BenchmarkScore; | ||
import org.owasp.benchmarkutils.score.ResultFile; | ||
import org.owasp.benchmarkutils.score.TestCaseResult; | ||
import org.owasp.benchmarkutils.score.TestSuiteResults; | ||
|
||
/** | ||
* This reader is made for the datadog-static-analyzer available on | ||
* <a href="https://github.com/DataDog/datadog-static-analyzer">...</a>. | ||
* It uses the SARIF file produces by the tool. | ||
*/ | ||
public class DatadogSastReader extends Reader { | ||
private static final String DATADOG_TOOL_NAME = "datadog-static-analyzer"; | ||
|
||
@Override | ||
public boolean canRead(ResultFile resultFile) { | ||
try { | ||
return resultFile.filename().endsWith(".sarif") | ||
&& resultFile.isJson() | ||
&& resultFile | ||
.json() | ||
.getJSONArray("runs") | ||
.getJSONObject(0) | ||
.getJSONObject("tool") | ||
.getJSONObject("driver") | ||
.getString("name") | ||
.equalsIgnoreCase(DATADOG_TOOL_NAME) | ||
&& resultFile | ||
.json() | ||
.getJSONArray("runs") | ||
.getJSONObject(0) | ||
.getJSONObject("tool") | ||
.getJSONObject("driver") | ||
.has("version"); | ||
} catch (Exception e) { | ||
return false; | ||
} | ||
} | ||
|
||
/** | ||
* Provide a direct mapping between a rule identifier and a CWE | ||
* | ||
* @param ruleId the rule identifier | ||
* @return the corresponding CWE identifier | ||
*/ | ||
private Type getTypeFromRuleId(String ruleId) { | ||
if (ruleId.equalsIgnoreCase("java-security/cookies-secure-flag") | ||
|| ruleId.equalsIgnoreCase("java-security/cookies-http-only")) { | ||
return Type.INSECURE_COOKIE; | ||
} | ||
if (ruleId.equalsIgnoreCase("java-security/avoid-random")) { | ||
return Type.WEAK_RANDOMNESS; | ||
} | ||
if (ruleId.equalsIgnoreCase("java-security/sql-injection")) { | ||
return Type.SQL_INJECTION; | ||
} | ||
if (ruleId.equalsIgnoreCase("java-security/keygenerator-avoid-des")) { | ||
return Type.WEAK_CIPHER; | ||
} | ||
if (ruleId.equalsIgnoreCase("java-security/ldap-injection")) { | ||
return Type.LDAP_INJECTION; | ||
} | ||
if (ruleId.equalsIgnoreCase("java-security/command-injection")) { | ||
return Type.COMMAND_INJECTION; | ||
} | ||
if (ruleId.equalsIgnoreCase("java-security/weak-message-digest-md5") | ||
|| ruleId.equalsIgnoreCase("java-security/weak-message-digest-sha1")) { | ||
return Type.WEAK_HASH; | ||
} | ||
if (ruleId.equalsIgnoreCase("java-security/xml-parsing-xxe-xpath") | ||
|| ruleId.equalsIgnoreCase("java-security/tainted-xpath")) { | ||
return Type.XPATH_INJECTION; | ||
} | ||
if (ruleId.contains("java-security") && ruleId.contains("xss")) { | ||
return Type.XSS; | ||
} | ||
if (ruleId.contains("java-security") | ||
&& ruleId.contains("trust") | ||
&& ruleId.contains("bound")) { | ||
return Type.TRUST_BOUNDARY_VIOLATION; | ||
} | ||
if (ruleId.equalsIgnoreCase("java-security/path-traversal")) { | ||
return Type.PATH_TRAVERSAL; | ||
} | ||
return null; | ||
} | ||
|
||
/** | ||
* Try to get the CWE from the violation object in the SARIF report. | ||
* | ||
* @param violation the violation object from the SARIF report | ||
* @return the CWE if found, 0 otherwise | ||
*/ | ||
private int getCweFromProperties(JSONObject violation) { | ||
try { | ||
JSONObject properties = violation.getJSONObject("properties"); | ||
JSONArray tags = properties.getJSONArray("tags"); | ||
for (int k = 0; k < tags.length(); k++) { | ||
String s = tags.getString(k); | ||
if (s.contains("CWE:")) { | ||
return Integer.parseInt(s.split(":")[1]); | ||
} | ||
} | ||
} catch (Exception e) { | ||
return 0; | ||
} | ||
return 0; | ||
} | ||
|
||
@Override | ||
public TestSuiteResults parse(ResultFile resultFile) throws Exception { | ||
JSONArray runs = resultFile.json().getJSONArray("runs"); | ||
|
||
TestSuiteResults tr = | ||
new TestSuiteResults("DatadogSast", false, TestSuiteResults.ToolType.SAST); | ||
|
||
tr.setTime(resultFile.file()); | ||
|
||
for (int i = 0; i < runs.length(); i++) { | ||
JSONObject run = runs.getJSONObject(i); | ||
|
||
JSONObject driver = run.getJSONObject("tool").getJSONObject("driver"); | ||
if (!driver.has("name") | ||
|| !driver.getString("name").equalsIgnoreCase(DATADOG_TOOL_NAME)) { | ||
continue; | ||
} | ||
|
||
tr.setToolVersion(driver.getString("version")); | ||
|
||
JSONArray results = run.getJSONArray("results"); | ||
|
||
for (int j = 0; j < results.length(); j++) { | ||
JSONObject result = results.getJSONObject(j); | ||
String ruleId = result.getString("ruleId"); | ||
TestCaseResult tcr = new TestCaseResult(); | ||
|
||
// First, try to get the CWE based on the rule id. If it fails, try to get it from | ||
// the property of the violation. | ||
Type t = getTypeFromRuleId(ruleId); | ||
|
||
if (t != null) { | ||
tcr.setCWE(t.number); | ||
tcr.setCategory(t.id); | ||
} else { | ||
continue; | ||
} | ||
|
||
if (tcr.getCWE() == 0) { | ||
continue; | ||
} | ||
|
||
JSONArray locations = result.getJSONArray("locations"); | ||
String filename = | ||
locations | ||
.getJSONObject(0) | ||
.getJSONObject("physicalLocation") | ||
.getJSONObject("artifactLocation") | ||
.getString("uri"); | ||
|
||
filename = filename.substring(filename.lastIndexOf('/') + 1); | ||
if (!filename.startsWith(BenchmarkScore.TESTCASENAME)) { | ||
continue; | ||
} | ||
int testnumber = testNumber(filename); | ||
tcr.setNumber(testnumber); | ||
tcr.setEvidence(result.getJSONObject("message").getString("text")); | ||
tr.put(tcr); | ||
} | ||
} | ||
return tr; | ||
} | ||
|
||
// Enumeration that contains the CWE and associated category. | ||
private enum Type { | ||
COMMAND_INJECTION(78), | ||
WEAK_HASH("crypto-bad-mac", 328), | ||
WEAK_CIPHER("crypto-bad-ciphers", 327), | ||
HEADER_INJECTION(113), | ||
INSECURE_COOKIE("cookie-flags-missing", 614), | ||
LDAP_INJECTION(90), | ||
PATH_TRAVERSAL(22), | ||
REFLECTION_INJECTION(0), | ||
SQL_INJECTION(89), | ||
STACKTRACE_LEAK(209), | ||
TRUST_BOUNDARY_VIOLATION(501), | ||
WEAK_RANDOMNESS("crypto-weak-randomness", 330), | ||
XPATH_INJECTION(643), | ||
XSS("reflected-xss", 79); | ||
|
||
private final int number; | ||
|
||
private final String id; | ||
|
||
Type(final int number) { | ||
this.number = number; | ||
id = name().toLowerCase().replaceAll("_", "-"); | ||
} | ||
|
||
Type(final String id, final int number) { | ||
this.number = number; | ||
this.id = id; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
58 changes: 58 additions & 0 deletions
58
plugin/src/test/java/org/owasp/benchmarkutils/score/parsers/DatadogSastReaderTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
/** | ||
* OWASP Benchmark Project | ||
* | ||
* <p>This file is part of the Open Web Application Security Project (OWASP) Benchmark Project For | ||
* details, please see <a | ||
* href="https://owasp.org/www-project-benchmark/">https://owasp.org/www-project-benchmark/</a>. | ||
* | ||
* <p>The OWASP Benchmark is free software: you can redistribute it and/or modify it under the terms | ||
* of the GNU General Public License as published by the Free Software Foundation, version 2. | ||
* | ||
* <p>The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY | ||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR | ||
* PURPOSE. See the GNU General Public License for more details. | ||
* | ||
* @author Julien Delange | ||
* @created 2024 | ||
*/ | ||
package org.owasp.benchmarkutils.score.parsers; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
|
||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
import org.owasp.benchmarkutils.score.BenchmarkScore; | ||
import org.owasp.benchmarkutils.score.CweNumber; | ||
import org.owasp.benchmarkutils.score.ResultFile; | ||
import org.owasp.benchmarkutils.score.TestHelper; | ||
import org.owasp.benchmarkutils.score.TestSuiteResults; | ||
|
||
public class DatadogSastReaderTest extends ReaderTestBase { | ||
|
||
private ResultFile resultFile; | ||
|
||
@BeforeEach | ||
void setUp() { | ||
resultFile = TestHelper.resultFileOf("testfiles/Benchmark_DatadogSast.sarif"); | ||
BenchmarkScore.TESTCASENAME = "BenchmarkTest"; | ||
} | ||
|
||
@Test | ||
public void canReadFile() { | ||
assertOnlyMatcherClassIs(this.resultFile, DatadogSastReader.class); | ||
} | ||
|
||
@Test | ||
void readerHandlesGivenResultFile() throws Exception { | ||
DatadogSastReader reader = new DatadogSastReader(); | ||
TestSuiteResults result = reader.parse(resultFile); | ||
|
||
assertEquals(TestSuiteResults.ToolType.SAST, result.getToolType()); | ||
|
||
assertEquals("DatadogSast", result.getToolName()); | ||
|
||
assertEquals(1, result.getTotalResults()); | ||
|
||
assertEquals(CweNumber.INSECURE_COOKIE, result.get(10).get(0).getCWE()); | ||
} | ||
} |
1 change: 1 addition & 0 deletions
1
plugin/src/test/resources/testfiles/Benchmark_DatadogSast.sarif
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{"runs":[{"results":[{"fixes":[],"level":"warning","locations":[{"physicalLocation":{"artifactLocation":{"uri":"BenchmarkTest00010.java"},"region":{"endColumn":67,"endLine":79,"startColumn":21,"startLine":79}}}],"message":{"text":"the http only flag is not set on the cookie"},"partialFingerprints":{"DATADOG_FINGERPRINT":"e4e9899ecb58e179da4fff7e5ce8aadf6bffba5dc40df7e67f1c686fb99fb6ec"},"properties":{"tags":["DATADOG_CATEGORY:SECURITY","CWE:614"]},"ruleId":"java-security/cookies-http-only","ruleIndex":0}],"tool":{"driver":{"informationUri":"https://www.datadoghq.com","name":"datadog-static-analyzer","properties":{"tags":["DATADOG_DIFF_AWARE_CONFIG_DIGEST:1199c81a70ecaada6cbd3abf5c3ff7c5c1538cab6158bd955abb1c5f319d86e3","DATADOG_EXECUTION_TIME_SECS:0","DATADOG_DIFF_AWARE_ENABLED:false"]},"rules":[{"fullDescription":{"text":"A cookie must always be created with `HttpOnly`. The flag is set to prevent malicious script to use the cookie. Always set `HttpOnly`.\n\n#### Learn More\n\n - [OWASP HTTP Only](https://owasp.org/www-community/HttpOnly)\n - [Coding Horror - Protecting Your Cookies: HttpOnly](https://blog.codinghorror.com/protecting-your-cookies-httponly/)"},"helpUri":"https://docs.datadoghq.com/static_analysis/rules/owasp10/cookies-http-only","id":"owasp10/cookies-http-only","properties":{"tags":["DATADOG_RULE_TYPE:STATIC_ANALYSIS","CWE:614"]},"shortDescription":{"text":"Cookies HTTP only"}}],"version":"0.2.9"}}}],"version":"2.1.0"} |