-
-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Construct
NEW_VULNERABILITY
and NEW_VULNERABLE_DEPENDENCY
notific…
…ation subjects with JDBI This is a prerequisite for DependencyTrack/hyades#937 in order to make notification dispatching less resource-intensive. Loading this data via ORM is cumbersome, as we either load too much (due to default fetch groups), or too less (due to lazy loading). Getting the subjects in a single query also reduces database round trips. Signed-off-by: nscuro <[email protected]>
- Loading branch information
Showing
11 changed files
with
748 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
196 changes: 196 additions & 0 deletions
196
src/main/java/org/dependencytrack/persistence/jdbi/NotificationSubjectDao.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
package org.dependencytrack.persistence.jdbi; | ||
|
||
import org.dependencytrack.model.VulnerabilityAnalysisLevel; | ||
import org.dependencytrack.persistence.jdbi.mapping.NotificationComponentRowMapper; | ||
import org.dependencytrack.persistence.jdbi.mapping.NotificationProjectRowMapper; | ||
import org.dependencytrack.persistence.jdbi.mapping.NotificationSubjectNewVulnerabilityRowMapper; | ||
import org.dependencytrack.persistence.jdbi.mapping.NotificationSubjectNewVulnerableDependencyRowReducer; | ||
import org.dependencytrack.persistence.jdbi.mapping.NotificationVulnerabilityRowMapper; | ||
import org.dependencytrack.proto.notification.v1.NewVulnerabilitySubject; | ||
import org.dependencytrack.proto.notification.v1.NewVulnerableDependencySubject; | ||
import org.jdbi.v3.sqlobject.config.RegisterRowMapper; | ||
import org.jdbi.v3.sqlobject.config.RegisterRowMappers; | ||
import org.jdbi.v3.sqlobject.statement.SqlQuery; | ||
import org.jdbi.v3.sqlobject.statement.UseRowReducer; | ||
|
||
import java.util.Collection; | ||
import java.util.List; | ||
import java.util.Optional; | ||
import java.util.UUID; | ||
|
||
@RegisterRowMappers({ | ||
@RegisterRowMapper(NotificationComponentRowMapper.class), | ||
@RegisterRowMapper(NotificationProjectRowMapper.class), | ||
@RegisterRowMapper(NotificationVulnerabilityRowMapper.class) | ||
}) | ||
public interface NotificationSubjectDao { | ||
|
||
@SqlQuery(""" | ||
SELECT | ||
"C"."UUID" AS "componentUuid", | ||
"C"."GROUP" AS "componentGroup", | ||
"C"."NAME" AS "componentName", | ||
"C"."VERSION" AS "componentVersion", | ||
"C"."PURL" AS "componentPurl", | ||
"C"."MD5" AS "componentMd5", | ||
"C"."SHA1" AS "componentSha1", | ||
"C"."SHA_256" AS "componentSha256", | ||
"C"."SHA_512" AS "componentSha512", | ||
"P"."UUID" AS "projectUuid", | ||
"P"."NAME" AS "projectName", | ||
"P"."VERSION" AS "projectVersion", | ||
"P"."DESCRIPTION" AS "projectDescription", | ||
"P"."PURL" AS "projectPurl", | ||
(SELECT | ||
STRING_AGG("T"."NAME", ',') | ||
FROM | ||
"TAG" AS "T" | ||
INNER JOIN | ||
"PROJECTS_TAGS" AS "PT" ON "PT"."TAG_ID" = "T"."ID" | ||
WHERE | ||
"PT"."PROJECT_ID" = "P"."ID" | ||
) AS "projectTags", | ||
"V"."UUID" AS "vulnUuid", | ||
"V"."VULNID" AS "vulnId", | ||
"V"."SOURCE" AS "vulnSource", | ||
"V"."TITLE" AS "vulnTitle", | ||
"V"."SUBTITLE" AS "vulnSubTitle", | ||
"V"."DESCRIPTION" AS "vulnDescription", | ||
"V"."RECOMMENDATION" AS "vulnRecommendation", | ||
-- TODO: Select ratings from ANALYSIS table if available. | ||
-- https://github.com/DependencyTrack/hyades/issues/941 | ||
"V"."CVSSV2BASESCORE" AS "vulnCvssV2BaseScore", | ||
"V"."CVSSV3BASESCORE" AS "vulnCvssV3BaseScore", | ||
"V"."OWASPRRBUSINESSIMPACTSCORE" AS "vulnOwaspRrBusinessImpactScore", | ||
"V"."OWASPRRLIKELIHOODSCORE" AS "vulnOwaspRrLikelihoodScore", | ||
"V"."OWASPRRTECHNICALIMPACTSCORE" AS "vulnOwaspRrTechnicalImpactScore", | ||
"V"."SEVERITY" AS "vulnSeverity", | ||
"V"."CWES" AS "vulnCwes", | ||
"vulnAliasesJson", | ||
:vulnAnalysisLevel AS "vulnAnalysisLevel" | ||
FROM | ||
"COMPONENT" AS "C" | ||
INNER JOIN | ||
"PROJECT" AS "P" ON "P"."ID" = "C"."PROJECT_ID" | ||
INNER JOIN | ||
"COMPONENTS_VULNERABILITIES" AS "CV" ON "CV"."COMPONENT_ID" = "C"."ID" | ||
INNER JOIN | ||
"VULNERABILITY" AS "V" ON "V"."ID" = "CV"."VULNERABILITY_ID" | ||
LEFT JOIN | ||
"ANALYSIS" AS "A" ON "A"."COMPONENT_ID" = "C"."ID" AND "A"."VULNERABILITY_ID" = "V"."ID" | ||
LEFT JOIN LATERAL ( | ||
SELECT | ||
CAST(JSONB_AGG(DISTINCT JSONB_STRIP_NULLS(JSONB_BUILD_OBJECT( | ||
'cveId', "VA"."CVE_ID", | ||
'ghsaId', "VA"."GHSA_ID", | ||
'gsdId', "VA"."GSD_ID", | ||
'internalId', "VA"."INTERNAL_ID", | ||
'osvId', "VA"."OSV_ID", | ||
'sonatypeId', "VA"."SONATYPE_ID", | ||
'snykId', "VA"."SNYK_ID", | ||
'vulnDbId', "VA"."VULNDB_ID" | ||
))) AS TEXT) AS "vulnAliasesJson" | ||
FROM | ||
"VULNERABILITYALIAS" AS "VA" | ||
WHERE | ||
("V"."SOURCE" = 'NVD' AND "VA"."CVE_ID" = "V"."VULNID") | ||
OR ("V"."SOURCE" = 'GITHUB' AND "VA"."GHSA_ID" = "V"."VULNID") | ||
OR ("V"."SOURCE" = 'GSD' AND "VA"."GSD_ID" = "V"."VULNID") | ||
OR ("V"."SOURCE" = 'INTERNAL' AND "VA"."INTERNAL_ID" = "V"."VULNID") | ||
OR ("V"."SOURCE" = 'OSV' AND "VA"."OSV_ID" = "V"."VULNID") | ||
OR ("V"."SOURCE" = 'SONATYPE' AND "VA"."SONATYPE_ID" = "V"."VULNID") | ||
OR ("V"."SOURCE" = 'SNYK' AND "VA"."SNYK_ID" = "V"."VULNID") | ||
OR ("V"."SOURCE" = 'VULNDB' AND "VA"."VULNDB_ID" = "V"."VULNID") | ||
) AS "vulnAliases" ON TRUE | ||
WHERE | ||
"C"."UUID" = (:componentUuid)::TEXT AND "V"."UUID" = ANY((:vulnUuids)::TEXT[]) | ||
AND ("A"."SUPPRESSED" IS NULL OR NOT "A"."SUPPRESSED") | ||
""") | ||
@RegisterRowMapper(NotificationSubjectNewVulnerabilityRowMapper.class) | ||
List<NewVulnerabilitySubject> getForNewVulnerabilities(final UUID componentUuid, final Collection<UUID> vulnUuids, | ||
final VulnerabilityAnalysisLevel vulnAnalysisLevel); | ||
|
||
@SqlQuery(""" | ||
SELECT | ||
"C"."UUID" AS "componentUuid", | ||
"C"."GROUP" AS "componentGroup", | ||
"C"."NAME" AS "componentName", | ||
"C"."VERSION" AS "componentVersion", | ||
"C"."PURL" AS "componentPurl", | ||
"C"."MD5" AS "componentMd5", | ||
"C"."SHA1" AS "componentSha1", | ||
"C"."SHA_256" AS "componentSha256", | ||
"C"."SHA_512" AS "componentSha512", | ||
"P"."UUID" AS "projectUuid", | ||
"P"."NAME" AS "projectName", | ||
"P"."VERSION" AS "projectVersion", | ||
"P"."DESCRIPTION" AS "projectDescription", | ||
"P"."PURL" AS "projectPurl", | ||
(SELECT | ||
STRING_AGG("T"."NAME", ',') | ||
FROM | ||
"TAG" AS "T" | ||
INNER JOIN | ||
"PROJECTS_TAGS" AS "PT" ON "PT"."TAG_ID" = "T"."ID" | ||
WHERE | ||
"PT"."PROJECT_ID" = "P"."ID" | ||
) AS "projectTags", | ||
"V"."UUID" AS "vulnUuid", | ||
"V"."VULNID" AS "vulnId", | ||
"V"."SOURCE" AS "vulnSource", | ||
"V"."TITLE" AS "vulnTitle", | ||
"V"."SUBTITLE" AS "vulnSubTitle", | ||
"V"."DESCRIPTION" AS "vulnDescription", | ||
"V"."RECOMMENDATION" AS "vulnRecommendation", | ||
-- TODO: Select ratings from ANALYSIS table if available. | ||
-- https://github.com/DependencyTrack/hyades/issues/941 | ||
"V"."CVSSV2BASESCORE" AS "vulnCvssV2BaseScore", | ||
"V"."CVSSV3BASESCORE" AS "vulnCvssV3BaseScore", | ||
"V"."OWASPRRBUSINESSIMPACTSCORE" AS "vulnOwaspRrBusinessImpactScore", | ||
"V"."OWASPRRLIKELIHOODSCORE" AS "vulnOwaspRrLikelihoodScore", | ||
"V"."OWASPRRTECHNICALIMPACTSCORE" AS "vulnOwaspRrTechnicalImpactScore", | ||
"V"."SEVERITY" AS "vulnSeverity", | ||
"V"."CWES" AS "vulnCwes", | ||
"vulnAliasesJson" | ||
FROM | ||
"COMPONENT" AS "C" | ||
INNER JOIN | ||
"PROJECT" AS "P" ON "P"."ID" = "C"."PROJECT_ID" | ||
INNER JOIN | ||
"COMPONENTS_VULNERABILITIES" AS "CV" ON "CV"."COMPONENT_ID" = "C"."ID" | ||
INNER JOIN | ||
"VULNERABILITY" AS "V" ON "V"."ID" = "CV"."VULNERABILITY_ID" | ||
LEFT JOIN | ||
"ANALYSIS" AS "A" ON "A"."COMPONENT_ID" = "C"."ID" AND "A"."VULNERABILITY_ID" = "V"."ID" | ||
LEFT JOIN LATERAL ( | ||
SELECT | ||
CAST(JSONB_AGG(DISTINCT JSONB_STRIP_NULLS(JSONB_BUILD_OBJECT( | ||
'cveId', "VA"."CVE_ID", | ||
'ghsaId', "VA"."GHSA_ID", | ||
'gsdId', "VA"."GSD_ID", | ||
'internalId', "VA"."INTERNAL_ID", | ||
'osvId', "VA"."OSV_ID", | ||
'sonatypeId', "VA"."SONATYPE_ID", | ||
'snykId', "VA"."SNYK_ID", | ||
'vulnDbId', "VA"."VULNDB_ID" | ||
))) AS TEXT) AS "vulnAliasesJson" | ||
FROM | ||
"VULNERABILITYALIAS" AS "VA" | ||
WHERE | ||
("V"."SOURCE" = 'NVD' AND "VA"."CVE_ID" = "V"."VULNID") | ||
OR ("V"."SOURCE" = 'GITHUB' AND "VA"."GHSA_ID" = "V"."VULNID") | ||
OR ("V"."SOURCE" = 'GSD' AND "VA"."GSD_ID" = "V"."VULNID") | ||
OR ("V"."SOURCE" = 'INTERNAL' AND "VA"."INTERNAL_ID" = "V"."VULNID") | ||
OR ("V"."SOURCE" = 'OSV' AND "VA"."OSV_ID" = "V"."VULNID") | ||
OR ("V"."SOURCE" = 'SONATYPE' AND "VA"."SONATYPE_ID" = "V"."VULNID") | ||
OR ("V"."SOURCE" = 'SNYK' AND "VA"."SNYK_ID" = "V"."VULNID") | ||
OR ("V"."SOURCE" = 'VULNDB' AND "VA"."VULNDB_ID" = "V"."VULNID") | ||
) AS "vulnAliases" ON TRUE | ||
WHERE | ||
"C"."UUID" = (:componentUuid)::TEXT | ||
AND ("A"."SUPPRESSED" IS NULL OR NOT "A"."SUPPRESSED") | ||
""") | ||
@UseRowReducer(NotificationSubjectNewVulnerableDependencyRowReducer.class) | ||
Optional<NewVulnerableDependencySubject> getForNewVulnerableDependency(final UUID componentUuid); | ||
|
||
} |
29 changes: 29 additions & 0 deletions
29
...ain/java/org/dependencytrack/persistence/jdbi/mapping/NotificationComponentRowMapper.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package org.dependencytrack.persistence.jdbi.mapping; | ||
|
||
import org.dependencytrack.proto.notification.v1.Component; | ||
import org.jdbi.v3.core.mapper.RowMapper; | ||
import org.jdbi.v3.core.statement.StatementContext; | ||
|
||
import java.sql.ResultSet; | ||
import java.sql.SQLException; | ||
|
||
import static org.apache.commons.lang3.StringUtils.trimToEmpty; | ||
|
||
public class NotificationComponentRowMapper implements RowMapper<Component> { | ||
|
||
@Override | ||
public Component map(final ResultSet rs, final StatementContext ctx) throws SQLException { | ||
return Component.newBuilder() | ||
.setUuid(trimToEmpty(rs.getString("componentUuid"))) | ||
.setGroup(trimToEmpty(rs.getString("componentGroup"))) | ||
.setName(trimToEmpty(rs.getString("componentName"))) | ||
.setVersion(trimToEmpty(rs.getString("componentVersion"))) | ||
.setPurl(trimToEmpty(rs.getString("componentPurl"))) | ||
.setMd5(trimToEmpty(rs.getString("componentMd5"))) | ||
.setSha1(trimToEmpty(rs.getString("componentSha1"))) | ||
.setSha256(trimToEmpty(rs.getString("componentSha256"))) | ||
.setSha512(trimToEmpty(rs.getString("componentSha512"))) | ||
.build(); | ||
} | ||
|
||
} |
34 changes: 34 additions & 0 deletions
34
src/main/java/org/dependencytrack/persistence/jdbi/mapping/NotificationProjectRowMapper.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package org.dependencytrack.persistence.jdbi.mapping; | ||
|
||
import org.apache.commons.lang3.StringUtils; | ||
import org.dependencytrack.proto.notification.v1.Project; | ||
import org.jdbi.v3.core.mapper.RowMapper; | ||
import org.jdbi.v3.core.statement.StatementContext; | ||
|
||
import java.sql.ResultSet; | ||
import java.sql.SQLException; | ||
import java.util.Arrays; | ||
import java.util.Objects; | ||
import java.util.Optional; | ||
|
||
import static org.apache.commons.lang3.StringUtils.trimToEmpty; | ||
|
||
public class NotificationProjectRowMapper implements RowMapper<Project> { | ||
|
||
@Override | ||
public Project map(final ResultSet rs, final StatementContext ctx) throws SQLException { | ||
return Project.newBuilder() | ||
.setUuid(trimToEmpty(rs.getString("projectUuid"))) | ||
.setName(trimToEmpty(rs.getString("projectName"))) | ||
.setVersion(trimToEmpty(rs.getString("projectVersion"))) | ||
.setDescription(trimToEmpty(rs.getString("projectDescription"))) | ||
.setPurl(trimToEmpty(rs.getString("projectPurl"))) | ||
.addAllTags(Optional.ofNullable(rs.getString("projectTags")).stream() | ||
.flatMap(tagNames -> Arrays.stream(tagNames.split(","))) | ||
.map(StringUtils::trimToNull) | ||
.filter(Objects::nonNull) | ||
.toList()) | ||
.build(); | ||
} | ||
|
||
} |
30 changes: 30 additions & 0 deletions
30
...ependencytrack/persistence/jdbi/mapping/NotificationSubjectNewVulnerabilityRowMapper.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package org.dependencytrack.persistence.jdbi.mapping; | ||
|
||
import org.dependencytrack.proto.notification.v1.Component; | ||
import org.dependencytrack.proto.notification.v1.NewVulnerabilitySubject; | ||
import org.dependencytrack.proto.notification.v1.Project; | ||
import org.dependencytrack.proto.notification.v1.Vulnerability; | ||
import org.jdbi.v3.core.mapper.RowMapper; | ||
import org.jdbi.v3.core.statement.StatementContext; | ||
|
||
import java.sql.ResultSet; | ||
import java.sql.SQLException; | ||
|
||
public class NotificationSubjectNewVulnerabilityRowMapper implements RowMapper<NewVulnerabilitySubject> { | ||
|
||
@Override | ||
public NewVulnerabilitySubject map(final ResultSet rs, final StatementContext ctx) throws SQLException { | ||
final RowMapper<Component> componentRowMapper = ctx.findRowMapperFor(Component.class).orElseThrow(); | ||
final RowMapper<Project> projectRowMapper = ctx.findRowMapperFor(Project.class).orElseThrow(); | ||
final RowMapper<Vulnerability> vulnRowMapper = ctx.findRowMapperFor(Vulnerability.class).orElseThrow(); | ||
|
||
return NewVulnerabilitySubject.newBuilder() | ||
.setComponent(componentRowMapper.map(rs, ctx)) | ||
.setProject(projectRowMapper.map(rs, ctx)) | ||
.setVulnerability(vulnRowMapper.map(rs, ctx)) | ||
.setVulnerabilityAnalysisLevel(rs.getString("vulnAnalysisLevel")) | ||
.addAffectedProjects(projectRowMapper.map(rs, ctx)) | ||
.build(); | ||
} | ||
|
||
} |
Oops, something went wrong.