mirror of
https://github.com/ysoftdevs/DependencyCheck.git
synced 2026-01-16 16:46:55 +01:00
implemented CSV reports per #675
This commit is contained in:
@@ -146,7 +146,7 @@ public class Check extends Update {
|
||||
private boolean updateOnly = false;
|
||||
|
||||
/**
|
||||
* The report format to be generated (HTML, XML, VULN, ALL). Default is
|
||||
* The report format to be generated (HTML, XML, VULN, CSV, JSON, ALL). Default is
|
||||
* HTML.
|
||||
*/
|
||||
private String reportFormat = "HTML";
|
||||
@@ -1102,7 +1102,7 @@ public class Check extends Update {
|
||||
}
|
||||
|
||||
/**
|
||||
* An enumeration of supported report formats: "ALL", "HTML", "XML", "VULN",
|
||||
* An enumeration of supported report formats: "ALL", "HTML", "XML", "CSV", "JSON", "VULN",
|
||||
* etc..
|
||||
*/
|
||||
public static class ReportFormats extends EnumeratedAttribute {
|
||||
|
||||
@@ -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, JSON, HTML, VULN, or ALL", format);
|
||||
+ "Supported output formats are HTML, XML, CSV, JSON, VULN, or ALL", format);
|
||||
throw new ParseException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,9 @@ package org.owasp.dependencycheck.reporting;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Set;
|
||||
import org.apache.commons.lang3.StringEscapeUtils;
|
||||
import org.owasp.dependencycheck.dependency.Identifier;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -94,4 +96,71 @@ public class EscapeTool {
|
||||
}
|
||||
return StringEscapeUtils.escapeJson(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats text for CSV format. This includes trimming whitespace, replace
|
||||
* line breaks with spaces, and if necessary quotes the text and/or escapes
|
||||
* contained quotes.
|
||||
*
|
||||
* @param text the text to escape and quote
|
||||
* @return the escaped and quoted text
|
||||
*/
|
||||
public String csv(String text) {
|
||||
if (text == null || text.isEmpty()) {
|
||||
return text;
|
||||
}
|
||||
return StringEscapeUtils.escapeCsv(text.trim().replace("\n", " "));
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a set of Identifiers, filters them to none CPE, and formats them
|
||||
* for display in a CSV.
|
||||
*
|
||||
* @param ids the set of identifiers
|
||||
* @return the formated list of none CPE identifiers
|
||||
*/
|
||||
public String csvIdentifiers(Set<Identifier> ids) {
|
||||
if (ids == null || ids.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
boolean addComma = false;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Identifier id : ids) {
|
||||
if (!"cpe".equals(id.getType())) {
|
||||
if (addComma) {
|
||||
sb.append(", ");
|
||||
} else {
|
||||
addComma = true;
|
||||
}
|
||||
sb.append(id.getValue());
|
||||
}
|
||||
}
|
||||
return StringEscapeUtils.escapeCsv(sb.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a set of Identifiers, filters them to just CPEs, and formats them
|
||||
* for display in a CSV.
|
||||
*
|
||||
* @param ids the set of identifiers
|
||||
* @return the formated list of CPE identifiers
|
||||
*/
|
||||
public String csvCpe(Set<Identifier> ids) {
|
||||
if (ids == null || ids.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
boolean addComma = false;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Identifier id : ids) {
|
||||
if ("cpe".equals(id.getType())) {
|
||||
if (addComma) {
|
||||
sb.append(", ");
|
||||
} else {
|
||||
addComma = true;
|
||||
}
|
||||
sb.append(id.getValue());
|
||||
}
|
||||
}
|
||||
return StringEscapeUtils.escapeCsv(sb.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +81,11 @@ public class ReportGenerator {
|
||||
/**
|
||||
* Generate JSON report.
|
||||
*/
|
||||
JSON
|
||||
JSON,
|
||||
/**
|
||||
* Generate CSV report.
|
||||
*/
|
||||
CSV
|
||||
}
|
||||
/**
|
||||
* The Velocity Engine.
|
||||
@@ -191,6 +195,9 @@ public class ReportGenerator {
|
||||
if (format == Format.JSON || format == Format.ALL) {
|
||||
generateReport("JsonReport", outputStream);
|
||||
}
|
||||
if (format == Format.CSV || format == Format.ALL) {
|
||||
generateReport("CsvReport", outputStream);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -209,6 +216,9 @@ public class ReportGenerator {
|
||||
generateReport("JsonReport", outputDir + File.separator + "dependency-check-report.json");
|
||||
pretifyJson(outputDir + File.separator + "dependency-check-report.json");
|
||||
}
|
||||
if (format == Format.CSV || format == Format.ALL) {
|
||||
generateReport("CsvReport", outputDir + File.separator + "dependency-check-report.csv");
|
||||
}
|
||||
if (format == Format.HTML || format == Format.ALL) {
|
||||
generateReport("HtmlReport", outputDir + File.separator + "dependency-check-report.html");
|
||||
}
|
||||
@@ -344,6 +354,13 @@ public class ReportGenerator {
|
||||
generateReports(outputDir, Format.JSON);
|
||||
}
|
||||
}
|
||||
if ("CSV".equalsIgnoreCase(format)) {
|
||||
if (pathToCheck.endsWith(".csv")) {
|
||||
generateReport("CsvReport", outputDir);
|
||||
} else {
|
||||
generateReports(outputDir, Format.JSON);
|
||||
}
|
||||
}
|
||||
if ("ALL".equalsIgnoreCase(format)) {
|
||||
generateReports(outputDir, Format.ALL);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#**
|
||||
This file is part of Dependency-Check.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Copyright (c) 2017 Jeremy Long. All Rights Reserved.
|
||||
|
||||
@author Jeremy Long <jeremy.long@owasp.org>
|
||||
@version 1 *###
|
||||
"Project","ScanDate","DependencyName","DependencyPath","Description","License","Md5","Sha1","Identifiers","CPE","CVE","CWE","Vulnerability","Severity","CVSSv2"
|
||||
#macro(writeSev $score)#if($score<4.0)"Low"#elseif($score>=7.0)"High"#else"Medium"#end#end
|
||||
#foreach($dependency in $dependencies)#if($dependency.getVulnerabilities().size()>0)
|
||||
#foreach($vuln in $dependency.getVulnerabilities())
|
||||
$enc.csv($applicationName),$enc.csv($scanDate),$enc.csv($dependency.DisplayFileName),#if($dependency.FilePath)$enc.csv($dependency.FilePath)#end,#if($dependency.description)$enc.csv($dependency.description)#end,#if($dependency.license)$enc.csv($dependency.license)#end,#if($dependency.Md5sum)$enc.csv($dependency.Md5sum)#end,#if($dependency.Sha1sum)$enc.csv($dependency.Sha1sum)#end,#if($dependency.identifiers)$enc.csvIdentifiers($dependency.identifiers)#end,#if($dependency.identifiers)$enc.csvCpe($dependency.identifiers)#end,#if($vuln.name)$enc.csv($vuln.name)#end,#if($dependency.cwe)$enc.csv($vuln.cwe)#end,#if($vuln.description)$enc.csv($vuln.description)#end,#writeSev($vuln.cvssScore),$vuln.cvssScore
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright (c) 2017 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.reporting;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
import org.owasp.dependencycheck.dependency.Identifier;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jerem
|
||||
*/
|
||||
public class EscapeToolTest {
|
||||
|
||||
/**
|
||||
* Test of url method, of class EscapeTool.
|
||||
*/
|
||||
@Test
|
||||
public void testUrl() {
|
||||
String text = null;
|
||||
EscapeTool instance = new EscapeTool();
|
||||
String expResult = null;
|
||||
String result = instance.url(text);
|
||||
assertEquals(expResult, result);
|
||||
|
||||
text = "";
|
||||
expResult = "";
|
||||
result = instance.url(text);
|
||||
assertEquals(expResult, result);
|
||||
|
||||
text = " ";
|
||||
expResult = "+";
|
||||
result = instance.url(text);
|
||||
assertEquals(expResult, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of html method, of class EscapeTool.
|
||||
*/
|
||||
@Test
|
||||
public void testHtml() {
|
||||
EscapeTool instance = new EscapeTool();
|
||||
String text = null;
|
||||
String expResult = null;
|
||||
String result = instance.html(text);
|
||||
assertEquals(expResult, result);
|
||||
|
||||
text = "";
|
||||
expResult = "";
|
||||
result = instance.html(text);
|
||||
assertEquals(expResult, result);
|
||||
|
||||
text = "<div>";
|
||||
expResult = "<div>";
|
||||
result = instance.html(text);
|
||||
assertEquals(expResult, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of xml method, of class EscapeTool.
|
||||
*/
|
||||
@Test
|
||||
public void testXml() {
|
||||
EscapeTool instance = new EscapeTool();
|
||||
String text = null;
|
||||
String expResult = null;
|
||||
String result = instance.xml(text);
|
||||
assertEquals(expResult, result);
|
||||
|
||||
text = "";
|
||||
expResult = "";
|
||||
result = instance.xml(text);
|
||||
assertEquals(expResult, result);
|
||||
|
||||
text = "<div>";
|
||||
expResult = "<div>";
|
||||
result = instance.xml(text);
|
||||
assertEquals(expResult, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of json method, of class EscapeTool.
|
||||
*/
|
||||
@Test
|
||||
public void testJson() {
|
||||
String text = null;
|
||||
EscapeTool instance = new EscapeTool();
|
||||
String expResult = null;
|
||||
String result = instance.json(text);
|
||||
assertEquals(expResult, result);
|
||||
|
||||
text = "";
|
||||
expResult = "";
|
||||
result = instance.json(text);
|
||||
assertEquals(expResult, result);
|
||||
|
||||
text = "test \"quote\"\"";
|
||||
expResult = "test \\\"quote\\\"\\\"";
|
||||
result = instance.json(text);
|
||||
assertEquals(expResult, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of csv method, of class EscapeTool.
|
||||
*/
|
||||
@Test
|
||||
public void testCsv() {
|
||||
String text = null;
|
||||
EscapeTool instance = new EscapeTool();
|
||||
String expResult = null;
|
||||
String result = instance.csv(text);
|
||||
assertEquals(expResult, result);
|
||||
|
||||
text = "";
|
||||
expResult = "";
|
||||
result = instance.csv(text);
|
||||
assertEquals(expResult, result);
|
||||
|
||||
text = "one, two";
|
||||
expResult = "\"one, two\"";
|
||||
result = instance.csv(text);
|
||||
assertEquals(expResult, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of csvIdentifiers method, of class EscapeTool.
|
||||
*/
|
||||
@Test
|
||||
public void testCsvIdentifiers() {
|
||||
EscapeTool instance = new EscapeTool();
|
||||
Set<Identifier> ids = null;
|
||||
String expResult = "";
|
||||
String result = instance.csvIdentifiers(ids);
|
||||
assertEquals(expResult, result);
|
||||
|
||||
ids = new HashSet<>();
|
||||
expResult = "";
|
||||
result = instance.csvIdentifiers(ids);
|
||||
assertEquals(expResult, result);
|
||||
|
||||
ids = new HashSet<>();
|
||||
ids.add(new Identifier("cpe", "cpe:/a:somegroup:something:1.0", ""));
|
||||
expResult = "";
|
||||
result = instance.csvIdentifiers(ids);
|
||||
assertEquals(expResult, result);
|
||||
|
||||
ids = new HashSet<>();
|
||||
ids.add(new Identifier("gav", "somegroup:something:1.0", ""));
|
||||
expResult = "somegroup:something:1.0";
|
||||
result = instance.csvIdentifiers(ids);
|
||||
assertEquals(expResult, result);
|
||||
|
||||
ids = new HashSet<>();
|
||||
ids.add(new Identifier("cpe", "cpe:/a:somegroup:something:1.0", ""));
|
||||
ids.add(new Identifier("gav", "somegroup:something:1.0", ""));
|
||||
expResult = "somegroup:something:1.0";
|
||||
result = instance.csvIdentifiers(ids);
|
||||
assertEquals(expResult, result);
|
||||
|
||||
ids = new HashSet<>();
|
||||
ids.add(new Identifier("cpe", "cpe:/a:somegroup:something:1.0", ""));
|
||||
ids.add(new Identifier("gav", "somegroup:something:1.0", ""));
|
||||
ids.add(new Identifier("gav", "somegroup2:something:1.2", ""));
|
||||
expResult = "\"somegroup:something:1.0, somegroup2:something:1.2\"";
|
||||
String expResult2 = "\"somegroup2:something:1.2, somegroup:something:1.0\"";
|
||||
result = instance.csvIdentifiers(ids);
|
||||
assertTrue(expResult.equals(result) || expResult2.equals(result));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of csvCpe method, of class EscapeTool.
|
||||
*/
|
||||
@Test
|
||||
public void testCsvCpe() {
|
||||
EscapeTool instance = new EscapeTool();
|
||||
Set<Identifier> ids = null;
|
||||
String expResult = "";
|
||||
String result = instance.csvCpe(ids);
|
||||
assertEquals(expResult, result);
|
||||
|
||||
ids = new HashSet<>();
|
||||
expResult = "";
|
||||
result = instance.csvCpe(ids);
|
||||
assertEquals(expResult, result);
|
||||
|
||||
ids = new HashSet<>();
|
||||
ids.add(new Identifier("gav", "somegroup:something:1.0", ""));
|
||||
expResult = "";
|
||||
result = instance.csvCpe(ids);
|
||||
assertEquals(expResult, result);
|
||||
|
||||
ids = new HashSet<>();
|
||||
ids.add(new Identifier("cpe", "cpe:/a:somegroup:something:1.0", ""));
|
||||
expResult = "cpe:/a:somegroup:something:1.0";
|
||||
result = instance.csvCpe(ids);
|
||||
assertEquals(expResult, result);
|
||||
|
||||
ids = new HashSet<>();
|
||||
ids.add(new Identifier("cpe", "cpe:/a:somegroup:something:1.0", ""));
|
||||
ids.add(new Identifier("gav", "somegroup:something:1.0", ""));
|
||||
expResult = "cpe:/a:somegroup:something:1.0";
|
||||
result = instance.csvCpe(ids);
|
||||
assertEquals(expResult, result);
|
||||
|
||||
ids = new HashSet<>();
|
||||
ids.add(new Identifier("cpe", "cpe:/a:somegroup:something:1.0", ""));
|
||||
ids.add(new Identifier("gav", "somegroup:something:1.0", ""));
|
||||
ids.add(new Identifier("cpe", "cpe:/a:somegroup2:something:1.2", ""));
|
||||
expResult = "\"cpe:/a:somegroup:something:1.0, cpe:/a:somegroup2:something:1.2\"";
|
||||
String expResult2 = "\"cpe:/a:somegroup2:something:1.2, cpe:/a:somegroup:something:1.0\"";
|
||||
result = instance.csvCpe(ids);
|
||||
assertTrue(expResult.equals(result) || expResult2.equals(result));
|
||||
}
|
||||
}
|
||||
@@ -757,6 +757,10 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma
|
||||
return "dependency-check-report.xml#";
|
||||
} else if ("VULN".equalsIgnoreCase(this.format)) {
|
||||
return "dependency-check-vulnerability";
|
||||
} else if ("JSON".equalsIgnoreCase(this.format)) {
|
||||
return "dependency-check-report.json";
|
||||
} else if ("CSV".equalsIgnoreCase(this.format)) {
|
||||
return "dependency-check-report.csv";
|
||||
} else {
|
||||
getLog().warn("Unknown report format used during site generation.");
|
||||
return "dependency-check-report";
|
||||
|
||||
Reference in New Issue
Block a user