Skip to content

Commit

Permalink
#65 - Reader for Snyk and Semgrep SARIF files (#67)
Browse files Browse the repository at this point in the history
* #65 - Reader for Snyk and Semgrep SARIF files

* #65 - Fix test typo
  • Loading branch information
darkspirit510 authored Apr 15, 2024
1 parent bc1690b commit cbdb828
Show file tree
Hide file tree
Showing 11 changed files with 648 additions and 593 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ public boolean canRead(ResultFile resultFile) {
.getJSONObject(0)
.getJSONObject("tool")
.getJSONObject("driver")
.has("semanticVersion");
.get("name")
.equals("CodeQL");
} catch (Exception e) {
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ public static List<Reader> allReaders() {
new ScnrReader(),
new SeekerReader(),
new SemgrepReader(),
new SemgrepSarifReader(),
new ShiftLeftReader(),
new ShiftLeftScanReader(),
new SnappyTickReader(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/**
* 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 Sascha Knoop
* @created 2024
*/
package org.owasp.benchmarkutils.score.parsers;

import java.util.Map;
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;

public abstract class SarifReader extends Reader {

private static final int INVALID_RULE_ID = -1;

protected abstract String expectedSarifToolName();

protected abstract boolean isCommercial();

protected abstract Map<String, Integer> ruleCweMappings(JSONArray rules);

@Override
public boolean canRead(ResultFile resultFile) {
try {
return resultFile.isJson() && sarifToolName(resultFile).equals(expectedSarifToolName());
} catch (Exception e) {
return false;
}
}

private String sarifToolName(ResultFile resultFile) {
return toolDriver(resultFile).getString("name");
}

private static JSONObject toolDriver(ResultFile resultFile) {
return firstRun(resultFile).getJSONObject("tool").getJSONObject("driver");
}

private static JSONObject firstRun(ResultFile resultFile) {
return resultFile.json().getJSONArray("runs").getJSONObject(0);
}

@Override
public TestSuiteResults parse(ResultFile resultFile) throws Exception {
JSONObject driver = toolDriver(resultFile);

Map<String, Integer> mappings = ruleCweMappings(driver.getJSONArray("rules"));

TestSuiteResults testSuiteResults =
new TestSuiteResults(
sarifToolName(resultFile), isCommercial(), TestSuiteResults.ToolType.SAST);

testSuiteResults.setToolVersion(driver.getString("semanticVersion"));

JSONArray results = firstRun(resultFile).getJSONArray("results");

for (int i = 0; i < results.length(); i++) {
JSONObject result = results.getJSONObject(i);

String className = extractFilename(resultUri(result));

if (!className.startsWith(BenchmarkScore.TESTCASENAME)) {
continue;
}

TestCaseResult tcr = new TestCaseResult();

String ruleId = result.getString("ruleId");

int cwe = mappings.getOrDefault(ruleId, INVALID_RULE_ID);

if (cwe == INVALID_RULE_ID) {
System.out.println("CWE # not parseable from: " + ruleId);
continue;
}

String evidence = result.getJSONObject("message").getString("text");

tcr.setCWE(cwe);
tcr.setCategory(ruleId);
tcr.setEvidence(evidence);
tcr.setConfidence(0);
tcr.setNumber(testNumber(className));

testSuiteResults.put(tcr);
}

return testSuiteResults;
}

private static String resultUri(JSONObject result) {
return result.getJSONArray("locations")
.getJSONObject(0)
.getJSONObject("physicalLocation")
.getJSONObject("artifactLocation")
.getString("uri");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/**
* 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 Sascha Knoop
* @created 2024
*/
package org.owasp.benchmarkutils.score.parsers;

import static java.lang.Integer.parseInt;

import java.util.HashMap;
import java.util.Map;
import org.json.JSONArray;
import org.json.JSONObject;

public class SemgrepSarifReader extends SarifReader {

@Override
protected String expectedSarifToolName() {
return "Semgrep OSS";
}

@Override
protected boolean isCommercial() {
return false;
}

@Override
protected Map<String, Integer> ruleCweMappings(JSONArray rules) {
Map<String, Integer> mappings = new HashMap<>();

for (int i = 0; i < rules.length(); i++) {
JSONObject rule = rules.getJSONObject(i);

JSONArray tags = rule.getJSONObject("properties").getJSONArray("tags");

for (int j = 0; j < tags.length(); j++) {
String tag = tags.getString(j);

if (tag.startsWith("CWE")) {
int cwe = parseInt(tag.split(":")[0].substring(4));

mappings.put(rule.getString("id"), cwe);
}
}
}

return mappings;
}
}
Original file line number Diff line number Diff line change
@@ -1,121 +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 Raj Barath
* @created 2023
*/
package org.owasp.benchmarkutils.score.parsers;

import static java.lang.Integer.parseInt;

import java.util.HashMap;
import java.util.Map;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.owasp.benchmarkutils.score.BenchmarkScore;
import org.owasp.benchmarkutils.score.CweNumber;
import org.owasp.benchmarkutils.score.ResultFile;
import org.owasp.benchmarkutils.score.TestCaseResult;
import org.owasp.benchmarkutils.score.TestSuiteResults;

public class SnykReader extends Reader {

public static final int INVALID_RULE_ID = -1;
private static final Map<String, Integer> snykCweMap =
new HashMap<String, Integer>() {
{
put("Xpath", CweNumber.XPATH_INJECTION);
put("WebCookieWithSecureFalse", CweNumber.INSECURE_COOKIE);
put("Sqli", CweNumber.SQL_INJECTION);
put("PT", CweNumber.PATH_TRAVERSAL);
put("HardcodedPassword", 0);
put("NoHardcodedCredentials", 0);
put("WebCookieMissesCallToSetHttpOnly", CweNumber.COOKIE_WITHOUT_HTTPONLY);
put("ServerInformationExposure", 0);
put("UserControlledFormatString", CweNumber.EXTERNALLY_CONTROLLED_STRING);
put("SpringCSRF", CweNumber.CSRF);
put("TrustBoundaryViolation", CweNumber.TRUST_BOUNDARY_VIOLATION);
put("CommandInjection", CweNumber.COMMAND_INJECTION);
put("EnvCommandInjection", CweNumber.COMMAND_INJECTION);
put("DOMXSS", CweNumber.XSS);
put("XSS", CweNumber.XSS);
put("InsecureCipherNoIntegrity", CweNumber.WEAK_CRYPTO_ALGO);
put("InsecureDefaultAesCipher", CweNumber.WEAK_CRYPTO_ALGO);
put("HttpResponseSplitting", CweNumber.HTTP_RESPONSE_SPLITTING);
put("InsecureSecret", CweNumber.WEAK_RANDOM);
put("LdapInjection", CweNumber.LDAP_INJECTION);
put("InsecureCipher", CweNumber.WEAK_CRYPTO_ALGO);
put("InsecureHash", CweNumber.WEAK_HASH_ALGO);
}
};
public class SnykReader extends SarifReader {

@Override
public boolean canRead(ResultFile resultFile) {
return resultFile.isJson() && isSnyk(resultFile);
protected String expectedSarifToolName() {
return "SnykCode";
}

@Override
public TestSuiteResults parse(ResultFile resultFile) throws Exception {
TestSuiteResults tr = new TestSuiteResults("Snyk", true, TestSuiteResults.ToolType.SAST);

JSONArray results =
resultFile.json().getJSONArray("runs").getJSONObject(0).getJSONArray("results");

for (int result = 0; result < results.length(); result++) {
TestCaseResult tcr = parseSnykFindings(results.getJSONObject(result));
if (tcr != null) {
tr.put(tcr);
}
}
return tr;
protected boolean isCommercial() {
return true;
}

private TestCaseResult parseSnykFindings(JSONObject result) {
try {
String className =
result.getJSONArray("locations")
.getJSONObject(0)
.getJSONObject("physicalLocation")
.getJSONObject("artifactLocation")
.getString("uri");
className = (className.substring(className.lastIndexOf('/') + 1)).split("\\.")[0];
if (className.startsWith(BenchmarkScore.TESTCASENAME)) {

TestCaseResult tcr = new TestCaseResult();

String ruleId = result.getString("ruleId");
ruleId = (ruleId.substring(ruleId.lastIndexOf('/') + 1)).split("\\.")[0];

int cwe = snykCweMap.getOrDefault(ruleId, INVALID_RULE_ID);

if (cwe == INVALID_RULE_ID) {
System.out.println("CWE # not parseable from: " + ruleId);
return null;
}
@Override
protected Map<String, Integer> ruleCweMappings(JSONArray rules) {
Map<String, Integer> mappings = new HashMap<>();

String evidence = result.getJSONObject("message").getString("text");
for (int i = 0; i < rules.length(); i++) {
JSONObject rule = rules.getJSONObject(i);

tcr.setCWE(cwe);
tcr.setCategory(ruleId);
tcr.setEvidence(evidence);
tcr.setConfidence(0);
tcr.setNumber(testNumber(className));
int cwe =
parseInt(
rule.getJSONObject("properties")
.getJSONArray("cwe")
.getString(0)
.substring(4));

return tcr;
}
} catch (Exception ex) {
ex.printStackTrace();
mappings.put(rule.getString("id"), cwe);
}

return null;
}

private Boolean isSnyk(ResultFile resultFile) {

try {
return resultFile
.json()
.getJSONArray("runs")
.getJSONObject(0)
.getJSONObject("tool")
.getJSONObject("driver")
.getString("name")
.equalsIgnoreCase("SnykCode");
} catch (JSONException e) {
return false;
}
return mappings;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package org.owasp.benchmarkutils.score.builder;

import java.util.HashMap;
import java.util.Map;
import org.owasp.benchmarkutils.score.CategoryResults;
import org.owasp.benchmarkutils.score.ToolResults;

public class ToolResultsBuilder {

private Map<String, CategoryResults> categoryResultsMap = new HashMap<>();

private ToolResultsBuilder() {}

public static ToolResultsBuilder builder() {
return new ToolResultsBuilder();
}

public ToolResults build() {
return null;
}

public ToolResultsBuilder setCategoryResults(Map<String, CategoryResults> categoryResultsMap) {
this.categoryResultsMap = categoryResultsMap;

return this;
}

public ToolResultsBuilder setCategoryResult(String key, CategoryResults value) {
this.categoryResultsMap.put(key, value);

return this;
}
}
Loading

0 comments on commit cbdb828

Please sign in to comment.