diff --git a/docs/_posts/2022-10-13-v4.6.1.md b/docs/_posts/2022-10-13-v4.6.1.md new file mode 100644 index 0000000000..c3229aa0d6 --- /dev/null +++ b/docs/_posts/2022-10-13-v4.6.1.md @@ -0,0 +1,32 @@ +--- +title: v4.6.1 +type: patch +--- + +**Fixes:** + +* Resolved defect that caused policy name and violation state to not be displayed in the violations audit tab - [#2043] + +For a complete list of changes, refer to the respective GitHub milestones: + +* [API server milestone 4.6.1](https://github.com/DependencyTrack/dependency-track/milestone/28?closed=1) + +###### dependency-track-apiserver.jar + +| Algorithm | Checksum | +|:----------|:---------| +| SHA-1 | | +| SHA-256 | | + +###### dependency-track-bundled.jar + +| Algorithm | Checksum | +|:----------|:---------| +| SHA-1 | | +| SHA-256 | | + +###### Software Bill of Materials (SBOM) + +* API Server: [bom.json](https://github.com/DependencyTrack/dependency-track/releases/download/4.6.1/bom.json) + +[#2043]: https://github.com/DependencyTrack/dependency-track/issues/2043 diff --git a/src/main/java/org/dependencytrack/resources/v1/PolicyViolationResource.java b/src/main/java/org/dependencytrack/resources/v1/PolicyViolationResource.java index 3a4b955dd4..84d03a10d1 100644 --- a/src/main/java/org/dependencytrack/resources/v1/PolicyViolationResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/PolicyViolationResource.java @@ -34,6 +34,8 @@ import org.dependencytrack.model.Project; import org.dependencytrack.persistence.QueryManager; +import javax.jdo.FetchPlan; +import javax.jdo.PersistenceManager; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; @@ -41,6 +43,7 @@ import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import java.util.Collection; /** * JAX-RS resources for processing policy violations. @@ -68,7 +71,9 @@ public Response getViolations(@ApiParam(value = "Optionally includes suppressed @QueryParam("suppressed") boolean suppressed) { try (QueryManager qm = new QueryManager(getAlpineRequest())) { final PaginatedResult result = qm.getPolicyViolations(suppressed); - return Response.ok(result.getObjects()).header(TOTAL_COUNT_HEADER, result.getTotal()).build(); + return Response.ok(detachViolations(qm, result.getList(PolicyViolation.class))) + .header(TOTAL_COUNT_HEADER, result.getTotal()) + .build(); } } @@ -95,7 +100,9 @@ public Response getViolationsByProject(@PathParam("uuid") String uuid, if (project != null) { if (qm.hasAccess(super.getPrincipal(), project)) { final PaginatedResult result = qm.getPolicyViolations(project, suppressed); - return Response.ok(result.getObjects()).header(TOTAL_COUNT_HEADER, result.getTotal()).build(); + return Response.ok(detachViolations(qm, result.getList(PolicyViolation.class))) + .header(TOTAL_COUNT_HEADER, result.getTotal()) + .build(); } else { return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); } @@ -128,7 +135,9 @@ public Response getViolationsByComponent(@PathParam("uuid") String uuid, if (component != null) { if (qm.hasAccess(super.getPrincipal(), component.getProject())) { final PaginatedResult result = qm.getPolicyViolations(component, suppressed); - return Response.ok(result.getObjects()).header(TOTAL_COUNT_HEADER, result.getTotal()).build(); + return Response.ok(detachViolations(qm, result.getList(PolicyViolation.class))) + .header(TOTAL_COUNT_HEADER, result.getTotal()) + .build(); } else { return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified component is forbidden").build(); } @@ -137,4 +146,23 @@ public Response getViolationsByComponent(@PathParam("uuid") String uuid, } } } + + /** + * Detach a given {@link Collection} of {@link PolicyViolation} suitable for use in API responses. + *

+ * This ensures that responses include not only the violations themselves, but also the associated + * {@link org.dependencytrack.model.Policy}, which is required to tell the policy name and violation state. + * + * @param qm The {@link QueryManager} to use + * @param violations The {@link PolicyViolation}s to detach + * @return A detached {@link Collection} of {@link PolicyViolation}s + * @see GitHub issue + */ + private Collection detachViolations(final QueryManager qm, final Collection violations) { + final PersistenceManager pm = qm.getPersistenceManager(); + pm.getFetchPlan().setMaxFetchDepth(2); // Ensure policy is included + pm.getFetchPlan().setDetachmentOptions(FetchPlan.DETACH_LOAD_FIELDS); + return qm.getPersistenceManager().detachCopyAll(violations); + } + } diff --git a/src/test/java/org/dependencytrack/resources/v1/PolicyViolationResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/PolicyViolationResourceTest.java index 7f27bafbd0..d3e6a8da72 100644 --- a/src/test/java/org/dependencytrack/resources/v1/PolicyViolationResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/PolicyViolationResourceTest.java @@ -89,6 +89,11 @@ public void getViolationsTest() { final JsonObject jsonObject = jsonArray.getJsonObject(0); assertThat(jsonObject.getString("uuid")).isEqualTo(violation.getUuid().toString()); assertThat(jsonObject.getString("type")).isEqualTo(PolicyViolation.Type.OPERATIONAL.name()); + assertThat(jsonObject.getJsonObject("policyCondition")).isNotNull(); + assertThat(jsonObject.getJsonObject("policyCondition").getJsonObject("policy")).isNotNull(); + assertThat(jsonObject.getJsonObject("policyCondition").getJsonObject("policy").getString("name")).isEqualTo("Blacklisted Version"); + assertThat(jsonObject.getJsonObject("policyCondition").getJsonObject("policy").getString("violationState")).isEqualTo("FAIL"); + } @Test @@ -137,6 +142,10 @@ public void getViolationsByProjectTest() { final JsonObject jsonObject = jsonArray.getJsonObject(0); assertThat(jsonObject.getString("uuid")).isEqualTo(violation.getUuid().toString()); assertThat(jsonObject.getString("type")).isEqualTo(PolicyViolation.Type.OPERATIONAL.name()); + assertThat(jsonObject.getJsonObject("policyCondition")).isNotNull(); + assertThat(jsonObject.getJsonObject("policyCondition").getJsonObject("policy")).isNotNull(); + assertThat(jsonObject.getJsonObject("policyCondition").getJsonObject("policy").getString("name")).isEqualTo("Blacklisted Version"); + assertThat(jsonObject.getJsonObject("policyCondition").getJsonObject("policy").getString("violationState")).isEqualTo("FAIL"); } @Test @@ -200,6 +209,10 @@ public void getViolationsByComponentTest() { final JsonObject jsonObject = jsonArray.getJsonObject(0); assertThat(jsonObject.getString("uuid")).isEqualTo(violation.getUuid().toString()); assertThat(jsonObject.getString("type")).isEqualTo(PolicyViolation.Type.OPERATIONAL.name()); + assertThat(jsonObject.getJsonObject("policyCondition")).isNotNull(); + assertThat(jsonObject.getJsonObject("policyCondition").getJsonObject("policy")).isNotNull(); + assertThat(jsonObject.getJsonObject("policyCondition").getJsonObject("policy").getString("name")).isEqualTo("Blacklisted Version"); + assertThat(jsonObject.getJsonObject("policyCondition").getJsonObject("policy").getString("violationState")).isEqualTo("FAIL"); } @Test