17
17
*/
18
18
package org .owasp .benchmarkutils .score .parsers ;
19
19
20
- import java .io .StringReader ;
20
+ import static java .lang .Integer .parseInt ;
21
+
21
22
import java .util .ArrayList ;
22
23
import java .util .List ;
23
- import javax .xml .parsers .DocumentBuilder ;
24
- import javax .xml .parsers .DocumentBuilderFactory ;
25
24
import org .owasp .benchmarkutils .score .BenchmarkScore ;
26
25
import org .owasp .benchmarkutils .score .CweNumber ;
27
26
import org .owasp .benchmarkutils .score .ResultFile ;
28
27
import org .owasp .benchmarkutils .score .TestCaseResult ;
29
28
import org .owasp .benchmarkutils .score .TestSuiteResults ;
30
- import org .w3c .dom .Document ;
31
29
import org .w3c .dom .Node ;
32
- import org .xml .sax .InputSource ;
33
30
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
+ */
37
36
public class HCLAppScanStandardReader extends Reader {
38
37
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
+
39
62
@ Override
40
63
public boolean canRead (ResultFile resultFile ) {
41
64
return resultFile .filename ().endsWith (".xml" )
@@ -46,235 +69,74 @@ public boolean canRead(ResultFile resultFile) {
46
69
47
70
@ Override
48
71
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 ();
61
73
62
74
TestSuiteResults tr =
63
75
new TestSuiteResults ("HCL AppScan Standard" , true , TestSuiteResults .ToolType .DAST );
64
76
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 );
77
79
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 ));
86
81
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 ();
118
85
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
+ });
126
99
}
100
+
127
101
return tr ;
128
102
}
129
103
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 ());
155
108
}
156
109
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 (" " );
186
113
187
- if (testArea .contains ("BenchmarkTest" ))
188
- testCaseElementsFromVariants .add (testArea );
189
- }
190
- }
191
- }
192
- }
193
-
194
- return testCaseElementsFromVariants ;
114
+ return variantUrl [1 ].trim ();
195
115
}
196
116
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 ));
203
122
204
- return cwe ;
123
+ if (version != null ) {
124
+ tr .setToolVersion (version .getTextContent ().split (" " )[0 ]);
125
+ }
205
126
}
206
127
207
- private int cweLookup (String vtype ) {
128
+ private int cweLookup (String vtype , int xmlCwe ) {
208
129
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
-
242
130
case "attXPathInjection" :
243
131
case "attBlindXpathInjectionSingleQuote" :
244
132
case "attBlindXPathInjection" :
245
133
return CweNumber .XPATH_INJECTION ;
134
+ }
246
135
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 ;
277
138
}
278
- return 0 ;
139
+
140
+ return xmlCwe ;
279
141
}
280
142
}
0 commit comments