diff --git a/core/src/main/java/org/owasp/dependencycheck/reporting/ReportGenerator.java b/core/src/main/java/org/owasp/dependencycheck/reporting/ReportGenerator.java index 041c3c07367..0f08ce1f362 100644 --- a/core/src/main/java/org/owasp/dependencycheck/reporting/ReportGenerator.java +++ b/core/src/main/java/org/owasp/dependencycheck/reporting/ReportGenerator.java @@ -124,7 +124,13 @@ public enum Format { /** * Generate JUNIT report. */ - JUNIT + JUNIT, + /** + * Generate Report in GitLab dependency check format: + * @see format definition + * @see additional explantions on the format + */ + GITLAB } /** @@ -251,6 +257,7 @@ private VelocityContext createContext(String applicationName, List d final String scanDate = DateTimeFormatter.RFC_1123_DATE_TIME.format(dt); final String scanDateXML = DateTimeFormatter.ISO_INSTANT.format(dt); final String scanDateJunit = DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(dt); + final String scanDateGitLab = DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(dt.withNano(0)); final VelocityContext ctxt = new VelocityContext(); ctxt.put("applicationName", applicationName); @@ -261,6 +268,7 @@ private VelocityContext createContext(String applicationName, List d ctxt.put("scanDate", scanDate); ctxt.put("scanDateXML", scanDateXML); ctxt.put("scanDateJunit", scanDateJunit); + ctxt.put("scanDateGitLab", scanDateGitLab); ctxt.put("enc", new EscapeTool()); ctxt.put("rpt", new ReportTool()); ctxt.put("checksum", Checksum.class); @@ -394,6 +402,9 @@ public static File getReportFile(String outputLocation, Format format) { if (format == Format.SARIF && !pathToCheck.endsWith(".sarif")) { return new File(outFile, "dependency-check-report.sarif"); } + if (format == Format.GITLAB && !pathToCheck.endsWith(".json")) { + return new File(outFile, "dependency-check-gitlab.json"); + } return outFile; } diff --git a/core/src/main/java/org/owasp/dependencycheck/reporting/ReportTool.java b/core/src/main/java/org/owasp/dependencycheck/reporting/ReportTool.java index cc553298e91..6247764b83c 100644 --- a/core/src/main/java/org/owasp/dependencycheck/reporting/ReportTool.java +++ b/core/src/main/java/org/owasp/dependencycheck/reporting/ReportTool.java @@ -122,7 +122,7 @@ private String determineScore(Vulnerability vuln) { return "Unknown"; } - private String normalizeSeverity(String sev) { + public String normalizeSeverity(String sev) { switch (sev) { case "critical": return "Critical"; diff --git a/core/src/main/resources/templates/gitlabReport.vsl b/core/src/main/resources/templates/gitlabReport.vsl new file mode 100644 index 00000000000..c0bcd5d5999 --- /dev/null +++ b/core/src/main/resources/templates/gitlabReport.vsl @@ -0,0 +1,152 @@ +{ + "version": "15.0.6", + "schema": "https://gitlab.com/gitlab-org/security-products/security-report-schemas/-/raw/v15.0.6/dist/dependency-scanning-report-format.json?ref_type=tags", ##todo + "scan": { + ## this describes the tool responsible for scanning + "scanner": { + "id": "org.owasp.dependency-check", + "name": "Dependency-Check Core", + "version": "$enc.json($version)", + "vendor": { + "name": "OWASP" + }, + + ## optional properties + "url": "https://github.com/jeremylong/DependencyCheck/" + }, + ## this describes the tool responsible for interpreting the scan result + ## in our case it's the same as the scanner + "analyzer": { + "id": "org.owasp.dependency-check", + "name": "Dependency-Check Core", + "version": "$enc.json($version)", + "vendor": { + "name": "OWASP" + }, + + ## optional properties + "url": "https://github.com/jeremylong/DependencyCheck/" + }, + + "end_time": "$enc.json($scanDateGitLab)", + ## we don't acutally have the real start time, so this is the best we can do + "start_time": "$enc.json($scanDateGitLab)", + ## we only generate a scan report, if the scan has successfully finished + "status": #if($exceptions) "failure" #else "success" #end , + ## this is the only type of scan there is according to the format definition + "type": "dependency_scanning" + + ## optional properties + ## "messages": [], --> not implemented + ##"options": [], --> not implemented + ##"primary_identifiers": [], --> not implemented + }, + "vulnerabilities": [ + #set( $vulnerability_first = true ) + #foreach( $dependency in $dependencies ) + #if( $dependency.vulnerabilities.size() != 0 ) + #foreach( $vulnerability in $dependency.getVulnerabilities(true) ) + ## make sure to insert comma between array elements + #if( $vulnerability_first == true ) + #set( $vulnerability_first = false ) + #else + , + #end + ## ((List)context.get("dependencies")).get(5).getVulnerabilities().stream().collect(Collectors.toList()).get(0) + { + "id": "$enc.json($vulnerability.name)", + "identifiers": [ + { + "type": "$enc.json($vulnerability.getSource().name())" + #if( $vulnerability.getSource().name().equals("NVD") ) + , "name": "$enc.json($vulnerability.name)" + #elseif( $vulnerability.getSource().name().equals("NPM") ) + , "name": "$enc.json($vulnerability.name) (NPM)" + #else + , "name": "$enc.json($vulnerability.name)" + #end + , "value": "$enc.json($dependency.Sha1sum)" + + ## optional properties + #if( $vulnerability.getSource().name().equals("NVD") ) + , "url": "https://web.nvd.nist.gov/view/vuln/detail?vulnId=$enc.url($vulnerability.name)" + #elseif( $vulnerability.getSource().name().equals("NPM") ) + , "url": "https://github.com/advisories/$enc.url($vulnerability.name)" + #end + } + ], + "location": { + "file": "$enc.json($dependency.filePath)", + "dependency": { + "package": { + "name": "$enc.json($dependency.name)" + }, + "version": "$enc.json($dependency.version)" + ## optional properties + ## "iid": "", --> not implemented + ## "direct": false, --> not implemented + ## we don't have a good way of assigning iids, so this won't work + ##"dependency_path": [ + ## #foreach( $inc in $dependency.includedBy ) + ## { + ## "iid": + ## } + ## #if( $foreach.hasNext ),#end + ## #end + ##] + } + }, + + ## optional properties + "name": "$enc.json($vulnerability.name)", + "description": "$enc.json($vulnerability.description)", + "severity": "$rpt.normalizeSeverity($vulnerability.cvssV3.getBaseSeverity().toLowerCase())", + ## "solution": "" --> not implemented + "links": [ + #foreach( $ref in $vulnerability.getReferences(true) ) + { + "name": "$enc.json($ref.name)", + + ## optional properties + "url": "$enc.json($ref.url)" + } + #if( $foreach.hasNext ),#end + #end + ] + ## "details": [], --> not implemented + ## "tracking": {}, --> not implemented + ## "flags": [], --> not implemented. + } + #end + #end + #end + ], + "dependency_files": [ + ## for lack of better knowledge, we just assume we have only scanned a single pom.xml fileā€¦ + { + "path": "pom.xml", + "package_manager": "maven", + "dependencies": [ + #foreach( $dependency in $dependencies ) + { + "package": { + "name": "$enc.json($dependency.name)" + }, + "version": "$enc.json($dependency.version)" + + ## optional properties + ## "iid": number, --> not implemtend + ##"direct": false, --> not implemeten + ##"dependency_path": [] --> not implemented + } + #if( $foreach.hasNext ),#end + #end + ] + ## no optional properties + } + ], + + ## optional properties + "remediations": [] ## not implemented + +}