Merge branch 'json-support' of https://github.com/ThomasGoeytil/DependencyCheck into ThomasGoeytil-json-support

This commit is contained in:
Jeremy Long
2017-05-06 14:51:30 -04:00
6 changed files with 260 additions and 21 deletions

View File

@@ -120,7 +120,7 @@ public final class CliParser {
Format.valueOf(format);
} catch (IllegalArgumentException ex) {
final String msg = String.format("An invalid 'format' of '%s' was specified. "
+ "Supported output formats are XML, HTML, VULN, or ALL", format);
+ "Supported output formats are XML, JSON, HTML, VULN, or ALL", format);
throw new ParseException(msg);
}
}
@@ -262,7 +262,7 @@ public final class CliParser {
.build();
final Option outputFormat = Option.builder(ARGUMENT.OUTPUT_FORMAT_SHORT).argName("format").hasArg().longOpt(ARGUMENT.OUTPUT_FORMAT)
.desc("The output format to write to (XML, HTML, VULN, ALL). The default is HTML.")
.desc("The output format to write to (XML, JSON, HTML, VULN, ALL). The default is HTML.")
.build();
final Option verboseLog = Option.builder(ARGUMENT.VERBOSE_LOG_SHORT).argName("file").hasArg().longOpt(ARGUMENT.VERBOSE_LOG)

View File

@@ -253,6 +253,10 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
<groupId>com.sun.mail</groupId>
<artifactId>mailapi</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<!-- The following dependencies are only used during testing -->
<dependency>
<groupId>org.apache.maven.scm</groupId>
@@ -383,6 +387,7 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
<scope>test</scope>
<optional>true</optional>
</dependency>
</dependencies>
<profiles>
<profile>
@@ -571,13 +576,6 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
<scope>test</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.3.1</version>
<scope>test</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-extension-api</artifactId>

View File

@@ -80,4 +80,16 @@ public class EscapeTool {
}
return StringEscapeUtils.escapeXml11(text);
}
/**
* JSON Encodes the provded text
* @param text the text to encode
* @return the JSON encoded text
*/
public String json(String text) {
if (text == null || text.isEmpty()) {
return text;
}
return StringEscapeUtils.escapeJson(text);
}
}

View File

@@ -17,17 +17,14 @@
*/
package org.owasp.dependencycheck.reporting;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.*;
import java.nio.file.*;
import java.util.List;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.context.Context;
@@ -78,7 +75,11 @@ public class ReportGenerator {
/**
* Generate HTML Vulnerability report.
*/
VULN
VULN,
/**
* Generate JSON report.
*/
JSON
}
/**
* The Velocity Engine.
@@ -186,6 +187,9 @@ public class ReportGenerator {
if (format == Format.VULN || format == Format.ALL) {
generateReport("VulnerabilityReport", outputStream);
}
if (format == Format.JSON || format == Format.ALL) {
generateReport("JsonReport", outputStream);
}
}
/**
@@ -200,12 +204,27 @@ public class ReportGenerator {
if (format == Format.XML || format == Format.ALL) {
generateReport("XmlReport", outputDir + File.separator + "dependency-check-report.xml");
}
if (format == Format.JSON || format == Format.ALL) {
generateReport("JsonReport", outputDir + File.separator + "dependency-check-report.json");
try {
Path resultPath = Paths.get(outputDir + File.separator + "dependency-check-report.json");
String content = new String(Files.readAllBytes(resultPath));
Gson gson = new GsonBuilder().setPrettyPrinting().create();
JsonParser jp = new JsonParser();
JsonElement je = jp.parse(content);
String prettyJson = gson.toJson(je);
Files.write(Paths.get(outputDir + File.separator + "dependency-check-report.json"), prettyJson.getBytes(), StandardOpenOption.WRITE);
} catch (IOException e) {
LOGGER.error("Unable to generate pretty report, got error: ", e.getMessage());
}
}
if (format == Format.HTML || format == Format.ALL) {
generateReport("HtmlReport", outputDir + File.separator + "dependency-check-report.html");
}
if (format == Format.VULN || format == Format.ALL) {
generateReport("VulnerabilityReport", outputDir + File.separator + "dependency-check-vulnerability.html");
}
}
/**
@@ -220,7 +239,7 @@ public class ReportGenerator {
public void generateReports(String outputDir, String outputFormat) throws ReportException {
final String format = outputFormat.toUpperCase();
final String pathToCheck = outputDir.toLowerCase();
if (format.matches("^(XML|HTML|VULN|ALL)$")) {
if (format.matches("^(XML|HTML|VULN|JSON|ALL)$")) {
if ("XML".equalsIgnoreCase(format)) {
if (pathToCheck.endsWith(".xml")) {
generateReport("XmlReport", outputDir);
@@ -242,6 +261,13 @@ public class ReportGenerator {
generateReports(outputDir, Format.VULN);
}
}
if ("JSON".equalsIgnoreCase(format)) {
if (pathToCheck.endsWith(".json")) {
generateReport("JsonReport", outputDir);
} else {
generateReports(outputDir, Format.JSON);
}
}
if ("ALL".equalsIgnoreCase(format)) {
generateReports(outputDir, Format.ALL);
}

View File

@@ -0,0 +1,198 @@
{
"analysis": {
"scanInfo": {
"engineVersion": "$version",
"dataSource": [
#foreach($prop in $properties.getMetaData().entrySet())
#if($foreach.count > 1),#end{
"name": "$enc.json($prop.key)",
"timestamp": "$enc.json($prop.value)"
}
#end
]
},
"projectInfo": {
"name": "$enc.json($applicationName)",
"reportDate": "$scanDateXML",
"credits": "This report contains data retrieved from the National Vulnerability Database: http://nvd.nist.gov"
},
"dependencies": [
#foreach($dependency in $dependencies)#if($foreach.count > 1),#end{
"fileName": "$enc.json($dependency.DisplayFileName)",
"filePath": "$enc.json($dependency.FilePath)",
"md5": "$enc.json($dependency.Md5sum)",
"sha1": "$enc.json($dependency.Sha1sum)"
#if($dependency.description),"description": "$enc.json($dependency.description)"#end
#if($dependency.license),"license": "$enc.json($dependency.license)"#end
#if ($dependency.getRelatedDependencies().size()>0)
,"relatedDependencies": [
#foreach($related in $dependency.getRelatedDependencies()) #if($foreach.count > 1),#end {
"filePath": "$enc.json($related.FilePath)",
"sha1": "$enc.json($related.Sha1sum)",
"md5": "$enc.json($related.Md5sum)"#if($related.getIdentifiers()),#end
"identifiers": [
#foreach($id in $related.getIdentifiers())
#if ($id.type=="maven")
{
"type": "$enc.json($id.type)",
"name": "$id.value"
#if( $id.url ),"url": "$enc.json($id.url)"#end
#if ($id.notes),"notes": "$enc.json($id.notes)"#end
}
#end
#end
]
}
#end
]
#end
,"evidenceCollected": {
"vendorEvidence": [
#foreach($evidence in $dependency.getVendorEvidence())
#if($foreach.count > 1),#end{
"type": "vendor",
"confidence": "$enc.json($evidence.getConfidence().toString())",
"source": "$enc.json($evidence.getSource())",
"name": "$enc.json($evidence.getName())",
"value": "$enc.json($evidence.getValue().trim())"
}
#end
],
"productEvidence": [
#foreach($evidence in $dependency.getProductEvidence())
#if($foreach.count > 1),#end{
"type": "product",
"confidence": "$enc.json($evidence.getConfidence().toString())",
"source": "$enc.json($evidence.getSource())",
"name": "$enc.json($evidence.getName())",
"value": "$enc.json($evidence.getValue().trim())"
}
#end
],
"versionEvidence": [
#foreach($evidence in $dependency.getVersionEvidence())
#if($foreach.count > 1),#end{
"type": "version",
"confidence": "$enc.json($evidence.getConfidence().toString())",
"source": "$enc.json($evidence.getSource())",
"name": "$enc.json($evidence.getName())",
"value": "$enc.json($evidence.getValue().trim())"
}
#end
]
},
"identifiers": [
#foreach($id in $dependency.getIdentifiers())#if($foreach.count > 1),#end{
"name": "$id.value",
"type": "$enc.json($id.type)",
#if($id.confidence)"confidence": "$id.confidence",#end
#if($id.url)"url": "$enc.json($id.url)",#end
#if($id.description )"description": "$enc.json($id.description)",#end
#if ($id.notes)"notes": "$enc.json($id.notes)",#end
"suppressedIdentifiers": [
#foreach($id in $dependency.getSuppressedIdentifiers())
#if($foreach.count > 1),#end{
"type": "$enc.json($id.type)",
#if($id.confidence)"confidence": "$id.confidence",#end
"name": "$id.value",
#if($id.url)"url": "$enc.json($id.url),"#end
#if($id.description)"description": "$enc.json($id.description)",#end
#if ($id.notes)"notes": "$enc.json($id.notes)"#end
}
#end
]
}
#end
]
#if($dependency.getVulnerabilities().size()>0 || $dependency.getSuppressedVulnerabilities().size()>0)
,"vulnerabilities": [
#foreach($vuln in $dependency.getVulnerabilities())
#if($foreach.count > 1),#end {
"name": "$enc.json($vuln.name)",
"cvssScore": "$vuln.cvssScore",
"cvssAccessVector": "$enc.json($vuln.cvssAccessVector)",
"cvssAccessComplexity": "$enc.json($vuln.cvssAccessComplexity)",
"cvssAuthenticationr": "$enc.json($vuln.cvssAuthentication)",
"cvssConfidentialImpact": "$enc.json($vuln.cvssConfidentialityImpact)",
"cvssIntegrityImpact": "$enc.json($vuln.cvssIntegrityImpact)",
"cvssAvailabilityImpact": "$enc.json($vuln.cvssAvailabilityImpact)",
#if ($vuln.cvssScore<4.0)
"severity": "Low",
#elseif ($vuln.cvssScore>=7.0)
"severity": "High",
#else
"severity": "Medium",
#end
#if($vuln.cwe)"cwe": "$enc.json($vuln.cwe)",#end
"description": "$enc.json($vuln.description)",
#if ($vuln.notes)"notes": "$enc.json($vuln.notes)"#end
"references": [
#foreach($ref in $vuln.getReferences())
#if($foreach.count > 1),#end {
"source": "$enc.json($ref.source)",
"url": "$enc.json($ref.url)",
"name": "$enc.json($ref.name)"
}
#end
],
"vulnerableSoftware": [
#foreach($vs in $vuln.getVulnerableSoftware())
#if($foreach.count > 1),#end {
#if($vs.hasPreviousVersion()) "allPreviousVersion": "true",#end
"software": "$enc.json($vs.name)"
}
#end
]
}#end
]
#end
#if($dependency.getSuppressedVulnerabilities().size()>0 || $dependency.getSuppressedVulnerabilities().size()>0)
,"suppressedVulnerabilities": [
#foreach($vuln in $dependency.getSuppressedVulnerabilities())#if($foreach.count > 1),#end {
"name": "$enc.json($vuln.name)",
"cvssScore": "$vuln.cvssScore",
"cvssAccessVector": "$enc.json"($vuln.cvssAccessVector),
"cvssAccessComplexity": "$enc.json"($vuln.cvssAccessComplexity),
"cvssAuthenticationr": "$enc.json"($vuln.cvssAuthentication),
"cvssConfidentialImpact": "$enc.json"($vuln.cvssConfidentialityImpact),
"cvssIntegrityImpact": "$enc.json"($vuln.cvssIntegrityImpact),
"cvssAvailabilityImpact": "$enc.json"($vuln.cvssAvailabilityImpact),
#if ($vuln.cvssScore<4.0) "severity": "Low",
#elseif ($vuln.cvssScore>=7.0) "severity": "High",
#else "severity": "Medium",
#end
#if ($vuln.cwe)"cwe": "$enc.json($vuln.cwe)",#end
"description": "$enc.json($vuln.description)"
#if ($vuln.notes),"notes": "$enc.json($vuln.notes)"#end
,"references" [
#foreach($ref in $vuln.getReferences())
#if($foreach.count > 1),#end {
"source": "$enc.json($ref.source)",
"url": "$enc.json($ref.url)",
"name": "$enc.json($ref.name)"
}
#end
],
"vulnerableSoftware": [
#foreach($vs in $vuln.getVulnerableSoftware())
#if($foreach.count > 1),#end {
#if($vs.hasPreviousVersion()) "allPreviousVersion": "true"#end,
"name": "$enc.json($vs.name)"
}
#end
]
}
#end
}
#end
}
#end
]
}
}

View File

@@ -644,6 +644,11 @@ Copyright (c) 2012 - Jeremy Long
<artifactId>annotations</artifactId>
<version>3.0.1u2</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>