Skip to content

Commit

Permalink
feat: add bearer reader (#59)
Browse files Browse the repository at this point in the history
* feat: add bearer reader

* chore: address PR reviews

* fix: fixup weak_crypto detections
  • Loading branch information
cfabianski authored Nov 21, 2023
1 parent c63ab56 commit ddb35eb
Show file tree
Hide file tree
Showing 4 changed files with 288 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/**
* 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 Cédric Fabianski
* @created 2023
*/
package org.owasp.benchmarkutils.score.parsers;

import java.util.Objects;
import org.json.JSONArray;
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 BearerReader extends Reader {

@Override
public boolean canRead(ResultFile resultFile) {
return resultFile.isJson()
&& resultFile.json().has("findings")
&& resultFile.json().has("source")
&& Objects.equals(resultFile.json().getString("source"), "Bearer");
}

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

JSONArray results = resultFile.json().getJSONArray("findings");
tr.setToolVersion(resultFile.json().getString("version"));

// results
for (int i = 0; i < results.length(); i++) {
TestCaseResult tcr = parseBearerFindings(results.getJSONObject(i));
if (tcr != null) {
tr.put(tcr);
}
}
return tr;
}

private int translate(int cwe) {
switch (cwe) {
case 326:
return CweNumber.WEAK_CRYPTO_ALGO;
case 327:
return CweNumber.WEAK_HASH_ALGO;
default:
return cwe;
}
}

private TestCaseResult parseBearerFindings(JSONObject result) {
try {
String className = result.getString("filename");
className = (className.substring(className.lastIndexOf('/') + 1)).split("\\.")[0];
if (className.startsWith(BenchmarkScore.TESTCASENAME)) {
TestCaseResult tcr = new TestCaseResult();

// CWE
String cweString = result.getJSONArray("cwe_ids").getString(0);
int cwe = Integer.parseInt(cweString);

try {
cwe = translate(cwe);
} catch (NumberFormatException ex) {
System.out.println(
"CWE # not parseable from: " + result.getJSONObject("cwe_ids"));
}

// evidence
String evidence = result.getString("id");

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

return tcr;
}

} catch (Exception ex) {
ex.printStackTrace();
}

return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,18 @@ public abstract class Reader {
protected final ObjectMapper jsonMapper = new ObjectMapper();
protected final XmlMapper xmlMapper = new XmlMapper();

// TODO: Figure out how to dynamically add all readers here without listing them out manually
// NOTE: There is a unit test that at least automatically verifies that any reader with a unit
// TODO: Figure out how to dynamically add all readers here without listing them
// out manually
// NOTE: There is a unit test that at least automatically verifies that any
// reader with a unit
// test is in this list
public static List<Reader> allReaders() {
return Arrays.asList(
new AcunetixReader(),
new AppScanDynamicReader(),
new AppScanSourceReader(),
new ArachniReader(),
new BearerReader(),
new BurpJsonReader(),
new BurpReader(),
new CASTAIPReader(),
Expand Down Expand Up @@ -115,11 +118,13 @@ public static Node getNamedNode(String name, NodeList list) {
}
return null;
}
// Returns the node inside this nodelist whose name matches 'name', that also has an attribute
// Returns the node inside this nodelist whose name matches 'name', that also
// has an attribute
// called 'key' whose value matches 'keyvalue'

public static Node getNamedNode(String name, String keyValue, NodeList list) {
if ((name == null) || (keyValue == null) || (list == null)) return null;
if ((name == null) || (keyValue == null) || (list == null))
return null;
for (int i = 0; i < list.getLength(); i++) {
Node n = list.item(i);
if (n.getNodeName().equals(name)) {
Expand Down Expand Up @@ -168,7 +173,8 @@ public static List<Node> getNamedNodes(String name, NodeList list) {
}

public static String getAttributeValue(String name, Node node) {
if (node == null) return null;
if (node == null)
return null;
NamedNodeMap nnm = node.getAttributes();
if (nnm != null) {
Node attrnode = nnm.getNamedItem(name);
Expand Down Expand Up @@ -210,18 +216,16 @@ public static int testNumber(String path) {
if (path.indexOf(BenchmarkScore.TESTCASENAME) < 0) {
return -1;
}
int numberStart =
path.indexOf(BenchmarkScore.TESTCASENAME)
+ BenchmarkScore.TESTCASENAME.length()
+ 1;
int numberStart = path.indexOf(BenchmarkScore.TESTCASENAME)
+ BenchmarkScore.TESTCASENAME.length()
+ 1;
path = path.substring(numberStart);
// System.out.println("After length: " + path);
path = path.replaceAll("\\?.*", "");
path = path.replaceAll(",.*", "");

path =
path.replaceAll(
BenchmarkScore.TESTCASENAME + "v[0-9]*", BenchmarkScore.TESTCASENAME);
path = path.replaceAll(
BenchmarkScore.TESTCASENAME + "v[0-9]*", BenchmarkScore.TESTCASENAME);

path = path.replaceAll("/send", "");
if (path.contains(":")) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/**
* 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 Cédric Fabianski
* @created 2023
*/
package org.owasp.benchmarkutils.score.parsers;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;

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 BearerReaderTest extends ReaderTestBase {

private ResultFile resultFileV1_30;

@BeforeEach
void setUp() {
resultFileV1_30 = TestHelper.resultFileOf("testfiles/Benchmark_Bearer-v1.30.0.jsonv2");
BenchmarkScore.TESTCASENAME = "BenchmarkTest";
}

@Test
public void onlyBearerReaderReportsCanReadAsTrueForV1_30() {
assertOnlyMatcherClassIs(this.resultFileV1_30, BearerReader.class);
}

@Test
void readerHandlesGivenResultFileInV1_30() throws Exception {
BearerReader reader = new BearerReader();
TestSuiteResults result = reader.parse(resultFileV1_30);

assertEquals(TestSuiteResults.ToolType.SAST, result.getToolType());
assertFalse(result.isCommercial());
assertEquals("Bearer", result.getToolName());
assertEquals("v1.30.0", result.getToolVersion());

assertEquals(3, result.getTotalResults());

assertEquals(CweNumber.COMMAND_INJECTION, result.get(7).get(0).getCWE());
assertEquals(CweNumber.WEAK_HASH_ALGO, result.get(5).get(0).getCWE());
assertEquals(CweNumber.WEAK_CRYPTO_ALGO, result.get(35).get(0).getCWE());
}
}
108 changes: 108 additions & 0 deletions plugin/src/test/resources/testfiles/Benchmark_Bearer-v1.30.0.jsonv2
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
{
"source": "Bearer",
"version": "v1.30.0",
"findings": [
{
"cwe_ids": ["78"],
"id": "java_lang_os_command_injection",
"title": "Command injection vulnerability detected.",
"description": "## Description\n\nUsing external or user-defined input directly in an OS command can allow attackers to perform dangerous commands on the operating system.\n\n## Remediations\n\n❌ Avoid using OS commands, with or without dynamic input, wherever possible. For example, look for an equivalent library or function to use instead.\n\n✅ For dynamic input, rely on hardcoded values wherever possible\n\n```java\n String filePattern = \"*.json\";\n if request.getParameter(\"format\") == \"xml\" {\n filePattern = \"*.xml\"\n }\n\n Process process = Runtime.getRuntime().exec(\"ls /myDir/\" + extension);\n```\n\n## Resources\n- [OWASP command injection explained](https://owasp.org/www-community/attacks/Command_Injection)\n",
"documentation_url": "https://docs.bearer.com/reference/rules/java_lang_os_command_injection",
"line_number": 61,
"full_filename": "../../OWASP/BenchmarkJava/src/main/java/org/owasp/benchmark/testcode/BenchmarkTest00007.java",
"filename": "java/org/owasp/benchmark/testcode/BenchmarkTest00007.java",
"category_groups": ["PII", "Personal Data"],
"source": {
"start": 61,
"end": 61,
"column": {
"start": 25,
"end": 46
}
},
"sink": {
"start": 61,
"end": 61,
"column": {
"start": 25,
"end": 46
},
"content": "r.exec(args, argsEnv)"
},
"parent_line_number": 61,
"snippet": "r.exec(args, argsEnv)",
"fingerprint": "a7d389038dcf77969a24604f7d2b3d93_0",
"old_fingerprint": "d45907bfb55a9cd885577ae854996b20_2",
"code_extract": " Process p = r.exec(args, argsEnv);",
"severity": "high"
},
{
"cwe_ids": ["327"],
"id": "java_lang_padding_oracle_encryption_vulnerability",
"title": "Padding Oracle encryption vulnerability detected.",
"description": "## Description\n\nUsing a block cipher algorithm mode, such as CBC, together with a padding scheme is vulnerable to Padding Oracle attacks.\n\n## Remediations\n\n❌ Do not use CBC (Cipher Block Chaining) mode with padding\n\n```java\n Cipher c = Cipher.getInstance(\"AES/CBC/PKCS5Padding\");\n```\n\n✅ Prefer GCM (Galois/Counter Mode) instead\n\n```java\n Cipher c = Cipher.getInstance(\"AES/GCM/PKCS5Padding\");\n```\n\n## Resources\n- [Java Cipher class](https://docs.oracle.com/en/java/javase/20/docs/api/java.base/javax/crypto/Cipher.html)\n- [Java Security Standard Algorithm Names](https://docs.oracle.com/en/java/javase/20/docs/specs/security/standard-names.html)\n",
"documentation_url": "https://docs.bearer.com/reference/rules/java_lang_padding_oracle_encryption_vulnerability",
"line_number": 63,
"full_filename": "../../OWASP/BenchmarkJava/src/main/java/org/owasp/benchmark/testcode/BenchmarkTest00005.java",
"filename": "java/org/owasp/benchmark/testcode/BenchmarkTest00005.java",
"category_groups": ["PII", "Personal Data"],
"source": {
"start": 63,
"end": 63,
"column": {
"start": 37,
"end": 92
}
},
"sink": {
"start": 63,
"end": 63,
"column": {
"start": 37,
"end": 92
},
"content": "javax.crypto.Cipher.getInstance(\"DES/CBC/PKCS5Padding\")"
},
"parent_line_number": 63,
"snippet": "javax.crypto.Cipher.getInstance(\"DES/CBC/PKCS5Padding\")",
"fingerprint": "2b05bdeb1248912e33258917421e91ff_0",
"old_fingerprint": "1bff95a3cb3020fab82cd9495bf9010c_12",
"code_extract": " javax.crypto.Cipher c = javax.crypto.Cipher.getInstance(\"DES/CBC/PKCS5Padding\");",
"severity": "medium"
},
{
"cwe_ids": ["326", "327"],
"id": "java_lang_weak_encryption_des",
"title": "Weak encryption algorithm (DES) detected.",
"description": "## Description\n\nA weak encryption library can lead to data breaches and greater security risk.\n\n## Remediations\nAccording to [OWASP](https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/09-Testing_for_Weak_Cryptography/04-Testing_for_Weak_Encryption), DES (Data Encryption Standard) is considered a weak encryption algorithm and therefore shouldn't be used.\n\n✅ Use stronger encryption algorithms when storing data.\n\n```java\n Cipher c = Cipher.getInstance(\"AES/CBC/PKCS5Padding\");\n```\n\n## Resources\n- [Java Cipher class](https://docs.oracle.com/en/java/javase/20/docs/api/java.base/javax/crypto/Cipher.html)\n",
"documentation_url": "https://docs.bearer.com/reference/rules/java_lang_weak_encryption_des",
"line_number": 87,
"full_filename": "../../OWASP/BenchmarkJava/src/main/java/org/owasp/benchmark/testcode/BenchmarkTest00035.java",
"filename": "java/org/owasp/benchmark/testcode/BenchmarkTest00035.java",
"category_groups": ["PII", "Personal Data"],
"source": {
"start": 87,
"end": 87,
"column": {
"start": 29,
"end": 45
}
},
"sink": {
"start": 87,
"end": 87,
"column": {
"start": 29,
"end": 45
},
"content": "c.doFinal(input)"
},
"parent_line_number": 87,
"snippet": "c.doFinal(input)",
"fingerprint": "9163934a23bdb461be27983fa49e0695_0",
"old_fingerprint": "47611adbe4c6acbdd6d00c66b06db103_57",
"code_extract": " byte[] result = c.doFinal(input);",
"severity": "medium"
}
]
}

0 comments on commit ddb35eb

Please sign in to comment.