Skip to content

Commit fe418fc

Browse files
committed
Merge branch 'main' into generalizeScoring
2 parents 1f175e7 + 381ef89 commit fe418fc

File tree

7 files changed

+90
-241
lines changed

7 files changed

+90
-241
lines changed

plugin/src/main/java/org/owasp/benchmarkutils/score/parsers/HCLAppScanStandardReader.java

Lines changed: 74 additions & 212 deletions
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,48 @@
1717
*/
1818
package org.owasp.benchmarkutils.score.parsers;
1919

20-
import java.io.StringReader;
20+
import static java.lang.Integer.parseInt;
21+
2122
import java.util.ArrayList;
2223
import java.util.List;
23-
import javax.xml.parsers.DocumentBuilder;
24-
import javax.xml.parsers.DocumentBuilderFactory;
2524
import org.owasp.benchmarkutils.score.BenchmarkScore;
2625
import org.owasp.benchmarkutils.score.CweNumber;
2726
import org.owasp.benchmarkutils.score.ResultFile;
2827
import org.owasp.benchmarkutils.score.TestCaseResult;
2928
import org.owasp.benchmarkutils.score.TestSuiteResults;
30-
import org.w3c.dom.Document;
3129
import org.w3c.dom.Node;
32-
import org.xml.sax.InputSource;
3330

34-
// This is the new HCL AppScan DAST reader, where they generate ".xml" files. HCL calls this AppScan
35-
// Standard.
36-
// The 'old' reader is AppScanDynamicReader, which supports the previous .xml format from IBM.
31+
/**
32+
* This is the new HCL AppScan DAST reader, where they generate ".xml" files. HCL calls this AppScan
33+
* Standard. The 'old' reader is AppScanDynamicReader, which supports the previous .xml format from
34+
* IBM.
35+
*/
3736
public class HCLAppScanStandardReader extends Reader {
3837

38+
private final List<String> ignoreList = new ArrayList<>();
39+
40+
public HCLAppScanStandardReader() {
41+
ignoreList.add("attContentSecurityPolicyObjectSrc");
42+
ignoreList.add("attContentSecurityPolicyScriptSrc");
43+
ignoreList.add("attCachedSSL");
44+
ignoreList.add("attJSCookie");
45+
ignoreList.add("attLinkInjection");
46+
ignoreList.add("attUndefinedState");
47+
ignoreList.add("bodyParamsInQuery");
48+
ignoreList.add("ContentSecurityPolicy");
49+
ignoreList.add("ContentTypeOptions");
50+
ignoreList.add("GD_EmailAddress");
51+
ignoreList.add("GETParamOverSSL");
52+
ignoreList.add("GV_SQLErr");
53+
ignoreList.add("HSTS");
54+
ignoreList.add("MHTMLXSS");
55+
ignoreList.add("OpenSource");
56+
ignoreList.add("phishingInFrames");
57+
ignoreList.add("OldTLS");
58+
ignoreList.add("ShellShockCheck");
59+
ignoreList.add("SriSupport");
60+
}
61+
3962
@Override
4063
public boolean canRead(ResultFile resultFile) {
4164
return resultFile.filename().endsWith(".xml")
@@ -46,235 +69,74 @@ public boolean canRead(ResultFile resultFile) {
4669

4770
@Override
4871
public TestSuiteResults parse(ResultFile resultFile) throws Exception {
49-
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
50-
// Prevent XXE
51-
docBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
52-
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
53-
InputSource is = new InputSource(new StringReader(resultFile.content()));
54-
Document doc = docBuilder.parse(is);
55-
56-
Node root = doc.getDocumentElement();
57-
Node scanInfo = getNamedChild("scan-information", root);
58-
59-
Node scanConfiguration = getNamedChild("scan-configuration", root);
60-
String startingUrl = getNamedChild("starting-url", scanConfiguration).getTextContent();
72+
Node root = resultFile.xml().getDocumentElement();
6173

6274
TestSuiteResults tr =
6375
new TestSuiteResults("HCL AppScan Standard", true, TestSuiteResults.ToolType.DAST);
6476

65-
// version is usually like 9.3.0 but sometimes like 9.3.0 iFix005. We trim off the part
66-
// after the space char.
67-
Node version = getNamedChild("product-version", scanInfo);
68-
// System.out.println("Product version is: " + version.getTextContent());
69-
if (version != null) {
70-
tr.setToolVersion(version.getTextContent().split(" ")[0]);
71-
}
72-
73-
Node allIssueVariants = getNamedChild("issue-group", root);
74-
List<Node> variants = getNamedChildren("item", allIssueVariants);
75-
76-
List<String> testCaseElementsFromVariants = new ArrayList<>();
77+
setTime(root, tr);
78+
setVersion(root, tr);
7779

78-
if (variants.isEmpty()) {
79-
// Handle non-variant issue types , Older xml format as in 9.x release versions and
80-
// before
81-
// First get the type of vuln, and if we don't care about that type, move on
82-
Node allIssues = getNamedChild("url-group", root);
83-
List<Node> vulnerabilities = getNamedChildren("item", allIssues);
84-
for (Node vulnerability : vulnerabilities) {
85-
String issueType = getNamedChild("issue-type", vulnerability).getTextContent();
80+
List<Node> variants = getNamedChildren("item", getNamedChild("issue-group", root));
8681

87-
String url = getNamedChild("name", vulnerability).getTextContent();
88-
89-
TestCaseResult tcr = TestCaseLookup(issueType, url);
90-
if (tcr != null) tr.put(tcr);
91-
}
92-
} else {
93-
// Handle issues which are Variants, new xml format after 10.x release
94-
for (Node variant : variants) {
95-
String variantIssueType =
96-
getNamedChild("issue-type", variant).getTextContent().trim();
97-
// System.out.println("Variant Url Ref ID: " + variantUrlRefId);
98-
99-
// Add the record only if the issue type matches for the relevant variants
100-
Node variantNodes = getNamedChild("variant-group", variant);
101-
List<Node> variantNodeChildren = getNamedChildren("item", variantNodes);
102-
for (Node variantNodeChild : variantNodeChildren) {
103-
String httpTraffic =
104-
getNamedChild("test-http-traffic", variantNodeChild).getTextContent();
105-
String[] variantUrl = httpTraffic.split(" ");
106-
107-
String benchMarkTestCase = variantUrl[1].trim();
108-
109-
if (benchMarkTestCase.contains("BenchmarkTest")) {
110-
String[] urlElements = benchMarkTestCase.split("/");
111-
112-
String testAreaUrl =
113-
startingUrl
114-
+ urlElements[urlElements.length - 2]
115-
+ "/"
116-
+ urlElements[urlElements.length - 1];
117-
String testArea = testAreaUrl.split("\\?")[0]; // .split strips off the -##
82+
for (Node variant : variants) {
83+
int xmlCwe = parseInt(getNamedChild("cwe", variant).getTextContent());
84+
String variantIssueType = getNamedChild("issue-type", variant).getTextContent().trim();
11885

119-
if (testArea.contains("BenchmarkTest"))
120-
testCaseElementsFromVariants.add(testArea);
121-
TestCaseResult tcr = TestCaseLookup(variantIssueType, testArea);
122-
if (tcr != null) tr.put(tcr);
123-
}
124-
}
125-
}
86+
// FIXME: The filter for startsWith needs to be fixed to support generalized scoring
87+
getNamedChildren("item", getNamedChild("variant-group", variant)).stream()
88+
.map(node -> extractFilenameWithoutEnding(extractUrlFrom(node)))
89+
.filter(filename -> filename.startsWith(BenchmarkScore.TESTCASENAME))
90+
.forEach(
91+
filename -> {
92+
TestCaseResult tcr = new TestCaseResult();
93+
tcr.setTestID(getBenchmarkStyleTestCaseNumber(filename));
94+
tcr.setCWE(cweLookup(variantIssueType, xmlCwe));
95+
tcr.setEvidence(variantIssueType);
96+
97+
tr.put(tcr);
98+
});
12699
}
100+
127101
return tr;
128102
}
129103

130-
/// Issues which are not variants
131-
private TestCaseResult TestCaseLookup(String issueType, String url) {
132-
String[] urlElements = url.split("/");
133-
String testArea =
134-
urlElements[urlElements.length - 2].split("-")[0]; // .split strips off the -##
135-
136-
int vtype = cweLookup(issueType, testArea);
137-
138-
// Then get the filename containing the vuln. And if not in a test case, skip it.
139-
// Parse out test number from:
140-
// https://localhost:port/benchmark/testarea-##/BenchmarkTest02603
141-
int startOfTestCase = url.lastIndexOf("/") + 1;
142-
String testcase = url.substring(startOfTestCase, url.length());
143-
// if test case has extension (e.g., BenchmarkTestCase#####.html), strip it off.
144-
testcase = testcase.split("\\.")[0];
145-
// System.out.println("Candidate test case is: " + testcase);
146-
if (testcase.startsWith(BenchmarkScore.TESTCASENAME)) {
147-
// Add the vuln found in a test case to the results for this tool
148-
TestCaseResult tcr = new TestCaseResult();
149-
tcr.setTestID(getBenchmarkStyleTestCaseNumber(testcase));
150-
tcr.setCWE(vtype);
151-
tcr.setEvidence(issueType);
152-
return tcr;
153-
}
154-
return null;
104+
private void setTime(Node root, TestSuiteResults tr) {
105+
tr.setTime(
106+
getNamedChild("scan-Duration", getNamedChild("scan-summary", root))
107+
.getTextContent());
155108
}
156109

157-
// Fetch Issues listed as variants, to cater to post 10.x release xml format
158-
private List<String> variantLookup(
159-
String issueType, String itemID, String startingUrl, List<Node> variants) {
160-
List<String> testCaseElementsFromVariants = new ArrayList<>();
161-
162-
for (Node variant : variants) {
163-
String variantUrlRefId = getNamedChild("url", variant).getTextContent().trim();
164-
String variantIssueType = getNamedChild("issue-type", variant).getTextContent().trim();
165-
166-
// Add the record only if the issue type matches for the relevant variants
167-
if (issueType.equals(variantIssueType) && itemID.equals(variantUrlRefId)) {
168-
Node variantNodes = getNamedChild("variant-group", variant);
169-
List<Node> variantNodeChildren = getNamedChildren("item", variantNodes);
170-
for (Node variantNodeChild : variantNodeChildren) {
171-
String httpTraffic =
172-
getNamedChild("test-http-traffic", variantNodeChild).getTextContent();
173-
String[] variantUrl = httpTraffic.split(" ");
174-
175-
String benchMarkTestCase = variantUrl[1].trim();
176-
177-
if (benchMarkTestCase.contains("BenchmarkTest")) {
178-
String[] urlElements = benchMarkTestCase.split("/");
179-
180-
String testAreaUrl =
181-
startingUrl
182-
+ urlElements[urlElements.length - 2]
183-
+ "/"
184-
+ urlElements[urlElements.length - 1];
185-
String testArea = testAreaUrl.split("\\?")[0]; // .split strips off the -##
110+
private static String extractUrlFrom(Node variantNodeChild) {
111+
String[] variantUrl =
112+
getNamedChild("test-http-traffic", variantNodeChild).getTextContent().split(" ");
186113

187-
if (testArea.contains("BenchmarkTest"))
188-
testCaseElementsFromVariants.add(testArea);
189-
}
190-
}
191-
}
192-
}
193-
194-
return testCaseElementsFromVariants;
114+
return variantUrl[1].trim();
195115
}
196116

197-
private int cweLookup(String vtype, String testArea) {
198-
int cwe = cweLookup(vtype); // Do the standard CWE lookup
199-
200-
// Then map some to other CWEs based on the test area being processed.
201-
if ("xpathi".equals(testArea) && cwe == 89) cwe = 643; // CWE for XPath injection
202-
if ("ldapi".equals(testArea) && cwe == 89) cwe = 90; // CWE for LDAP injection
117+
/*
118+
* Version is usually like 9.3.0 but sometimes like 9.3.0 iFix005. We trim off the part after the space char.
119+
*/
120+
private static void setVersion(Node root, TestSuiteResults tr) {
121+
Node version = getNamedChild("product-version", getNamedChild("scan-information", root));
203122

204-
return cwe;
123+
if (version != null) {
124+
tr.setToolVersion(version.getTextContent().split(" ")[0]);
125+
}
205126
}
206127

207-
private int cweLookup(String vtype) {
128+
private int cweLookup(String vtype, int xmlCwe) {
208129
switch (vtype) {
209-
case "attDirectoryFound":
210-
case "attDirOptions":
211-
case "attFileUnix":
212-
return CweNumber.PATH_TRAVERSAL;
213-
214-
case "attApplicationRemoteCodeExecutionAdns":
215-
case "attCodeInjectionInSystemCall":
216-
case "attCommandInjectionAdns":
217-
case "attCommandInjectionUnixTws":
218-
case "attFileParamPipe":
219-
return CweNumber.COMMAND_INJECTION;
220-
221-
case "attCrossSiteScripting":
222-
return CweNumber.XSS;
223-
224-
case "attBlindSqlInjectionStrings":
225-
case "attSqlInjectionChecks":
226-
return CweNumber.SQL_INJECTION;
227-
228-
case "attLDAPInjection":
229-
case "attLDAPInjection2":
230-
case "attBlindLDAPInjection":
231-
return CweNumber.LDAP_INJECTION;
232-
233-
case "SHA1CipherSuites":
234-
return CweNumber.WEAK_HASH_ALGO; // Better if set to 327?
235-
236-
case "passParamGET":
237-
return CweNumber.UNPROTECTED_CREDENTIALS_TRANSPORT;
238-
239-
case "attRespCookieNotSecureSSL":
240-
return CweNumber.INSECURE_COOKIE;
241-
242130
case "attXPathInjection":
243131
case "attBlindXpathInjectionSingleQuote":
244132
case "attBlindXPathInjection":
245133
return CweNumber.XPATH_INJECTION;
134+
}
246135

247-
// These don't map to anything we care about
248-
case "attContentSecurityPolicyObjectSrc":
249-
case "attContentSecurityPolicyScriptSrc":
250-
case "attCachedSSL":
251-
case "attJSCookie":
252-
// case "attLinkInjection" : return 00;
253-
case "attUndefinedState":
254-
case "bodyParamsInQuery":
255-
case "ContentSecurityPolicy":
256-
case "ContentTypeOptions":
257-
case "GD_EmailAddress":
258-
case "GETParamOverSSL":
259-
case "GV_SQLErr":
260-
// case "HSTS" : return 00;
261-
262-
// Microsoft MHTML XSS - Giving AppScan XSS 'credit' for this introduces ~2.4% False
263-
// Positives and no real ones so I (shivababuh) disabled it instead
264-
case "MHTMLXSS":
265-
// case "OpenSource" : return 00; // Known vuln in open source lib.
266-
// case "phishingInFrames" : return 00;
267-
case "OldTLS":
268-
case "ShellShockCheck":
269-
case "SriSupport":
270-
// case "SSL_CertWithBadCN" : return 00;
271-
// case "XSSProtectionHeader" : return 00;
272-
return CweNumber.DONTCARE;
273-
274-
default:
275-
System.out.println(
276-
"WARNING: HCL AppScan Standard-Unrecognized finding type: " + vtype);
136+
if (ignoreList.contains(vtype)) {
137+
return CweNumber.DONTCARE;
277138
}
278-
return 0;
139+
140+
return xmlCwe;
279141
}
280142
}

plugin/src/main/java/org/owasp/benchmarkutils/score/parsers/MendReader.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public TestSuiteResults parse(ResultFile resultFile) throws Exception {
4747
for (Report.EngineResults.Result.Vulnerability vulnerability :
4848
result.vulnerabilities) {
4949
try {
50-
String testfile = extractFilename(vulnerability.filename);
50+
String testfile = extractFilenameWithoutEnding(vulnerability.filename);
5151

5252
if (testfile.startsWith(BenchmarkScore.TESTCASENAME)) {
5353
TestCaseResult tcr = new TestCaseResult();

plugin/src/main/java/org/owasp/benchmarkutils/score/parsers/Rapid7Reader.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public TestSuiteResults parse(ResultFile resultFile) throws Exception {
4747

4848
for (Report.Vulnerability vulnerability : report.vulnerabilities) {
4949
try {
50-
String testfile = extractFilename(vulnerability.url);
50+
String testfile = extractFilenameWithoutEnding(vulnerability.url);
5151

5252
if (testfile.startsWith(BenchmarkScore.TESTCASENAME)) {
5353
TestCaseResult tcr = new TestCaseResult();

plugin/src/main/java/org/owasp/benchmarkutils/score/parsers/Reader.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -298,11 +298,15 @@ public static int getBenchmarkStyleTestCaseNumber(String path, String testCaseNa
298298
}
299299
}
300300

301-
public static String extractFilename(String path) {
301+
public static String extractFilenameWithoutEnding(String path) {
302302
try {
303-
path = removeUrlPart(path);
303+
String name = new File(fixWindowsPath(removeUrlPart(path))).getName();
304304

305-
return new File(fixWindowsPath(path)).getName();
305+
if (name.contains(".")) {
306+
return name.substring(0, name.lastIndexOf("."));
307+
} else {
308+
return name;
309+
}
306310
} catch (Throwable t) {
307311
return "";
308312
}

plugin/src/main/java/org/owasp/benchmarkutils/score/parsers/sarif/SarifReader.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ public String toolName(ResultFile resultFile) {
235235
private TestCaseResult testCaseResultFor(JSONObject result, Map<String, Integer> mappings) {
236236
TestCaseResult tcr = new TestCaseResult();
237237

238-
String className = extractFilename(resultUri(result));
238+
String className = extractFilenameWithoutEnding(resultUri(result));
239239

240240
if (!isTestCaseFile(className)) {
241241
return null;

0 commit comments

Comments
 (0)