-
-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Reduce code duplication by migrating more code to use JDBI `Vulnerabi…
…lityPolicyDao` Signed-off-by: nscuro <[email protected]>
- Loading branch information
Showing
13 changed files
with
504 additions
and
779 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
367 changes: 4 additions & 363 deletions
367
src/main/java/org/dependencytrack/persistence/VulnerabilityPolicyQueryManager.java
Large diffs are not rendered by default.
Oops, something went wrong.
18 changes: 18 additions & 0 deletions
18
src/main/java/org/dependencytrack/persistence/jdbi/AnalysisDao.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,18 @@ | ||
package org.dependencytrack.persistence.jdbi; | ||
|
||
import org.jdbi.v3.sqlobject.customizer.Bind; | ||
import org.jdbi.v3.sqlobject.statement.SqlBatch; | ||
|
||
import java.util.List; | ||
|
||
public interface AnalysisDao { | ||
|
||
@SqlBatch(""" | ||
INSERT INTO "ANALYSISCOMMENT" | ||
("ANALYSIS_ID", "COMMENT", "COMMENTER", "TIMESTAMP") | ||
VALUES | ||
(:analysisId, :comment, :commenter, NOW()) | ||
""") | ||
void createComments(@Bind List<Long> analysisId, @Bind String commenter, @Bind List<String> comment); | ||
|
||
} |
194 changes: 177 additions & 17 deletions
194
src/main/java/org/dependencytrack/persistence/jdbi/VulnerabilityPolicyDao.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 |
---|---|---|
@@ -1,62 +1,222 @@ | ||
package org.dependencytrack.persistence.jdbi; | ||
|
||
import org.dependencytrack.model.Analysis; | ||
import org.dependencytrack.persistence.jdbi.mapping.AnalysisRowMapper; | ||
import org.dependencytrack.persistence.jdbi.mapping.VulnPolicyAnalysisArgumentFactory; | ||
import org.dependencytrack.persistence.jdbi.mapping.VulnPolicyRatingsArgumentFactory; | ||
import org.dependencytrack.persistence.jdbi.mapping.VulnerabilityPolicyRowMapper; | ||
import org.dependencytrack.policy.vulnerability.VulnerabilityPolicy; | ||
import org.dependencytrack.util.AnalysisCommentFormatter.AnalysisCommentField; | ||
import org.jdbi.v3.sqlobject.SqlObject; | ||
import org.jdbi.v3.sqlobject.config.RegisterArgumentFactories; | ||
import org.jdbi.v3.sqlobject.config.RegisterArgumentFactory; | ||
import org.jdbi.v3.sqlobject.config.RegisterRowMapper; | ||
import org.jdbi.v3.sqlobject.customizer.Bind; | ||
import org.jdbi.v3.sqlobject.customizer.BindBean; | ||
import org.jdbi.v3.sqlobject.statement.GetGeneratedKeys; | ||
import org.jdbi.v3.sqlobject.statement.SqlQuery; | ||
import org.jdbi.v3.sqlobject.statement.SqlUpdate; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Optional; | ||
|
||
import static org.dependencytrack.util.AnalysisCommentFormatter.formatComment; | ||
|
||
@RegisterArgumentFactories({ | ||
@RegisterArgumentFactory(VulnPolicyAnalysisArgumentFactory.class), | ||
@RegisterArgumentFactory(VulnPolicyRatingsArgumentFactory.class) | ||
}) | ||
@RegisterRowMapper(VulnerabilityPolicyRowMapper.class) | ||
public interface VulnerabilityPolicyDao { | ||
public interface VulnerabilityPolicyDao extends SqlObject { | ||
|
||
@SqlQuery(""" | ||
SELECT * FROM "VULNERABILITY_POLICY"; | ||
SELECT * FROM "VULNERABILITY_POLICY" | ||
""") | ||
List<VulnerabilityPolicy> getAll(); | ||
|
||
@SqlQuery(""" | ||
SELECT | ||
* | ||
FROM | ||
"VULNERABILITY_POLICY" | ||
WHERE | ||
("VALID_FROM" IS NULL OR "VALID_FROM" <= NOW()) | ||
AND ("VALID_UNTIL" IS NULL OR "VALID_UNTIL" >= NOW()) | ||
""") | ||
List<VulnerabilityPolicy> getAllVulnerabilityPolicies(); | ||
List<VulnerabilityPolicy> getAllValid(); | ||
|
||
@SqlQuery(""" | ||
SELECT * FROM "VULNERABILITY_POLICY" WHERE "NAME" = ?; | ||
SELECT * FROM "VULNERABILITY_POLICY" WHERE "NAME" = ? | ||
""") | ||
VulnerabilityPolicy getVulnerabilityPolicyByName(@Bind String name); | ||
VulnerabilityPolicy getByName(@Bind String name); | ||
|
||
@SqlUpdate(""" | ||
INSERT INTO "VULNERABILITY_POLICY" | ||
("ANALYSIS", "AUTHOR", "CONDITIONS", "CREATED", "DESCRIPTION", "NAME", "RATINGS", "UPDATED", "VALID_FROM", "VALID_UNTIL") | ||
("ANALYSIS", "AUTHOR", "CONDITIONS", "CREATED", "DESCRIPTION", "NAME", "RATINGS", "VALID_FROM", "VALID_UNTIL") | ||
VALUES | ||
((:analysis)::JSON, :author, :conditions, :created, :description, :name, (:ratings)::JSON, :updated, :validFrom, :validUntil) | ||
((:analysis)::JSONB, :author, :conditions, NOW(), :description, :name, (:ratings)::JSONB, :validFrom, :validUntil) | ||
RETURNING * | ||
""") | ||
int createVulnerabilityPolicy(@BindBean VulnerabilityPolicy vulnerabilityPolicy); | ||
@GetGeneratedKeys("*") | ||
VulnerabilityPolicy create(@BindBean VulnerabilityPolicy vulnerabilityPolicy); | ||
|
||
@SqlUpdate(""" | ||
DELETE FROM "VULNERABILITY_POLICY" WHERE "NAME" = ? | ||
""") | ||
int deleteVulnerabilityPolicyByName(@Bind String name); | ||
int deleteByName(@Bind String name); | ||
|
||
@SqlUpdate(""" | ||
UPDATE "VULNERABILITY_POLICY" | ||
SET | ||
"ANALYSIS" = :analysis::JSON, | ||
"AUTHOR" = :author, | ||
"CONDITIONS" = :conditions, | ||
"ANALYSIS" = (:analysis)::JSONB, | ||
"AUTHOR" = :author, | ||
"CONDITIONS" = :conditions, | ||
"DESCRIPTION" = :description, | ||
"RATINGS" = :ratings::JSON, | ||
"UPDATED" = :updated, | ||
"VALID_FROM" = :validFrom, | ||
"RATINGS" = (:ratings)::JSONB, | ||
"UPDATED" = NOW(), | ||
"VALID_FROM" = :validFrom, | ||
"VALID_UNTIL" = :validUntil | ||
WHERE "NAME" = :name | ||
WHERE | ||
"NAME" = :name AND ( | ||
-- Using IS DISTINCT FROM instead of != for nullable columns | ||
-- because != does not handle NULL. | ||
"ANALYSIS" != (:analysis)::JSONB | ||
OR "AUTHOR" IS DISTINCT FROM :author | ||
OR "CONDITIONS" != (:conditions)::TEXT[] | ||
OR "DESCRIPTION" IS DISTINCT FROM :description | ||
OR "RATINGS" IS DISTINCT FROM (:ratings)::JSONB | ||
OR "VALID_FROM" IS DISTINCT FROM :validFrom | ||
OR "VALID_UNTIL" IS DISTINCT FROM :validUntil | ||
) | ||
RETURNING * | ||
""") | ||
int updateVulnerabilityPolicyByName(@BindBean VulnerabilityPolicy vulnerabilityPolicy); | ||
@GetGeneratedKeys("*") | ||
VulnerabilityPolicy update(@BindBean VulnerabilityPolicy vulnerabilityPolicy); | ||
|
||
default List<Analysis> unassignFromAnalysesByName(final String name) { | ||
// NB: Can't use interface method here due to https://github.com/jdbi/jdbi/issues/1807. | ||
return getHandle().createUpdate(""" | ||
WITH "CTE_VULN_POLICY" AS ( | ||
SELECT | ||
"ID" | ||
FROM | ||
"VULNERABILITY_POLICY" | ||
WHERE | ||
"NAME" = :name | ||
) | ||
UPDATE | ||
"ANALYSIS" AS "NEW" | ||
SET | ||
"STATE" = 'NOT_SET', -- Must be non-null | ||
"JUSTIFICATION" = NULL, | ||
"RESPONSE" = NULL, | ||
"DETAILS" = NULL, | ||
"SUPPRESSED" = FALSE, | ||
"SEVERITY" = NULL, | ||
"CVSSV2VECTOR" = NULL, | ||
"CVSSV2SCORE" = NULL, | ||
"CVSSV3VECTOR" = NULL, | ||
"CVSSV3SCORE" = NULL, | ||
"OWASPVECTOR" = NULL, | ||
"OWASPSCORE" = NULL, | ||
"VULNERABILITY_POLICY_ID" = NULL | ||
FROM | ||
"ANALYSIS" AS "OLD" -- Self-join to get access to pre-update values | ||
WHERE | ||
"NEW"."ID" = "OLD"."ID" | ||
AND "NEW"."VULNERABILITY_POLICY_ID" IS NOT NULL | ||
AND "NEW"."VULNERABILITY_POLICY_ID" = (SELECT "ID" FROM "CTE_VULN_POLICY") | ||
RETURNING | ||
"OLD"."ID", | ||
"OLD"."STATE", | ||
"OLD"."JUSTIFICATION", | ||
"OLD"."RESPONSE", | ||
"OLD"."DETAILS", | ||
"OLD"."SUPPRESSED", | ||
"OLD"."SEVERITY", | ||
"OLD"."CVSSV2VECTOR", | ||
"OLD"."CVSSV2SCORE", | ||
"OLD"."CVSSV3VECTOR", | ||
"OLD"."CVSSV3SCORE", | ||
"OLD"."OWASPVECTOR", | ||
"OLD"."OWASPSCORE" | ||
""") | ||
.bind("name", name) | ||
.executeAndReturnGeneratedKeys() | ||
.map(new AnalysisRowMapper()) | ||
.list(); | ||
} | ||
|
||
/** | ||
* Un-assign a given {@link VulnerabilityPolicy} from any associated {@link Analysis} | ||
* records, reset the analyses' states, and populate the audit trail accordingly. | ||
* | ||
* @param name Name of the {@link VulnerabilityPolicy} to un-assign | ||
*/ | ||
default void unassignAndDeleteByName(String name) { | ||
final List<Analysis> unassignedAnalyses = unassignFromAnalysesByName(name); | ||
|
||
final var analysisIds = new ArrayList<Long>(); | ||
final var comments = new ArrayList<String>(); | ||
for (final Analysis analysis : unassignedAnalyses) { | ||
analysisIds.add(analysis.getId()); | ||
comments.add("Policy removed"); | ||
|
||
Optional.ofNullable(analysis.getAnalysisState()).ifPresent(oldState -> { | ||
analysisIds.add(analysis.getId()); | ||
comments.add(formatComment(AnalysisCommentField.STATE, oldState, null)); | ||
}); | ||
Optional.ofNullable(analysis.getAnalysisJustification()).ifPresent(oldJustification -> { | ||
analysisIds.add(analysis.getId()); | ||
comments.add(formatComment(AnalysisCommentField.JUSTIFICATION, oldJustification, null)); | ||
}); | ||
Optional.ofNullable(analysis.getAnalysisResponse()).ifPresent(oldResponse -> { | ||
analysisIds.add(analysis.getId()); | ||
comments.add(formatComment(AnalysisCommentField.RESPONSE, oldResponse, null)); | ||
}); | ||
Optional.ofNullable(analysis.getAnalysisDetails()).ifPresent(oldDetails -> { | ||
analysisIds.add(analysis.getId()); | ||
comments.add(formatComment(AnalysisCommentField.DETAILS, oldDetails, null)); | ||
}); | ||
// SUPPRESSED is not nullable; Can only be changed if it was previously true | ||
Optional.of(analysis.isSuppressed()).filter(Boolean.TRUE::equals).ifPresent(oldSuppressed -> { | ||
analysisIds.add(analysis.getId()); | ||
comments.add(formatComment(AnalysisCommentField.SUPPRESSED, oldSuppressed, false)); | ||
}); | ||
Optional.ofNullable(analysis.getSeverity()).ifPresent(oldSeverity -> { | ||
analysisIds.add(analysis.getId()); | ||
comments.add(formatComment(AnalysisCommentField.SEVERITY, oldSeverity, null)); | ||
}); | ||
Optional.ofNullable(analysis.getCvssV2Vector()).ifPresent(oldVector -> { | ||
analysisIds.add(analysis.getId()); | ||
comments.add(formatComment(AnalysisCommentField.CVSSV2_VECTOR, oldVector, null)); | ||
}); | ||
Optional.ofNullable(analysis.getCvssV2Score()).ifPresent(oldScore -> { | ||
analysisIds.add(analysis.getId()); | ||
comments.add(formatComment(AnalysisCommentField.CVSSV2_SCORE, oldScore, null)); | ||
}); | ||
Optional.ofNullable(analysis.getCvssV3Vector()).ifPresent(oldVector -> { | ||
analysisIds.add(analysis.getId()); | ||
comments.add(formatComment(AnalysisCommentField.CVSSV3_VECTOR, oldVector, null)); | ||
}); | ||
Optional.ofNullable(analysis.getCvssV3Score()).ifPresent(oldScore -> { | ||
analysisIds.add(analysis.getId()); | ||
comments.add(formatComment(AnalysisCommentField.CVSSV3_SCORE, oldScore, null)); | ||
}); | ||
Optional.ofNullable(analysis.getOwaspVector()).ifPresent(oldVector -> { | ||
analysisIds.add(analysis.getId()); | ||
comments.add(formatComment(AnalysisCommentField.OWASP_VECTOR, oldVector, null)); | ||
}); | ||
Optional.ofNullable(analysis.getOwaspScore()).ifPresent(oldScore -> { | ||
analysisIds.add(analysis.getId()); | ||
comments.add(formatComment(AnalysisCommentField.OWASP_SCORE, oldScore, null)); | ||
}); | ||
} | ||
|
||
final var commenter = "[Policy{Name=%s}]".formatted(name); | ||
final var analysisDao = getHandle().attach(AnalysisDao.class); | ||
analysisDao.createComments(analysisIds, commenter, comments); | ||
} | ||
|
||
} |
41 changes: 41 additions & 0 deletions
41
src/main/java/org/dependencytrack/persistence/jdbi/mapping/AnalysisRowMapper.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,41 @@ | ||
package org.dependencytrack.persistence.jdbi.mapping; | ||
|
||
import org.dependencytrack.model.Analysis; | ||
import org.dependencytrack.model.AnalysisJustification; | ||
import org.dependencytrack.model.AnalysisResponse; | ||
import org.dependencytrack.model.AnalysisState; | ||
import org.dependencytrack.model.Component; | ||
import org.dependencytrack.model.Vulnerability; | ||
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.dependencytrack.persistence.jdbi.mapping.RowMapperUtil.maybeSet; | ||
|
||
public class AnalysisRowMapper implements RowMapper<Analysis> { | ||
|
||
@Override | ||
public Analysis map(final ResultSet rs, final StatementContext ctx) throws SQLException { | ||
final var analysis = new Analysis(); | ||
maybeSet(rs, "ID", ResultSet::getLong, analysis::setId); | ||
maybeSet(rs, "COMPONENT_ID", ResultSet::getLong, value -> { | ||
final var component = new Component(); | ||
component.setId(value); | ||
analysis.setComponent(component); | ||
}); | ||
maybeSet(rs, "VULNERABILITY_ID", ResultSet::getLong, value -> { | ||
final var vuln = new Vulnerability(); | ||
vuln.setId(value); | ||
analysis.setVulnerability(vuln); | ||
}); | ||
maybeSet(rs, "STATE", ResultSet::getString, value -> analysis.setAnalysisState(AnalysisState.valueOf(value))); | ||
maybeSet(rs, "JUSTIFICATION", ResultSet::getString, value -> analysis.setAnalysisJustification(AnalysisJustification.valueOf(value))); | ||
maybeSet(rs, "RESPONSE", ResultSet::getString, value -> analysis.setAnalysisResponse(AnalysisResponse.valueOf(value))); | ||
maybeSet(rs, "DETAILS", ResultSet::getString, analysis::setAnalysisDetails); | ||
maybeSet(rs, "SUPPRESSED", ResultSet::getBoolean, analysis::setSuppressed); | ||
return analysis; | ||
} | ||
|
||
} |
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
Oops, something went wrong.