Skip to content

Commit

Permalink
Merge pull request #3259 from fnxpt/trivy
Browse files Browse the repository at this point in the history
Trivy
  • Loading branch information
nscuro authored Feb 21, 2024
2 parents c9c1702 + 1cfe0f4 commit 33efa7a
Show file tree
Hide file tree
Showing 33 changed files with 1,911 additions and 1 deletion.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ CI/CD environments.
* [GitHub Advisories]
* [Sonatype OSS Index]
* [Snyk]
* [Trivy]
* [OSV]
* [VulnDB] from [Risk Based Security]
* More coming soon.
Expand Down Expand Up @@ -216,6 +217,7 @@ the [notices] file for more information.
[GitHub Advisories]: https://www.github.com/advisories
[Sonatype OSS Index]: https://ossindex.sonatype.org
[Snyk]: https://snyk.io
[Trivy]: https://www.aquasec.com/products/trivy/
[OSV]: https://osv.dev
[VulnDB]: https://vulndb.cyberriskanalytics.com
[Risk Based Security]: https://www.riskbasedsecurity.com
Expand Down
5 changes: 5 additions & 0 deletions docs/_docs/analysis-types/known-vulnerabilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ This analyzer is applicable to all components with valid Package URLs.
Snyk REST API version is updated every 6 months and can be referred at
[Snyk REST API for PURL](https://apidocs.snyk.io/?version=2022-10-06#get-/orgs/-org_id-/packages/-purl-/issues) for additional information.

### Trivy Analyzer

Trivy analyzer relies on a server trivy instance to perform the analysis using REST API.
Trivy REST API is not publically documented so upgrading to a new version might lead to some issues.

### Analysis Result Cache

Dependency-Track contains an internal limiter which prevents repeated requests to remote services when performing
Expand Down
26 changes: 26 additions & 0 deletions docs/_docs/datasources/trivy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
title: Trivy
category: Datasources
chapter: 4
order: 6
---

[Trivy](https://www.aquasec.com/products/trivy/) is a tool provided by aquas allowing you to scan for vulnerabilities.

Dependency-Track integrates with Trivy using its undocumented REST API.

The Trivy integration is disabled by default.

### Configuration

To configure the Trivy integration, navigate to *Analyzers* -> *Trivy* in the administration panel.

|:---|:----|
| Base URL | Base URL of the Trivy REST API. Defaults to `http://localhost:8081`. |
| API Token | Authentication token for the REST API. |

![Trivy Configuration](../../images/screenshots/trivy-configuration.png)

### Run Trivy as Server

Trivy can be runned as a [server](https://github.com/aquasecurity/trivy/blob/b5874e3ad38e77ac86eedd7a65785b2933f3685f/docs/docs/references/configuration/cli/trivy_server.md) by executing the command `trivy server --listen localhost:8081 --token dummy -d` or by setting it up on a container.
Binary file added docs/images/screenshots/trivy-configuration.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ CI/CD environments.
* [GitHub Advisories]
* [Sonatype OSS Index]
* [Snyk]
* [Trivy]
* [OSV]
* [VulnDB] from [Risk Based Security]
* More coming soon.
Expand Down Expand Up @@ -80,6 +81,7 @@ CI/CD environments.
[GitHub Advisories]: https://www.github.com/advisories
[Sonatype OSS Index]: https://ossindex.sonatype.org
[Snyk]: https://snyk.io
[Trivy]: https://www.aquasec.com/products/trivy/
[OSV]: https://osv.dev
[VulnDB]: https://vulndb.cyberriskanalytics.com
[Risk Based Security]: https://www.riskbasedsecurity.com
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/org/dependencytrack/common/ConfigKey.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ public enum ConfigKey implements Config.Key {
OSSINDEX_RETRY_EXPONENTIAL_BACKOFF_MAX_ATTEMPTS("ossindex.retry.backoff.max.attempts", 10),
OSSINDEX_RETRY_EXPONENTIAL_BACKOFF_MULTIPLIER("ossindex.retry.backoff.multiplier", 2),
OSSINDEX_RETRY_EXPONENTIAL_BACKOFF_MAX_DURATION("ossindex.retry.backoff.max.duration", Duration.ofMinutes(10).toMillis()),
TRIVY_RETRY_EXPONENTIAL_BACKOFF_MAX_ATTEMPTS("trivy.retry.backoff.max.attempts", 10),
TRIVY_RETRY_EXPONENTIAL_BACKOFF_MULTIPLIER("trivy.retry.backoff.multiplier", 2),
TRIVY_RETRY_EXPONENTIAL_BACKOFF_MAX_DURATION("trivy.retry.backoff.max.duration", Duration.ofMinutes(10).toMillis()),
REPO_META_ANALYZER_CACHE_STAMPEDE_BLOCKER_ENABLED("repo.meta.analyzer.cacheStampedeBlocker.enabled", true),
REPO_META_ANALYZER_CACHE_STAMPEDE_BLOCKER_LOCK_BUCKETS("repo.meta.analyzer.cacheStampedeBlocker.lock.buckets", 1000),
REPO_META_ANALYZER_CACHE_STAMPEDE_BLOCKER_MAX_ATTEMPTS("repo.meta.analyzer.cacheStampedeBlocker.max.attempts", 10),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import org.dependencytrack.tasks.scanners.InternalAnalysisTask;
import org.dependencytrack.tasks.scanners.OssIndexAnalysisTask;
import org.dependencytrack.tasks.scanners.SnykAnalysisTask;
import org.dependencytrack.tasks.scanners.TrivyAnalysisTask;
import org.dependencytrack.tasks.scanners.VulnDbAnalysisTask;

import javax.servlet.ServletContextEvent;
Expand Down Expand Up @@ -95,6 +96,7 @@ public void contextInitialized(final ServletContextEvent event) {
EVENT_SERVICE.subscribe(VulnerabilityAnalysisEvent.class, VulnerabilityAnalysisTask.class);
EVENT_SERVICE.subscribe(PortfolioVulnerabilityAnalysisEvent.class, VulnerabilityAnalysisTask.class);
EVENT_SERVICE.subscribe(SnykAnalysisEvent.class, SnykAnalysisTask.class);
EVENT_SERVICE.subscribe(TrivyAnalysisEvent.class, TrivyAnalysisTask.class);
EVENT_SERVICE.subscribe(RepositoryMetaEvent.class, RepositoryMetaAnalyzerTask.class);
EVENT_SERVICE.subscribe(PolicyEvaluationEvent.class, PolicyEvaluationTask.class);
EVENT_SERVICE.subscribe(ComponentMetricsUpdateEvent.class, ComponentMetricsUpdateTask.class);
Expand Down Expand Up @@ -143,6 +145,7 @@ public void contextDestroyed(final ServletContextEvent event) {
EVENT_SERVICE.unsubscribe(PortfolioMetricsUpdateTask.class);
EVENT_SERVICE.unsubscribe(VulnerabilityMetricsUpdateTask.class);
EVENT_SERVICE.unsubscribe(SnykAnalysisTask.class);
EVENT_SERVICE.unsubscribe(TrivyAnalysisTask.class);
EVENT_SERVICE.unsubscribe(CloneProjectTask.class);
EVENT_SERVICE.unsubscribe(FortifySscUploadTask.class);
EVENT_SERVICE.unsubscribe(DefectDojoUploadTask.class);
Expand Down
40 changes: 40 additions & 0 deletions src/main/java/org/dependencytrack/event/TrivyAnalysisEvent.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* This file is part of Dependency-Track.
*
* 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.
*
* SPDX-License-Identifier: Apache-2.0
* Copyright (c) Steve Springett. All Rights Reserved.
*/
package org.dependencytrack.event;

import org.dependencytrack.model.Component;

import java.util.List;

/**
* Defines an event used to start an analysis via Trivy API.
*/
public class TrivyAnalysisEvent extends VulnerabilityAnalysisEvent {

public TrivyAnalysisEvent() { }

public TrivyAnalysisEvent(final Component component) {
super(component);
}

public TrivyAnalysisEvent(final List<Component> components) {
super(components);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ public enum ConfigPropertyConstants {
SCANNER_SNYK_API_VERSION("scanner", "snyk.api.version", "2023-06-22", PropertyType.STRING, "Snyk API version"),
SCANNER_SNYK_CVSS_SOURCE("scanner", "snyk.cvss.source", "NVD", PropertyType.STRING, "Type of source to be prioritized for cvss calculation"),
SCANNER_SNYK_BASE_URL("scanner", "snyk.base.url", "https://api.snyk.io", PropertyType.URL, "Base Url pointing to the hostname and path for Snyk analysis"),
SCANNER_TRIVY_ENABLED("scanner", "trivy.enabled", "false", PropertyType.BOOLEAN, "Flag to enable/disable Trivy Vulnerability Analysis"),
SCANNER_TRIVY_API_TOKEN("scanner", "trivy.api.token", null, PropertyType.ENCRYPTEDSTRING, "The API token used for Trivy API authentication"),
SCANNER_TRIVY_BASE_URL("scanner", "trivy.base.url", "http://localhost:8081", PropertyType.URL, "Base Url pointing to the hostname and path for Trivy analysis"),
SCANNER_TRIVY_IGNORE_UNFIXED("scanner", "trivy.ignore.unfixed", "false", PropertyType.BOOLEAN, "Flag to ignore unfixed vulnerabilities"),
VULNERABILITY_SOURCE_NVD_ENABLED("vuln-source", "nvd.enabled", "true", PropertyType.BOOLEAN, "Flag to enable/disable National Vulnerability Database"),
VULNERABILITY_SOURCE_NVD_FEEDS_URL("vuln-source", "nvd.feeds.url", "https://nvd.nist.gov/feeds", PropertyType.URL, "A base URL pointing to the hostname and path of the NVD feeds"),
VULNERABILITY_SOURCE_NVD_API_ENABLED("vuln-source", "nvd.api.enabled", "false", PropertyType.BOOLEAN, "Whether to enable NVD mirroring via REST API"),
Expand Down
17 changes: 16 additions & 1 deletion src/main/java/org/dependencytrack/model/Vulnerability.java
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,26 @@ public enum Source {
RETIREJS, // Retire.js
INTERNAL, // Internally-managed (and manually entered) vulnerability
OSV, // Google OSV Advisories
SNYK; // Snyk Purl Vulnerability
SNYK, // Snyk Purl Vulnerability
UNKNOWN; // Unknown vulnerability sources

public static boolean isKnownSource(String source) {
return Arrays.stream(values()).anyMatch(enumSource -> enumSource.name().equalsIgnoreCase(source));
}

public static Source resolve(String id) {
if (id.startsWith("CVE-")){
return NVD;
} else if (id.startsWith("GHSA-")){
return GITHUB;
} else if (id.startsWith("OSV-")){
return OSV;
} else if (id.startsWith("SNYK-")){
return SNYK;
}

return UNKNOWN;
}
}

@PrimaryKey
Expand Down
132 changes: 132 additions & 0 deletions src/main/java/org/dependencytrack/parser/trivy/TrivyParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
* This file is part of Dependency-Track.
*
* 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.
*
* SPDX-License-Identifier: Apache-2.0
* Copyright (c) Steve Springett. All Rights Reserved.
*/
package org.dependencytrack.parser.trivy;

import alpine.common.logging.Logger;
import org.dependencytrack.model.Cwe;
import org.dependencytrack.model.Severity;
import org.dependencytrack.model.Vulnerability;
import org.dependencytrack.model.VulnerableSoftware;
import org.dependencytrack.parser.common.resolver.CweResolver;
import org.dependencytrack.parser.trivy.model.CVSS;
import org.dependencytrack.persistence.QueryManager;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;

public class TrivyParser {

private static final Logger LOGGER = Logger.getLogger(TrivyParser.class);

public Vulnerability parse(org.dependencytrack.parser.trivy.model.Vulnerability data, QueryManager qm) {
Vulnerability synchronizedVulnerability = new Vulnerability();
Vulnerability vulnerability = new Vulnerability();
List<VulnerableSoftware> vsList = new ArrayList<>();

vulnerability.setSource(Vulnerability.Source.resolve(data.getVulnerabilityID()));

vulnerability.setPatchedVersions(data.getFixedVersion());

// get the id of the data record (vulnerability)
vulnerability.setVulnId(data.getVulnerabilityID());
vulnerability.setTitle(data.getTitle());
vulnerability.setDescription(data.getDescription());
vulnerability.setSeverity(parseSeverity(data.getSeverity()));

try {
vulnerability.setPublished(parseDate(data.getPublishedDate()));
vulnerability.setCreated(vulnerability.getPublished());
} catch (ParseException ex) {
LOGGER.warn("Unable to parse published date %s".formatted(data.getPublishedDate()));
}

try {
vulnerability.setUpdated(parseDate(data.getLastModifiedDate()));
} catch (ParseException ex) {
LOGGER.warn("Unable to parse last modified date %s".formatted(data.getLastModifiedDate()));
}

vulnerability.setReferences(addReferences(data.getReferences()));

// CWE
for (String id : data.getCweIDS()) {
final Cwe cwe = CweResolver.getInstance().lookup(id);
if (cwe != null) {
vulnerability.addCwe(cwe);
}
}

vulnerability = setCvssScore(data.getCvss().get(data.getSeveritySource()), vulnerability);

return vulnerability;
}

public Date parseDate(String input) throws ParseException {
if (input != null) {
String format = input.length() == 20 ? "yyyy-MM-dd'T'HH:mm:ss'Z'" : "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
SimpleDateFormat formatter = new SimpleDateFormat(format, Locale.ENGLISH);
return formatter.parse(input);
}
return null;
}

public Severity parseSeverity(String severity) {

if (severity != null) {
if (severity.equalsIgnoreCase("CRITICAL")) {
return Severity.CRITICAL;
} else if (severity.equalsIgnoreCase("HIGH")) {
return Severity.HIGH;
} else if (severity.equalsIgnoreCase("MEDIUM")) {
return Severity.MEDIUM;
} else if (severity.equalsIgnoreCase("LOW")) {
return Severity.LOW;
} else {
return Severity.UNASSIGNED;
}
}
return Severity.UNASSIGNED;
}

public Vulnerability setCvssScore(CVSS cvss, Vulnerability vulnerability) {
if (cvss != null) {
vulnerability.setCvssV2Vector(cvss.getV2Vector());
vulnerability.setCvssV3Vector(cvss.getV3Vector());
vulnerability.setCvssV2BaseScore(BigDecimal.valueOf(cvss.getV2Score()));
vulnerability.setCvssV3BaseScore(BigDecimal.valueOf(cvss.getV3Score()));
}

return vulnerability;
}

public String addReferences(String[] references) {
final StringBuilder sb = new StringBuilder();
for (String reference : references) {
if (reference != null) {
sb.append("* [").append(reference).append("](").append(reference).append(")\n");
}
}
return sb.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* This file is part of Dependency-Track.
*
* 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.
*
* SPDX-License-Identifier: Apache-2.0
* Copyright (c) Steve Springett. All Rights Reserved.
*/
package org.dependencytrack.parser.trivy.model;

import java.util.ArrayList;

public class Application {
private String type;
private ArrayList<Library> libraries;

public Application(String type) {
this.type = type;
this.libraries = new ArrayList<Library>();
}

public String getType() { return type; }
public void setType(String value) { this.type = value; }

public ArrayList<Library> getLibraries() { return libraries; }
public void setLibraries(ArrayList<Library> value) { this.libraries = value; }
public void addLibrary(Library value) { this.libraries.add(value); }
}
Loading

0 comments on commit 33efa7a

Please sign in to comment.