Skip to content
This repository was archived by the owner on Apr 8, 2025. It is now read-only.

Commit 0e231bd

Browse files
committed
Add scorecard generator for free DAST tool Wapiti.
1 parent 4d926a0 commit 0e231bd

File tree

2 files changed

+154
-5
lines changed

2 files changed

+154
-5
lines changed

src/main/java/org/owasp/benchmark/score/BenchmarkScore.java

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,14 @@
9393
import org.owasp.benchmark.score.parsers.ThunderScanReader;
9494
import org.owasp.benchmark.score.parsers.VeracodeReader;
9595
import org.owasp.benchmark.score.parsers.VisualCodeGrepperReader;
96+
import org.owasp.benchmark.score.parsers.WapitiReader;
9697
import org.owasp.benchmark.score.parsers.WebInspectReader;
9798
import org.owasp.benchmark.score.parsers.XanitizerReader;
9899
import org.owasp.benchmark.score.parsers.ZapReader;
99100
import org.owasp.benchmark.score.report.Report;
100101
import org.owasp.benchmark.score.report.ScatterHome;
101102
import org.owasp.benchmark.score.report.ScatterVulns;
103+
102104
import org.w3c.dom.Document;
103105
import org.w3c.dom.Node;
104106
import org.xml.sax.InputSource;
@@ -108,6 +110,9 @@ public class BenchmarkScore {
108110
// The prefix for the generated test file names. Used by lots of other classes too.
109111
public static final String BENCHMARKTESTNAME = "BenchmarkTest";
110112

113+
// The # of numbers in a test case name. Must match what is actually generated.
114+
public static final int TESTNAMEIDLENGTH = 5;
115+
111116
private static final String GUIDEFILENAME = "OWASP_Benchmark_Guide.html";
112117
private static final String HOMEFILENAME = "OWASP_Benchmark_Home.html";
113118
public static final String pathToScorecardResources = "src/main/resources/scorecard/";
@@ -666,19 +671,26 @@ else if ( filename.endsWith( ".faast" ) ) {
666671
}
667672

668673
else if ( filename.endsWith( ".json" ) ) {
674+
669675
String line2 = getLine( fileToParse, 1 );
670676
if ( line2.contains("Coverity") || line2.contains("formatVersion") ) {
671677
tr = new CoverityReader().parse( fileToParse );
672-
} else if ( line2.contains("Vendor") && line2.contains("Checkmarx") ) {
678+
}
679+
680+
else if ( line2.contains("Vendor") && line2.contains("Checkmarx") ) {
673681
tr = new CheckmarxESReader().parse( fileToParse );
674-
} else {
675-
// Have to parse the JSON object to figure out which tool it is
682+
}
683+
684+
else { // Handle JSON where we have to look for a specific node to identify the tool type
685+
676686
String content = new String(Files.readAllBytes(Paths.get(fileToParse.getPath())));
677687
JSONObject jsonobj = new JSONObject(content);
678688

679689
if (jsonobj.getJSONArray("results") != null) {
680690
tr = new SemgrepReader().parse( jsonobj );
681-
} else System.out.println("Error: No matching parser found for JSON file: " + filename);
691+
}
692+
693+
else System.out.println("Error: No matching parser found for JSON file: " + filename);
682694
}
683695
}
684696

@@ -757,7 +769,7 @@ else if ( line2.startsWith( "<CxXMLResults" )) {
757769
tr = new CheckmarxReader().parse( fileToParse );
758770
}
759771

760-
else if ( line2.startsWith( "<report" )) {
772+
else if ( line2.contains( "Arachni" )) {
761773
tr = new ArachniReader().parse( fileToParse );
762774
}
763775

@@ -769,6 +781,10 @@ else if (line2.startsWith("<CodeIssueCollection")) {
769781
tr = new VisualCodeGrepperReader().parse(fileToParse);
770782
}
771783

784+
else if ( getLine( fileToParse, 4 ).contains( "Wapiti" )) {
785+
tr = new WapitiReader().parse( fileToParse );
786+
}
787+
772788
else { // Handle XML where we have to look for a specific node to identify the tool type
773789

774790
Document doc = getXMLDocument( fileToParse );
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/**
2+
* OWASP Benchmark Project
3+
*
4+
* This file is part of the Open Web Application Security Project (OWASP)
5+
* Benchmark Project For details, please see
6+
* <a href="https://owasp.org/www-project-benchmark/">https://owasp.org/www-project-benchmark/</a>.
7+
*
8+
* The OWASP Benchmark is free software: you can redistribute it and/or modify it under the terms
9+
* of the GNU General Public License as published by the Free Software Foundation, version 2.
10+
*
11+
* The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
12+
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details
14+
*
15+
* @author Dave Wichers
16+
* @created 2020
17+
*/
18+
19+
package org.owasp.benchmark.score.parsers;
20+
21+
import java.io.File;
22+
import java.io.FileInputStream;
23+
import java.util.List;
24+
25+
import javax.xml.parsers.DocumentBuilder;
26+
import javax.xml.parsers.DocumentBuilderFactory;
27+
28+
import org.owasp.benchmark.score.BenchmarkScore;
29+
30+
import org.w3c.dom.Document;
31+
import org.w3c.dom.Node;
32+
import org.xml.sax.InputSource;
33+
34+
public class WapitiReader extends Reader {
35+
36+
public TestResults parse(File f) throws Exception {
37+
38+
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
39+
// Prevent XXE
40+
docBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
41+
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
42+
InputSource is = new InputSource(new FileInputStream(f));
43+
Document doc = docBuilder.parse(is);
44+
45+
TestResults tr = new TestResults( "Wapiti", false, TestResults.ToolType.DAST );
46+
47+
// Get the version of Wapiti out of the generatorVersion <info> element.
48+
Node root = doc.getDocumentElement();
49+
Node reportInfo = getNamedChild( "report_infos", root );
50+
List<Node> infoList = getNamedChildren( "info", reportInfo );
51+
for ( Node info : infoList ) {
52+
String name = getAttributeValue( "name", info );
53+
if ("generatorVersion".equals(name)) {
54+
String fullString = info.getTextContent(); // e.g., Wapiti 3.0.3
55+
String version = fullString.substring( fullString.lastIndexOf(' ') +1);
56+
tr.setToolVersion( version );
57+
break; // Exit for loop when version is found
58+
}
59+
}
60+
61+
// String time = getAttributeValue("ScanTime", root);
62+
// tr.setTime( time );
63+
64+
// Now parse each <vulnerability> in the set of <vulnerabilities>
65+
Node vulns = getNamedChild( "vulnerabilities", root );
66+
List<Node> vulnList = getNamedChildren( "vulnerability", vulns );
67+
68+
for ( Node vuln : vulnList ) {
69+
// Each vulnerability categority is a 'volunerability' node
70+
// And then there are <entries> within which each '<entry> is an instance of that vuln type
71+
72+
// First, get the CWE for all these entries
73+
int cwe = getCWE(vuln);
74+
75+
// Then process each entry
76+
Node entriesNode = getNamedChild( "entries", vuln );
77+
List<Node> entries = getNamedChildren( "entry", entriesNode );
78+
for ( Node entry : entries ) {
79+
String path = getNamedChild( "path", entry ).getTextContent();
80+
if (path.contains(BenchmarkScore.BENCHMARKTESTNAME)) {
81+
String testCaseNum = path.substring(path.length() - BenchmarkScore.TESTNAMEIDLENGTH);
82+
TestCaseResult tcr = new TestCaseResult();
83+
tcr.setCWE(cwe);
84+
tcr.setCategory( getAttributeValue( "name", vuln ) );
85+
tcr.setEvidence( getNamedChild( "curl_command", entry ).getTextContent() );
86+
try {
87+
tcr.setNumber( Integer.parseInt( testCaseNum ) );
88+
tr.put(tcr);
89+
} catch ( NumberFormatException e ) {
90+
System.out.println("ERROR: Couldn't parse test case number from: " + path);
91+
e.printStackTrace();
92+
}
93+
}
94+
}
95+
}
96+
return tr;
97+
}
98+
99+
// Parse the CWE # out of the references included with the vuln
100+
private int getCWE(Node vuln) {
101+
int cwe = -1;
102+
Node refs = getNamedChild( "references", vuln );
103+
List<Node> references = getNamedChildren( "reference", refs );
104+
for ( Node ref : references ) {
105+
String title = getNamedChild( "title", ref ).getTextContent();
106+
if (title.startsWith("CWE-")) {
107+
String cweNum = title.substring("CWE-".length(), title.indexOf(":"));
108+
try {
109+
cwe = Integer.parseInt( cweNum );
110+
break;
111+
} catch ( NumberFormatException e ) {
112+
e.printStackTrace();
113+
break;
114+
}
115+
}
116+
}
117+
return cwe;
118+
}
119+
120+
/* // Don't need any CWE translations currently.
121+
private int translate(String cwe) {
122+
switch( cwe ) {
123+
case "File Handling" : return 22; // Path Traversal
124+
case "Commands execution" : return 78; // Command Injection
125+
case "CRLF Injection" : return 9999; // don't care
126+
case "Cross Site Scripting" : return 79;
127+
case "SQL Injection" : return 89;
128+
case "Blind SQL Injection" : return 89;
129+
}
130+
return -1;
131+
}
132+
*/
133+
}

0 commit comments

Comments
 (0)