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

Composer Repository Vulnerability Mirroring #4515

Draft
wants to merge 37 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
52afcc8
Composer: add vulnerability mirroring
valentijnscholten Jan 2, 2025
1448669
Composer: add vulnerability mirroring - parsing complete
valentijnscholten Jan 3, 2025
f148fe3
Composer: add vulnerability mirroring - parsing complete
valentijnscholten Jan 3, 2025
e24742d
Composer: add vulnerability mirroring - alias complete
valentijnscholten Jan 3, 2025
570cd9b
Composer: add vulnerability mirroring - alias complete
valentijnscholten Jan 3, 2025
ca1315a
Composer vulnz
valentijnscholten Jan 4, 2025
4f7c961
Composer: add vulnerability mirroring - drupal complete
valentijnscholten Jan 4, 2025
b27cce3
Composer: add vulnerability mirroring - API
valentijnscholten Jan 4, 2025
273a023
Composer: add vulnerability mirroring - API
valentijnscholten Jan 4, 2025
ca8a31a
Composer: add vulnerability mirroring - API
valentijnscholten Jan 4, 2025
8620444
Composer: add vulnerability mirroring - error handling
valentijnscholten Jan 4, 2025
55a35fb
Composer: add vulnerability mirroring - error handling
valentijnscholten Jan 4, 2025
2def2e7
Composer: add vulnerability mirroring - error handling
valentijnscholten Jan 4, 2025
21a4e74
Composer: add vulnerability mirroring - cleanup
valentijnscholten Jan 4, 2025
f9a908b
Composer: add vulnerability mirroring - cleanup
valentijnscholten Jan 5, 2025
f909839
Composer: add vulnerability mirroring - fix test cases that use index…
valentijnscholten Jan 5, 2025
97c3164
Composer: add vulnerability mirroring - cleanup
valentijnscholten Jan 5, 2025
465910d
consistent naming + testcases
valentijnscholten Jan 6, 2025
509934c
consistent naming + testcases
valentijnscholten Jan 6, 2025
e09110d
consistent naming + testcases
valentijnscholten Jan 6, 2025
b3d4494
unorganize imports
valentijnscholten Jan 8, 2025
5f3cfc2
lingintg
valentijnscholten Jan 8, 2025
1ec5dce
add Repository Config test
valentijnscholten Jan 8, 2025
5f10cab
refactor a bit
valentijnscholten Jan 8, 2025
a38e940
add composer advisory mirroring docs
valentijnscholten Jan 11, 2025
8a3fff3
cleanup
valentijnscholten Jan 12, 2025
5e8f028
cleanup
valentijnscholten Jan 12, 2025
8434323
add end-to-end tests, extract source
valentijnscholten Jan 25, 2025
2dd94ee
aliases working
valentijnscholten Jan 25, 2025
8a28f46
attribution fixed
valentijnscholten Jan 25, 2025
d2c2b83
attribution fixed
valentijnscholten Jan 25, 2025
804a8dc
attribution fixed
valentijnscholten Jan 26, 2025
291ec09
remove import
valentijnscholten Jan 26, 2025
31ab710
finalization
valentijnscholten Jan 26, 2025
3ed6b9a
github runner cannot compile why
valentijnscholten Jan 26, 2025
4134bc2
fix rebase induced compile error
valentijnscholten Jan 26, 2025
682976a
fix rebase induced compile error
valentijnscholten Jan 26, 2025
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
14 changes: 14 additions & 0 deletions docs/_docs/datasources/composer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
title: Composer Advisories
category: Datasources
chapter: 4
order: 11
---

[Composer Advisories](https://blog.packagist.com/discover-security-advisories-with-composers-audit-command/) (PKSA) is a database of CVEs and other vulnerabilities affecting the Composer packages. Advisories may or may not be documented in the [National Vulnerability Database]({{ site.baseurl }}{% link _docs/datasources/nvd.md %}).

Dependency-Track integrates with Composer by mirroring advisories via each repositories [Packagist API](https://packagist.org/apidoc).
The mirroring (and alias synchronization) can be enabled/disabled [per repository]({{ site.baseurl }}{% link _docs/datasources/repositories.md %}).
The mirror is refreshed daily, or upon restart of the Dependency-Track instance.

![GitHub Advisories Configuration](../../images/screenshots/github-advisories-configuration.png)
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) OWASP Foundation. All Rights Reserved.
*/
package org.dependencytrack.event;

import java.util.UUID;

import alpine.event.framework.SingletonCapableEvent;

/**
* Defines an event used to start a mirror of the NVD.
*
* @author Valentijn Scholten
* @since 4.13.0
*/
public class ComposerAdvisoryMirrorEvent extends SingletonCapableEvent {

private static final UUID CHAIN_IDENTIFIER = UUID.fromString("62710465-3117-4cc1-ad9a-d1dce721aa86");

public ComposerAdvisoryMirrorEvent() {
setChainIdentifier(CHAIN_IDENTIFIER);
setSingleton(true);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import org.dependencytrack.tasks.metrics.PortfolioMetricsUpdateTask;
import org.dependencytrack.tasks.metrics.ProjectMetricsUpdateTask;
import org.dependencytrack.tasks.metrics.VulnerabilityMetricsUpdateTask;
import org.dependencytrack.tasks.repositories.ComposerAdvisoryMirrorTask;
import org.dependencytrack.tasks.repositories.RepositoryMetaAnalyzerTask;
import org.dependencytrack.tasks.scanners.InternalAnalysisTask;
import org.dependencytrack.tasks.scanners.OssIndexAnalysisTask;
Expand Down Expand Up @@ -99,6 +100,7 @@ public void contextInitialized(final ServletContextEvent event) {
EVENT_SERVICE.subscribe(GitHubAdvisoryMirrorEvent.class, GitHubAdvisoryMirrorTask.class);
EVENT_SERVICE.subscribe(OsvMirrorEvent.class, OsvDownloadTask.class);
EVENT_SERVICE.subscribe(VulnDbSyncEvent.class, VulnDbSyncTask.class);
EVENT_SERVICE.subscribe(ComposerAdvisoryMirrorEvent.class, ComposerAdvisoryMirrorTask.class);
EVENT_SERVICE.subscribe(VulnDbAnalysisEvent.class, VulnDbAnalysisTask.class);
EVENT_SERVICE.subscribe(VulnerabilityAnalysisEvent.class, VulnerabilityAnalysisTask.class);
EVENT_SERVICE.subscribe(PortfolioVulnerabilityAnalysisEvent.class, VulnerabilityAnalysisTask.class);
Expand Down Expand Up @@ -141,6 +143,7 @@ public void contextDestroyed(final ServletContextEvent event) {
EVENT_SERVICE.unsubscribe(InternalAnalysisTask.class);
EVENT_SERVICE.unsubscribe(OssIndexAnalysisTask.class);
EVENT_SERVICE.unsubscribe(GitHubAdvisoryMirrorTask.class);
EVENT_SERVICE.unsubscribe(ComposerAdvisoryMirrorTask.class);
EVENT_SERVICE.unsubscribe(OsvDownloadTask.class);
EVENT_SERVICE.unsubscribe(VulnDbSyncTask.class);
EVENT_SERVICE.unsubscribe(VulnDbAnalysisTask.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,11 @@ public enum ConfigPropertyConstants {
NOTIFICATION_TEMPLATE_BASE_DIR("notification", "template.baseDir", SystemUtils.getEnvironmentVariable("DEFAULT_TEMPLATES_OVERRIDE_BASE_DIRECTORY", System.getProperty("user.home")), PropertyType.STRING, "The base directory to use when searching for notification templates"),
NOTIFICATION_TEMPLATE_DEFAULT_OVERRIDE_ENABLED("notification", "template.default.override.enabled", SystemUtils.getEnvironmentVariable("DEFAULT_TEMPLATES_OVERRIDE_ENABLED", "false"), PropertyType.BOOLEAN, "Flag to enable/disable override of default notification templates"),
TASK_SCHEDULER_LDAP_SYNC_CADENCE("task-scheduler", "ldap.sync.cadence", "6", PropertyType.INTEGER, "Sync cadence (in hours) for LDAP"),
TASK_SCHEDULER_GHSA_MIRROR_CADENCE("task-scheduler", "ghsa.mirror.cadence", "24", PropertyType.INTEGER, "Mirror cadence (in hours) for Github Security Advisories"),
TASK_SCHEDULER_GHSA_MIRROR_CADENCE("task-scheduler", "ghsa.mirror.cadence", "1", PropertyType.INTEGER, "Mirror cadence (in hours) for Github Security Advisories"),
TASK_SCHEDULER_OSV_MIRROR_CADENCE("task-scheduler", "osv.mirror.cadence", "24", PropertyType.INTEGER, "Mirror cadence (in hours) for OSV database"),
TASK_SCHEDULER_NIST_MIRROR_CADENCE("task-scheduler", "nist.mirror.cadence", "24", PropertyType.INTEGER, "Mirror cadence (in hours) for NVD database"),
TASK_SCHEDULER_VULNDB_MIRROR_CADENCE("task-scheduler", "vulndb.mirror.cadence", "24", PropertyType.INTEGER, "Mirror cadence (in hours) for VulnDB database"),
TASK_SCHEDULER_COMPOSER_MIRROR_CADENCE("task-scheduler", "composer.mirror.cadencee", "24", PropertyType.INTEGER, "Mirror cadence (in hours) for Vulnerability mirroring of Composer repositories"),
TASK_SCHEDULER_PORTFOLIO_METRICS_UPDATE_CADENCE("task-scheduler", "portfolio.metrics.update.cadence", "1", PropertyType.INTEGER, "Update cadence (in hours) for portfolio metrics"),
TASK_SCHEDULER_VULNERABILITY_METRICS_UPDATE_CADENCE("task-scheduler", "vulnerability.metrics.update.cadence", "1", PropertyType.INTEGER, "Update cadence (in hours) for vulnerability metrics"),
TASK_SCHEDULER_PORTFOLIO_VULNERABILITY_ANALYSIS_CADENCE("task-scheduler", "portfolio.vulnerability.analysis.cadence", "24", PropertyType.INTEGER, "Launch cadence (in hours) for portfolio vulnerability analysis"),
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/org/dependencytrack/model/Finding.java
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,12 @@ public void addVulnerabilityAliases(List<VulnerabilityAlias> aliases) {
if (alias.getVulnDbId() != null && !alias.getVulnDbId().isBlank()) {
map.put("vulnDbId", alias.getVulnDbId());
}
if (alias.getDrupalId() != null && !alias.getDrupalId().isBlank()) {
map.put("drupalId", alias.getDrupalId());
}
if (alias.getComposerId() != null && !alias.getComposerId().isBlank()) {
map.put("composerId", alias.getComposerId());
}
uniqueAliases.add(map);
}
vulnerability.put("aliases",uniqueAliases);
Expand Down
26 changes: 26 additions & 0 deletions src/main/java/org/dependencytrack/model/Repository.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ public class Repository implements Serializable {
@JsonDeserialize(using = TrimmedStringDeserializer.class)
private String identifier;

@Persistent
@Column(name = "DESCRIPTION")
@JsonDeserialize(using = TrimmedStringDeserializer.class)
private String description;

@Persistent
@Column(name = "URL")
@NotBlank
Expand Down Expand Up @@ -100,6 +105,11 @@ public class Repository implements Serializable {
@Column(name = "PASSWORD")
private String password;

@Persistent
@Column(name = "CONFIG", jdbcType = "CLOB")
@JsonDeserialize(using = TrimmedStringDeserializer.class)
private String config;

@Persistent(customValueStrategy = "uuid")
@Index(name = "REPOSITORY_UUID_IDX") // Cannot be @Unique. Microsoft SQL Server throws an exception
@Column(name = "UUID", jdbcType = "VARCHAR", length = 36, allowsNull = "true")
Expand Down Expand Up @@ -131,6 +141,14 @@ public void setIdentifier(String identifier) {
this.identifier = identifier;
}

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

public String getUrl() {
return url;
}
Expand Down Expand Up @@ -189,6 +207,14 @@ public void setPassword(String password) {
this.password = password;
}

public String getConfig() {
return config;
}

public void setConfig(String config) {
this.config = config;
}

public UUID getUuid() {
return uuid;
}
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/org/dependencytrack/model/Vulnerability.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;

import org.apache.commons.lang3.StringUtils;
import org.dependencytrack.parser.common.resolver.CweResolver;
import org.dependencytrack.persistence.CollectionIntegerConverter;
import org.dependencytrack.resources.v1.serializers.CweDeserializer;
Expand Down Expand Up @@ -111,19 +113,29 @@ public enum Source {
OSV, // Google OSV Advisories
SNYK, // Snyk
TRIVY, // Trivy
COMPOSER, // Composer (Packagist)
DRUPAL, // Drupal
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 (StringUtils.isBlank(id)) {
return UNKNOWN;
}

if (id.startsWith("CVE-")){
return NVD;
} else if (id.startsWith("GHSA-")){
return GITHUB;
} else if (id.startsWith("OSV-")){
return OSV;
} else if (id.startsWith("SA-CORE-") || id.startsWith("SA-CONTRIB")){
return DRUPAL;
} else if (id.startsWith("PKSA-")){
return COMPOSER;
} else if (id.startsWith("SNYK-")){
return SNYK;
}
Expand Down
101 changes: 100 additions & 1 deletion src/main/java/org/dependencytrack/model/VulnerabilityAlias.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
import javax.jdo.annotations.Unique;

import org.apache.commons.lang3.StringUtils;

import java.io.Serializable;
import java.util.Arrays;
import java.util.Map;
Expand Down Expand Up @@ -111,6 +114,20 @@ public class VulnerabilityAlias implements Serializable {
@Pattern(regexp = RegexSequence.Definition.PRINTABLE_CHARS_PLUS, message = "The vulnDbId field may only contain printable characters")
private String vulnDbId;

@Persistent
@Column(name = "DRUPAL_ID")
@Index(name = "VULNERABILITYALIAS_DRUPAL_ID_IDX")
@JsonDeserialize(using = TrimmedStringDeserializer.class)
@Pattern(regexp = RegexSequence.Definition.PRINTABLE_CHARS_PLUS, message = "The drupalId field may only contain printable characters")
private String drupalId;

@Persistent
@Column(name = "COMPOSER_ID")
@Index(name = "VULNERABILITYALIAS_COMPOSER_ID_IDX")
@JsonDeserialize(using = TrimmedStringDeserializer.class)
@Pattern(regexp = RegexSequence.Definition.PRINTABLE_CHARS_PLUS, message = "The composerId field may only contain printable characters")
private String composerId;

@Persistent(customValueStrategy = "uuid")
@Unique(name = "VULNERABILITYALIAS_UUID_IDX")
@Column(name = "UUID", jdbcType = "VARCHAR", length = 36, allowsNull = "false")
Expand Down Expand Up @@ -185,6 +202,22 @@ public void setVulnDbId(String vulnDbId) {
this.vulnDbId = vulnDbId;
}

public String getDrupalId() {
return drupalId;
}

public void setDrupalId(String drupalId) {
this.drupalId = drupalId;
}

public String getComposerId() {
return composerId;
}

public void setComposerId(String composerId) {
this.composerId = composerId;
}

public UUID getUuid() {
return uuid;
}
Expand All @@ -202,6 +235,8 @@ private String getBySource(final Vulnerability.Source source) {
case OSV -> getOsvId();
case SNYK -> getSnykId();
case VULNDB -> getVulnDbId();
case DRUPAL -> getDrupalId();
case COMPOSER -> getComposerId();
default -> null;
};
}
Expand All @@ -224,6 +259,8 @@ public void copyFieldsFrom(VulnerabilityAlias other) {
gsdId = firstNonNull(other.gsdId, gsdId);
vulnDbId = firstNonNull(other.vulnDbId, vulnDbId);
internalId = firstNonNull(other.internalId, internalId);
drupalId = firstNonNull(other.drupalId, drupalId);
composerId = firstNonNull(other.composerId, composerId);
}

private static String firstNonNull(String first, String second) {
Expand Down Expand Up @@ -263,10 +300,71 @@ public int computeMatches(final VulnerabilityAlias other) {
if (this.getVulnDbId() != null && this.getVulnDbId().equals(other.getVulnDbId())) {
matches++;
}
if (this.getDrupalId() != null && this.getDrupalId().equals(other.getDrupalId())) {
matches++;
}
if (this.getComposerId() != null && this.getComposerId().equals(other.getComposerId())) {
matches++;
}

return matches;
}

/**
* Compute how many identifiers are set for this alias record
*
* @return Number of identifiers set
*/
public int countIdentifiers() {
var count = 0;
count += this.getCveId() != null ? 1 : 0;
count += this.getGhsaId() != null ? 1 : 0;
count += this.getGsdId() != null ? 1 : 0;
count += this.getOsvId() != null ? 1 : 0;
count += this.getSnykId() != null ? 1 : 0;
count += this.getSonatypeId() != null ? 1 : 0;
count += this.getVulnDbId() != null ? 1 : 0;
count += this.getDrupalId() != null ? 1 : 0;
count += this.getComposerId() != null ? 1 : 0;
return count;
}

public boolean setAliasFromVulnId(String vulnId) {
if (StringUtils.isBlank(vulnId)) return false;

if (vulnId.startsWith("GHSA") && this.getGhsaId() == null) {
this.setGhsaId(vulnId);
return true;
}
if (vulnId.startsWith("CVE") && this.getCveId() == null) {
this.setCveId(vulnId);
return true;
}
if ((vulnId.startsWith("SA-CORE") || vulnId.startsWith("SA-CONTRIB")) && this.getDrupalId() == null) {
this.setDrupalId(vulnId);
return true;
}
if (vulnId.startsWith("OSSINDEX") && this.getSonatypeId() == null) {
this.setSonatypeId(vulnId);
return true;
}
if (vulnId.startsWith("SNYK") && this.getSnykId() == null) {
this.setSnykId(vulnId);
return true;
}
if (vulnId.startsWith("GSD") && this.getGsdId() == null) {
this.setGsdId(vulnId);
return true;
}
if (vulnId.startsWith("PKSA") && this.getComposerId() == null) {
this.setComposerId(vulnId);
return true;
}

return false;
}


@Override
public String toString() {
return "VulnerabilityAlias{" +
Expand All @@ -279,8 +377,9 @@ public String toString() {
", snykId='" + snykId + '\'' +
", gsdId='" + gsdId + '\'' +
", vulnDbId='" + vulnDbId + '\'' +
", composerId='" + composerId + '\'' +
", drupalId='" + drupalId + '\'' +
", uuid=" + uuid +
'}';
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public static class Title {
public static final String NOTIFICATION_TEST = "Notification Test";
public static final String NVD_MIRROR = "NVD Mirroring";
public static final String GITHUB_ADVISORY_MIRROR = "GitHub Advisory Mirroring";
public static final String COMPOSER_ADVISORY_MIRROR = "Composer Advisory Mirroring";
public static final String EPSS_MIRROR = "EPSS Mirroring";
public static final String NPM_ADVISORY_MIRROR = "NPM Advisory Mirroring";
public static final String VULNDB_MIRROR = "VulnDB Mirroring";
Expand Down
Loading
Loading