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
- import org .w3c .dom .NamedNodeMap ;
32
29
import org .w3c .dom .Node ;
33
- import org .xml .sax .InputSource ;
34
30
35
- // This is the new HCL AppScan DAST reader, where they generate ".xml" files. HCL calls this AppScan
36
- // Standard.
37
- // 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
+ */
38
36
public class HCLAppScanStandardReader extends Reader {
39
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
+
40
62
@ Override
41
63
public boolean canRead (ResultFile resultFile ) {
42
64
return resultFile .filename ().endsWith (".xml" )
@@ -47,245 +69,75 @@ public boolean canRead(ResultFile resultFile) {
47
69
48
70
@ Override
49
71
public TestSuiteResults parse (ResultFile resultFile ) throws Exception {
50
- DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory .newInstance ();
51
- // Prevent XXE
52
- docBuilderFactory .setFeature ("http://apache.org/xml/features/disallow-doctype-decl" , true );
53
- DocumentBuilder docBuilder = docBuilderFactory .newDocumentBuilder ();
54
- InputSource is = new InputSource (new StringReader (resultFile .content ()));
55
- Document doc = docBuilder .parse (is );
56
-
57
- Node root = doc .getDocumentElement ();
58
- Node scanInfo = getNamedChild ("scan-information" , root );
59
-
60
- Node scanConfiguration = getNamedChild ("scan-configuration" , root );
61
- String startingUrl = getNamedChild ("starting-url" , scanConfiguration ).getTextContent ();
72
+ Node root = resultFile .xml ().getDocumentElement ();
62
73
63
74
TestSuiteResults tr =
64
75
new TestSuiteResults ("HCL AppScan Standard" , true , TestSuiteResults .ToolType .DAST );
65
76
66
- // version is usually like 9.3.0 but sometimes like 9.3.0 iFix005. We trim off the part
67
- // after the space char.
68
- Node version = getNamedChild ("product-version" , scanInfo );
69
- // System.out.println("Product version is: " + version.getTextContent());
70
- if (version != null ) {
71
- tr .setToolVersion (version .getTextContent ().split (" " )[0 ]);
72
- }
73
-
74
- Node allIssueVariants = getNamedChild ("issue-group" , root );
75
- List <Node > variants = getNamedChildren ("item" , allIssueVariants );
76
-
77
- List <String > testCaseElementsFromVariants = new ArrayList <>();
78
-
79
-
80
- if (variants .isEmpty ()) {
81
- // Handle non-variant issue types , Older xml format as in 9.x release versions and
82
- // before
83
- // First get the type of vuln, and if we don't care about that type, move on
84
- Node allIssues = getNamedChild ("url-group" , root );
85
- List <Node > vulnerabilities = getNamedChildren ("item" , allIssues );
86
- for (Node vulnerability : vulnerabilities ) {
87
- String issueType = getNamedChild ("issue-type" , vulnerability ).getTextContent ();
88
-
89
- String url = getNamedChild ("name" , vulnerability ).getTextContent ();
90
-
91
- TestCaseResult tcr = TestCaseLookup (issueType , url );
92
- if (tcr != null ) tr .put (tcr );
93
- }
94
- }
95
-
96
- else {
97
- // Handle issues which are Variants, new xml format after 10.x release
98
- for (Node variant : variants ) {
99
- String variantIssueType = getNamedChild ("issue-type" , variant ).getTextContent ().trim ();
100
- // System.out.println("Variant Url Ref ID: " + variantUrlRefId);
101
-
102
- // Add the record only if the issue type matches for the relevant variants
103
- Node variantNodes = getNamedChild ("variant-group" , variant );
104
- List <Node > variantNodeChildren = getNamedChildren ("item" , variantNodes );
105
- for (Node variantNodeChild : variantNodeChildren ) {
106
- String httpTraffic =
107
- getNamedChild ("test-http-traffic" , variantNodeChild ).getTextContent ();
108
- String [] variantUrl = httpTraffic .split (" " );
109
-
110
- String benchMarkTestCase = variantUrl [1 ].trim ();
111
-
112
- if (benchMarkTestCase .contains ("BenchmarkTest" )) {
113
- String [] urlElements = benchMarkTestCase .split ("/" );
114
-
115
- String testAreaUrl =
116
- startingUrl
117
- + urlElements [urlElements .length - 2 ]
118
- + "/"
119
- + urlElements [urlElements .length - 1 ];
120
- String testArea = testAreaUrl .split ("\\ ?" )[0 ]; // .split strips off the -##
121
-
122
- if (testArea .contains ("BenchmarkTest" ))
123
- testCaseElementsFromVariants .add (testArea );
124
- TestCaseResult tcr = TestCaseLookup (variantIssueType , testArea );
125
- if (tcr != null )
126
- tr .put (tcr );
127
- }
128
- }
129
- }
130
- }
131
- return tr ;
132
- }
133
-
134
- /// Issues which are not variants
135
- private TestCaseResult TestCaseLookup (String issueType , String url ) {
136
- String [] urlElements = url .split ("/" );
137
- String testArea =
138
- urlElements [urlElements .length - 2 ].split ("-" )[0 ]; // .split strips off the -##
139
-
140
- int vtype = cweLookup (issueType , testArea );
141
-
142
- // Then get the filename containing the vuln. And if not in a test case, skip it.
143
- // Parse out test number from:
144
- // https://localhost:port/benchmark/testarea-##/BenchmarkTest02603
145
- int startOfTestCase = url .lastIndexOf ("/" ) + 1 ;
146
- String testcase = url .substring (startOfTestCase , url .length ());
147
- // if test case has extension (e.g., BenchmarkTestCase#####.html), strip it off.
148
- testcase = testcase .split ("\\ ." )[0 ];
149
- // System.out.println("Candidate test case is: " + testcase);
150
- if (testcase .startsWith (BenchmarkScore .TESTCASENAME )) {
151
- // if (tn == -1) System.out.println("Found vuln outside of test case of type: " +
152
- // issueType);
153
-
154
- // Add the vuln found in a test case to the results for this tool
155
- TestCaseResult tcr = new TestCaseResult ();
156
- tcr .setNumber (testNumber (testcase ));
157
- tcr .setCategory (issueType ); // TODO: Is this right?
158
- tcr .setCWE (vtype );
159
- tcr .setEvidence (issueType );
160
- return tcr ;
161
- }
162
- return null ;
163
- }
164
-
165
- // Fetch Issues listed as variants, to cater to post 10.x release xml format
166
- private List <String > variantLookup (
167
- String issueType , String itemID , String startingUrl , List <Node > variants ) {
168
- List <String > testCaseElementsFromVariants = new ArrayList <>();
77
+ setTime (root , tr );
78
+ setVersion (root , tr );
169
79
170
- // System.out.println("Variant Lookup Item ID: " + itemID );
80
+ List < Node > variants = getNamedChildren ( "item" , getNamedChild ( "issue-group" , root ) );
171
81
172
82
for (Node variant : variants ) {
173
- String variantUrlRefId = getNamedChild ("url " , variant ).getTextContent (). trim ( );
83
+ int xmlCwe = parseInt ( getNamedChild ("cwe " , variant ).getTextContent ());
174
84
String variantIssueType = getNamedChild ("issue-type" , variant ).getTextContent ().trim ();
175
- // System.out.println("Variant Url Ref ID: " + variantUrlRefId);
176
-
177
- // Add the record only if the issue type matches for the relevant variants
178
- if (issueType .equals (variantIssueType ) && itemID .equals (variantUrlRefId )) {
179
- Node variantNodes = getNamedChild ("variant-group" , variant );
180
- List <Node > variantNodeChildren = getNamedChildren ("item" , variantNodes );
181
- for (Node variantNodeChild : variantNodeChildren ) {
182
- String httpTraffic =
183
- getNamedChild ("test-http-traffic" , variantNodeChild ).getTextContent ();
184
- String [] variantUrl = httpTraffic .split (" " );
185
-
186
- String benchMarkTestCase = variantUrl [1 ].trim ();
187
85
188
- if (benchMarkTestCase .contains ("BenchmarkTest" )) {
189
- String [] urlElements = benchMarkTestCase .split ("/" );
86
+ getNamedChildren ("item" , getNamedChild ("variant-group" , variant )).stream ()
87
+ .map (node -> extractFilenameWithoutEnding (extractUrlFrom (node )))
88
+ .filter (filename -> filename .startsWith (BenchmarkScore .TESTCASENAME ))
89
+ .forEach (
90
+ filename -> {
91
+ TestCaseResult tcr = new TestCaseResult ();
190
92
191
- String testAreaUrl =
192
- startingUrl
193
- + urlElements [urlElements .length - 2 ]
194
- + "/"
195
- + urlElements [urlElements .length - 1 ];
196
- String testArea = testAreaUrl .split ("\\ ?" )[0 ]; // .split strips off the -##
93
+ tcr .setNumber (testNumber (filename ));
94
+ tcr .setCategory (variantIssueType ); // TODO: Is this right?
95
+ tcr .setCWE (cweLookup (variantIssueType , xmlCwe ));
96
+ tcr .setEvidence (variantIssueType );
197
97
198
- if (testArea .contains ("BenchmarkTest" ))
199
- testCaseElementsFromVariants .add (testArea );
200
- }
201
- }
202
- }
98
+ tr .put (tcr );
99
+ });
203
100
}
204
101
205
- return testCaseElementsFromVariants ;
102
+ return tr ;
206
103
}
207
104
208
- private int cweLookup (String vtype , String testArea ) {
209
- int cwe = cweLookup (vtype ); // Do the standard CWE lookup
210
-
211
- // Then map some to other CWEs based on the test area being processed.
212
- if ("xpathi" .equals (testArea ) && cwe == 89 ) cwe = 643 ; // CWE for XPath injection
213
- if ("ldapi" .equals (testArea ) && cwe == 89 ) cwe = 90 ; // CWE for LDAP injection
214
-
215
- return cwe ;
105
+ private void setTime (Node root , TestSuiteResults tr ) {
106
+ tr .setTime (
107
+ getNamedChild ("scan-Duration" , getNamedChild ("scan-summary" , root ))
108
+ .getTextContent ());
216
109
}
217
110
218
- private int cweLookup (String vtype ) {
219
- switch (vtype ) {
220
- case "attDirectoryFound" :
221
- case "attDirOptions" :
222
- case "attFileUnix" :
223
- return CweNumber .PATH_TRAVERSAL ;
224
-
225
- case "attApplicationRemoteCodeExecutionAdns" :
226
- case "attCodeInjectionInSystemCall" :
227
- case "attCommandInjectionAdns" :
228
- case "attCommandInjectionUnixTws" :
229
- case "attFileParamPipe" :
230
- return CweNumber .COMMAND_INJECTION ;
231
-
232
- case "attCrossSiteScripting" :
233
- return CweNumber .XSS ;
234
-
235
- case "attBlindSqlInjectionStrings" :
236
- case "attSqlInjectionChecks" :
237
- return CweNumber .SQL_INJECTION ;
238
-
239
- case "attLDAPInjection" :
240
- case "attLDAPInjection2" :
241
- case "attBlindLDAPInjection" :
242
- return CweNumber .LDAP_INJECTION ;
111
+ private static String extractUrlFrom (Node variantNodeChild ) {
112
+ String [] variantUrl =
113
+ getNamedChild ("test-http-traffic" , variantNodeChild ).getTextContent ().split (" " );
243
114
244
- case "SHA1CipherSuites" :
245
- return CweNumber . WEAK_HASH_ALGO ; // Better if set to 327?
115
+ return variantUrl [ 1 ]. trim ();
116
+ }
246
117
247
- case "passParamGET" :
248
- return CweNumber .UNPROTECTED_CREDENTIALS_TRANSPORT ;
118
+ /*
119
+ * Version is usually like 9.3.0 but sometimes like 9.3.0 iFix005. We trim off the part after the space char.
120
+ */
121
+ private static void setVersion (Node root , TestSuiteResults tr ) {
122
+ Node version = getNamedChild ("product-version" , getNamedChild ("scan-information" , root ));
249
123
250
- case "attRespCookieNotSecureSSL" :
251
- return CweNumber .INSECURE_COOKIE ;
124
+ if (version != null ) {
125
+ tr .setToolVersion (version .getTextContent ().split (" " )[0 ]);
126
+ }
127
+ }
252
128
129
+ private int cweLookup (String vtype , int xmlCwe ) {
130
+ switch (vtype ) {
253
131
case "attXPathInjection" :
254
132
case "attBlindXpathInjectionSingleQuote" :
255
133
case "attBlindXPathInjection" :
256
134
return CweNumber .XPATH_INJECTION ;
135
+ }
257
136
258
- // These don't map to anything we care about
259
- case "attContentSecurityPolicyObjectSrc" :
260
- case "attContentSecurityPolicyScriptSrc" :
261
- case "attCachedSSL" :
262
- case "attJSCookie" :
263
- // case "attLinkInjection" : return 00;
264
- case "attUndefinedState" :
265
- case "bodyParamsInQuery" :
266
- case "ContentSecurityPolicy" :
267
- case "ContentTypeOptions" :
268
- case "GD_EmailAddress" :
269
- case "GETParamOverSSL" :
270
- case "GV_SQLErr" :
271
- // case "HSTS" : return 00;
272
-
273
- // Microsoft MHTML XSS - Giving AppScan XSS 'credit' for this introduces ~2.4% False
274
- // Positives and no real ones so I (shivababuh) disabled it instead
275
- case "MHTMLXSS" :
276
- // case "OpenSource" : return 00; // Known vuln in open source lib.
277
- // case "phishingInFrames" : return 00;
278
- case "OldTLS" :
279
- case "ShellShockCheck" :
280
- case "SriSupport" :
281
- // case "SSL_CertWithBadCN" : return 00;
282
- // case "XSSProtectionHeader" : return 00;
283
- return CweNumber .DONTCARE ;
284
-
285
- default :
286
- System .out .println (
287
- "WARNING: HCL AppScan Standard-Unrecognized finding type: " + vtype );
137
+ if (ignoreList .contains (vtype )) {
138
+ return CweNumber .DONTCARE ;
288
139
}
289
- return 0 ;
140
+
141
+ return xmlCwe ;
290
142
}
291
143
}
0 commit comments