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

Fix /api/v1/vulnerability/source/{source}/vuln/{vuln} returning all vulnerable components #934

Merged
merged 1 commit into from
Sep 30, 2024
Merged
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
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
Loading