Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Trivy #3259

Merged
merged 21 commits into from
Feb 21, 2024
Merged

Trivy #3259

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -58,6 +58,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 @@ -108,11 +108,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