Skip to content

Commit

Permalink
Fix /api/v1/vulnerability/source/{source}/vuln/{vuln} returning all…
Browse files Browse the repository at this point in the history
… vulnerable components (#934)
  • Loading branch information
nscuro authored Sep 30, 2024
1 parent 816b742 commit 55fd8df
Show file tree
Hide file tree
Showing 6 changed files with 397 additions and 97 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,10 @@
import org.dependencytrack.model.ComponentMetaInformation;
import org.dependencytrack.model.IntegrityMatchStatus;
import org.dependencytrack.model.License;
import org.dependencytrack.model.OrganizationalContact;
import org.dependencytrack.model.Project;
import org.dependencytrack.persistence.converter.OrganizationalContactsJsonConverter;

import java.util.Date;
import java.util.List;
import java.util.UUID;

public class ComponentProjection {
Expand Down Expand Up @@ -128,7 +126,7 @@ public class ComponentProjection {

public Boolean projectActive;

public List<OrganizationalContact> projectAuthors;
public String projectAuthors;

public String projectCpe;

Expand Down Expand Up @@ -215,7 +213,10 @@ public static Component mapToComponent(ComponentProjection result) {
if (result.projectId != null) {
project.setId(result.projectId);
}
project.setAuthors(result.projectAuthors);
if (result.projectAuthors != null) {
final var converter = new OrganizationalContactsJsonConverter();
project.setAuthors(converter.convertToAttribute(result.projectAuthors));
}
if (result.projectActive != null) {
project.setActive(result.projectActive);
}
Expand Down
237 changes: 200 additions & 37 deletions src/main/java/org/dependencytrack/persistence/jdbi/VulnerabilityDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@

import org.dependencytrack.model.Component;
import org.dependencytrack.model.Vulnerability;
import org.dependencytrack.model.VulnerableSoftware;
import org.dependencytrack.persistence.jdbi.mapping.VulnerabilityRowMapper;
import org.dependencytrack.persistence.jdbi.mapping.VulnerableSoftwareRowMapper;
import org.jdbi.v3.sqlobject.config.RegisterConstructorMapper;
import org.jdbi.v3.sqlobject.config.RegisterFieldMapper;
import org.jdbi.v3.sqlobject.config.RegisterRowMapper;
Expand All @@ -37,6 +39,110 @@
*/
public interface VulnerabilityDao {

@SqlQuery(/* language=InjectedFreeMarker */ """
<#-- @ftlvariable name="uuid" type="Boolean" -->
SELECT "V"."ID"
, "V"."VULNID"
, "V"."SOURCE"
, "V"."FRIENDLYVULNID"
, "V"."TITLE"
, "V"."SUBTITLE"
, "V"."DESCRIPTION"
, "V"."DETAIL"
, "V"."RECOMMENDATION"
, "V"."REFERENCES"
, "V"."CREDITS"
, "V"."CREATED"
, "V"."PUBLISHED"
, "V"."UPDATED"
, CAST(STRING_TO_ARRAY("V"."CWES", ',') AS INT[]) AS "CWES"
, "V"."CVSSV2BASESCORE"
, "V"."CVSSV2IMPACTSCORE"
, "V"."CVSSV2EXPLOITSCORE"
, "V"."CVSSV2VECTOR"
, "V"."CVSSV3BASESCORE"
, "V"."CVSSV3IMPACTSCORE"
, "V"."CVSSV3EXPLOITSCORE"
, "V"."CVSSV3VECTOR"
, "V"."OWASPRRLIKELIHOODSCORE"
, "V"."OWASPRRTECHNICALIMPACTSCORE"
, "V"."OWASPRRBUSINESSIMPACTSCORE"
, "V"."OWASPRRVECTOR"
, "V"."SEVERITY"
, "V"."VULNERABLEVERSIONS"
, "V"."PATCHEDVERSIONS"
, "V"."UUID"
, JSONB_VULN_ALIASES("V"."SOURCE", "V"."VULNID") AS "vulnAliasesJson"
, "EPSS"."SCORE"
, "EPSS"."PERCENTILE"
FROM "VULNERABILITY" AS "V"
LEFT JOIN "EPSS"
ON "V"."VULNID" = "EPSS"."CVE"
WHERE 1 = 1
<#if uuid>
AND "V"."UUID" = :uuid
<#else>
AND "V"."VULNID" = :vulnId
AND "V"."SOURCE" = :source
</#if>
""")
@DefineNamedBindings
@RegisterRowMapper(VulnerabilityRowMapper.class)
Vulnerability getByUuidOrVulnIdAndSource(@Bind UUID uuid, @Bind String vulnId, @Bind String source);

default Vulnerability getByUuid(final UUID uuid) {
return getByUuidOrVulnIdAndSource(uuid, null, null);
}

default Vulnerability getByVulnIdAndSource(final String vulnId, final String source) {
return getByUuidOrVulnIdAndSource(null, vulnId, source);
}

@SqlQuery("""
SELECT "VS"."ID"
, "VS"."PURL"
, "VS"."PURL_TYPE"
, "VS"."PURL_NAMESPACE"
, "VS"."PURL_NAME"
, "VS"."PURL_VERSION"
, "VS"."PURL_QUALIFIERS"
, "VS"."PURL_SUBPATH"
, "VS"."CPE22"
, "VS"."CPE23"
, "VS"."PART"
, "VS"."VENDOR"
, "VS"."PRODUCT"
, "VS"."VERSION"
, "VS"."UPDATE"
, "VS"."EDITION"
, "VS"."LANGUAGE"
, "VS"."SWEDITION"
, "VS"."TARGETSW"
, "VS"."TARGETHW"
, "VS"."OTHER"
, "VS"."VERSIONENDEXCLUDING"
, "VS"."VERSIONENDINCLUDING"
, "VS"."VERSIONSTARTEXCLUDING"
, "VS"."VERSIONSTARTINCLUDING"
, "VS"."VULNERABLE"
, "VS"."UUID"
, (SELECT JSONB_AGG(JSONB_BUILD_OBJECT(
'id', "ID"
, 'firstSeen', CAST(EXTRACT(EPOCH FROM "FIRST_SEEN") * 1000 AS BIGINT)
, 'lastSeen', CAST(EXTRACT(EPOCH FROM "LAST_SEEN") * 1000 AS BIGINT)
, 'source', "SOURCE"
, 'uuid', "UUID"))
FROM "AFFECTEDVERSIONATTRIBUTION"
WHERE "VULNERABILITY" = "VSV"."VULNERABILITY_ID"
AND "VULNERABLE_SOFTWARE" = "VSV"."VULNERABLESOFTWARE_ID") AS "attributionsJson"
FROM "VULNERABLESOFTWARE_VULNERABILITIES" AS "VSV"
INNER JOIN "VULNERABLESOFTWARE" AS "VS"
ON "VS"."ID" = "VSV"."VULNERABLESOFTWARE_ID"
WHERE "VSV"."VULNERABILITY_ID" = :id
""")
@RegisterRowMapper(VulnerableSoftwareRowMapper.class)
List<VulnerableSoftware> getVulnerableSoftwareByVulnId(@Bind long id);

@SqlQuery(/* language=InjectedFreeMarker */ """
<#-- @ftlvariable name="activeFilter" type="Boolean" -->
<#-- @ftlvariable name="apiOrderByClause" type="String" -->
Expand Down Expand Up @@ -104,24 +210,52 @@ record AffectedProjectListRow(

@SqlQuery(/* language=InjectedFreeMarker */ """
<#-- @ftlvariable name="apiFilterParameter" type="String" -->
SELECT "V"."ID", "V"."CREATED", "V"."CVSSV2BASESCORE", "V"."CVSSV2VECTOR", "V"."CVSSV3BASESCORE", "V"."CVSSV3VECTOR"
, STRING_TO_ARRAY("V"."CWES", ',') AS "CWES", "V"."DESCRIPTION", "V"."DETAIL", "V"."PATCHEDVERSIONS", "V"."PUBLISHED", "V"."RECOMMENDATION", "V"."REFERENCES"
, "V"."SEVERITY", "V"."SOURCE", "V"."TITLE", "V"."UPDATED", "V"."UUID", "V"."VULNID", "V"."VULNERABLEVERSIONS", "V"."OWASPRRVECTOR"
SELECT "V"."ID" AS "ID"
, "V"."VULNID"
, "V"."SOURCE"
, "V"."FRIENDLYVULNID"
, "V"."TITLE"
, "V"."SUBTITLE"
, "V"."DESCRIPTION"
, "V"."DETAIL"
, "V"."RECOMMENDATION"
, "V"."REFERENCES"
, "V"."CREDITS"
, "V"."CREATED"
, "V"."PUBLISHED"
, "V"."UPDATED"
, CAST(STRING_TO_ARRAY("V"."CWES", ',') AS INT[]) AS "CWES"
, "V"."CVSSV2BASESCORE"
, "V"."CVSSV2IMPACTSCORE"
, "V"."CVSSV2EXPLOITSCORE"
, "V"."CVSSV2VECTOR"
, "V"."CVSSV3BASESCORE"
, "V"."CVSSV3IMPACTSCORE"
, "V"."CVSSV3EXPLOITSCORE"
, "V"."CVSSV3VECTOR"
, "V"."OWASPRRLIKELIHOODSCORE"
, "V"."OWASPRRTECHNICALIMPACTSCORE"
, "V"."OWASPRRBUSINESSIMPACTSCORE"
, "V"."OWASPRRVECTOR"
, "V"."SEVERITY"
, "V"."VULNERABLEVERSIONS"
, "V"."PATCHEDVERSIONS"
, "V"."UUID"
, JSONB_VULN_ALIASES("V"."SOURCE", "V"."VULNID") AS "vulnAliasesJson"
, "EPSS"."SCORE"
, "EPSS"."PERCENTILE"
, JSONB_VULN_ALIASES("V"."SOURCE", "V"."VULNID") AS "vulnAliasesJson"
FROM "VULNERABILITY" AS "V"
INNER JOIN "COMPONENTS_VULNERABILITIES"
FROM "VULNERABILITY" AS "V"
INNER JOIN "COMPONENTS_VULNERABILITIES"
ON "V"."ID" = "COMPONENTS_VULNERABILITIES"."VULNERABILITY_ID"
AND "COMPONENTS_VULNERABILITIES"."COMPONENT_ID" = :componentId
INNER JOIN "COMPONENT"
ON "COMPONENTS_VULNERABILITIES"."COMPONENT_ID" = "COMPONENT"."ID"
LEFT JOIN "ANALYSIS"
AND "COMPONENTS_VULNERABILITIES"."COMPONENT_ID" = :componentId
INNER JOIN "COMPONENT"
ON "COMPONENTS_VULNERABILITIES"."COMPONENT_ID" = "COMPONENT"."ID"
LEFT JOIN "ANALYSIS"
ON "V"."ID" = "ANALYSIS"."VULNERABILITY_ID"
AND "COMPONENT"."PROJECT_ID" = "ANALYSIS"."PROJECT_ID"
LEFT JOIN "EPSS"
AND "COMPONENT"."PROJECT_ID" = "ANALYSIS"."PROJECT_ID"
LEFT JOIN "EPSS"
ON "V"."VULNID" = "EPSS"."CVE"
WHERE (:includeSuppressed OR "ANALYSIS"."SUPPRESSED" IS NULL OR NOT "ANALYSIS"."SUPPRESSED")
WHERE (:includeSuppressed OR "ANALYSIS"."SUPPRESSED" IS NULL OR NOT "ANALYSIS"."SUPPRESSED")
<#if apiFilterParameter??>
AND (LOWER("V"."VULNID") LIKE ('%' || LOWER(${apiFilterParameter}) || '%'))
</#if>
Expand All @@ -132,17 +266,17 @@ record AffectedProjectListRow(

@SqlQuery("""
SELECT "VULNERABILITY"."ID" AS "id"
, COUNT("PROJECT"."ID") AS "totalProjectCount"
, COUNT(*) FILTER (WHERE "PROJECT"."ACTIVE") AS "activeProjectCount"
FROM "VULNERABILITY"
INNER JOIN "COMPONENTS_VULNERABILITIES"
, COUNT("PROJECT"."ID") AS "totalProjectCount"
, COUNT(*) FILTER (WHERE "PROJECT"."ACTIVE") AS "activeProjectCount"
FROM "VULNERABILITY"
INNER JOIN "COMPONENTS_VULNERABILITIES"
ON "VULNERABILITY"."ID" = "COMPONENTS_VULNERABILITIES"."VULNERABILITY_ID"
INNER JOIN "COMPONENT"
INNER JOIN "COMPONENT"
ON "COMPONENTS_VULNERABILITIES"."COMPONENT_ID" = "COMPONENT"."ID"
INNER JOIN "PROJECT"
INNER JOIN "PROJECT"
ON "COMPONENT"."PROJECT_ID" = "PROJECT"."ID"
WHERE "VULNERABILITY"."ID" = ANY(:vulnerabilityIds)
GROUP BY "VULNERABILITY"."ID"
WHERE "VULNERABILITY"."ID" = ANY(:vulnerabilityIds)
GROUP BY "VULNERABILITY"."ID"
""")
@RegisterConstructorMapper(AffectedProjectCountRow.class)
List<AffectedProjectCountRow> getAffectedProjectCount(@Bind List<Long> vulnerabilityIds);
Expand All @@ -156,31 +290,60 @@ record AffectedProjectCountRow(

@SqlQuery(/* language=InjectedFreeMarker */ """
<#-- @ftlvariable name="apiFilterParameter" type="String" -->
SELECT DISTINCT "V"."ID", "V"."CREATED", "V"."CVSSV2BASESCORE", "V"."CVSSV2VECTOR", "V"."CVSSV3BASESCORE", "V"."CVSSV3VECTOR"
, STRING_TO_ARRAY("V"."CWES", ',') AS "CWES", "V"."DESCRIPTION", "V"."DETAIL", "V"."PATCHEDVERSIONS", "V"."PUBLISHED", "V"."RECOMMENDATION", "V"."REFERENCES"
, "V"."SEVERITY", "V"."SOURCE", "V"."TITLE", "V"."UPDATED", "V"."UUID", "V"."VULNID", "V"."VULNERABLEVERSIONS", "V"."OWASPRRVECTOR"
SELECT DISTINCT ON ("V"."ID")
"V"."ID"
, "V"."VULNID"
, "V"."SOURCE"
, "V"."FRIENDLYVULNID"
, "V"."TITLE"
, "V"."SUBTITLE"
, "V"."DESCRIPTION"
, "V"."DETAIL"
, "V"."RECOMMENDATION"
, "V"."REFERENCES"
, "V"."CREDITS"
, "V"."CREATED"
, "V"."PUBLISHED"
, "V"."UPDATED"
, CAST(STRING_TO_ARRAY("V"."CWES", ',') AS INT[]) AS "CWES"
, "V"."CVSSV2BASESCORE"
, "V"."CVSSV2IMPACTSCORE"
, "V"."CVSSV2EXPLOITSCORE"
, "V"."CVSSV2VECTOR"
, "V"."CVSSV3BASESCORE"
, "V"."CVSSV3IMPACTSCORE"
, "V"."CVSSV3EXPLOITSCORE"
, "V"."CVSSV3VECTOR"
, "V"."OWASPRRLIKELIHOODSCORE"
, "V"."OWASPRRTECHNICALIMPACTSCORE"
, "V"."OWASPRRBUSINESSIMPACTSCORE"
, "V"."OWASPRRVECTOR"
, "V"."SEVERITY"
, "V"."VULNERABLEVERSIONS"
, "V"."PATCHEDVERSIONS"
, "V"."UUID"
, JSONB_VULN_ALIASES("V"."SOURCE", "V"."VULNID") AS "vulnAliasesJson"
, ARRAY_AGG(DISTINCT("COMPONENT"."ID")) as "componentIdsArray"
, "EPSS"."SCORE"
, "EPSS"."PERCENTILE"
, ARRAY_AGG(DISTINCT("COMPONENT"."ID")) as "componentIdsArray"
, JSONB_VULN_ALIASES("V"."SOURCE", "V"."VULNID") AS "vulnAliasesJson"
FROM "VULNERABILITY" AS "V"
INNER JOIN "COMPONENTS_VULNERABILITIES"
FROM "VULNERABILITY" AS "V"
INNER JOIN "COMPONENTS_VULNERABILITIES"
ON "V"."ID" = "COMPONENTS_VULNERABILITIES"."VULNERABILITY_ID"
INNER JOIN "COMPONENT"
INNER JOIN "COMPONENT"
ON "COMPONENTS_VULNERABILITIES"."COMPONENT_ID" = "COMPONENT"."ID"
LEFT JOIN "EPSS"
LEFT JOIN "EPSS"
ON "V"."VULNID" = "EPSS"."CVE"
LEFT JOIN "ANALYSIS"
LEFT JOIN "ANALYSIS"
ON "V"."ID" = "ANALYSIS"."VULNERABILITY_ID"
AND "COMPONENT"."ID" = "ANALYSIS"."COMPONENT_ID"
AND "COMPONENT"."PROJECT_ID" = "ANALYSIS"."PROJECT_ID"
WHERE "COMPONENT"."PROJECT_ID" = :projectId
AND (:includeSuppressed OR "ANALYSIS"."SUPPRESSED" IS NULL OR NOT "ANALYSIS"."SUPPRESSED")
AND "COMPONENT"."ID" = "ANALYSIS"."COMPONENT_ID"
AND "COMPONENT"."PROJECT_ID" = "ANALYSIS"."PROJECT_ID"
WHERE "COMPONENT"."PROJECT_ID" = :projectId
AND (:includeSuppressed OR "ANALYSIS"."SUPPRESSED" IS NULL OR NOT "ANALYSIS"."SUPPRESSED")
<#if apiFilterParameter??>
AND (LOWER("V"."VULNID") LIKE ('%' || LOWER(${apiFilterParameter}) || '%'))
</#if>
GROUP BY "V"."ID", "EPSS"."SCORE", "EPSS"."PERCENTILE"
ORDER BY "V"."ID"
GROUP BY "V"."ID", "EPSS"."SCORE", "EPSS"."PERCENTILE"
ORDER BY "V"."ID"
""")
@RegisterRowMapper(VulnerabilityRowMapper.class)
List<Vulnerability> getVulnerabilitiesByProject(@Bind long projectId, boolean includeSuppressed);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,28 @@
import static org.dependencytrack.persistence.jdbi.mapping.RowMapperUtil.longArray;
import static org.dependencytrack.persistence.jdbi.mapping.RowMapperUtil.maybeSet;

/**
* @since 5.6.0
*/
public class VulnerabilityRowMapper implements RowMapper<Vulnerability> {

private static final TypeReference<List<VulnerabilityAlias>> VULNERABILITY_ALIASES_TYPE_REF = new TypeReference<>() {
};

private final RowMapper<Vulnerability> vulnerabilityMapper = BeanMapper.of(Vulnerability.class);
private final RowMapper<Epss> epssMapper = BeanMapper.of(Epss.class);

@Override
public Vulnerability map(final ResultSet rs, final StatementContext ctx) throws SQLException {
final Vulnerability vuln = BeanMapper.of(Vulnerability.class).map(rs, ctx);
final Epss epss = BeanMapper.of(Epss.class).map(rs, ctx);
final Vulnerability vuln = vulnerabilityMapper.map(rs, ctx);
final Epss epss = epssMapper.map(rs, ctx);
vuln.setEpss(epss);
maybeSet(rs, "vulnAliasesJson", (ignored, columnName) ->
deserializeJson(rs, columnName, VULNERABILITY_ALIASES_TYPE_REF), vuln::setAliases);
if (hasColumn(rs, "componentIdsArray")) {
var vulnerableComponentsIds = longArray(rs, "componentIdsArray");
if (!vulnerableComponentsIds.isEmpty()) {
vuln.setComponents(vulnerableComponentsIds.stream().map(cid -> new Component(cid)).toList());
vuln.setComponents(vulnerableComponentsIds.stream().map(Component::new).toList());
}
}
return vuln;
Expand Down
Loading

0 comments on commit 55fd8df

Please sign in to comment.