diff --git a/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java b/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java index aa62e36847..653b5aa8be 100644 --- a/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java @@ -18,9 +18,13 @@ */ package org.dependencytrack.persistence; +import alpine.model.ApiKey; +import alpine.model.Team; +import alpine.model.UserPrincipal; import alpine.persistence.PaginatedResult; import alpine.resources.AlpineRequest; import org.dependencytrack.model.Component; +import org.dependencytrack.model.ConfigPropertyConstants; import org.dependencytrack.model.License; import org.dependencytrack.model.LicenseGroup; import org.dependencytrack.model.Policy; @@ -31,13 +35,17 @@ import org.dependencytrack.model.ViolationAnalysis; import org.dependencytrack.model.ViolationAnalysisComment; import org.dependencytrack.model.ViolationAnalysisState; +import org.dependencytrack.util.DateUtil; import javax.jdo.PersistenceManager; import javax.jdo.Query; import java.util.ArrayList; import java.util.Collection; import java.util.Date; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.UUID; import static org.dependencytrack.util.PersistenceUtil.assertPersistent; import static org.dependencytrack.util.PersistenceUtil.assertPersistentAll; @@ -351,22 +359,31 @@ public PaginatedResult getPolicyViolations(final Component component, boolean in } /** - * Returns a List of all Policy violations for the entire portfolio. + * Returns a List of all Policy violations for the entire portfolio filtered by ACL and other optional filters. * @return a List of all Policy violations */ @SuppressWarnings("unchecked") - public PaginatedResult getPolicyViolations(boolean includeSuppressed) { + public PaginatedResult getPolicyViolations(boolean includeSuppressed, boolean showInactive, Map filters) { + final PaginatedResult result; final Query query = pm.newQuery(PolicyViolation.class); + final Map params = new HashMap<>(); + final List filterCriteria = new ArrayList<>(); if (!includeSuppressed) { - query.setFilter("analysis.suppressed == false || analysis.suppressed == null"); + filterCriteria.add("(analysis.suppressed == false || analysis.suppressed == null)"); } + if (!showInactive) { + filterCriteria.add("(project.active == true || project.active == null)"); + } + processViolationsFilters(filters, params, filterCriteria); if (orderBy == null) { query.setOrdering("timestamp desc, project.name, project.version, component.name, component.version"); } - final PaginatedResult result = execute(query); + final String queryFilter = String.join(" && ", filterCriteria); + preprocessACLs(query, queryFilter, params, false); + result = execute(query, params); for (final PolicyViolation violation: result.getList(PolicyViolation.class)) { - violation.getPolicyCondition().getPolicy(); // force policy to ne included since its not the default - violation.getComponent().getResolvedLicense(); // force resolved license to ne included since its not the default + violation.getPolicyCondition().getPolicy(); // force policy to be included since it's not the default + violation.getComponent().getResolvedLicense(); // force resolved license to be included since it's not the default violation.setAnalysis(getViolationAnalysis(violation.getComponent(), violation)); // Include the violation analysis by default } return result; @@ -616,6 +633,115 @@ public long getAuditedCount(final Component component, final PolicyViolation.Typ return getCount(query, component, type, ViolationAnalysisState.NOT_SET); } + private void processViolationsFilters(Map filters, Map params, List filterCriteria) { + for (Map.Entry filter : filters.entrySet()) { + switch (filter.getKey()) { + case "violationState" -> processArrayFilter(params, filterCriteria, "violationState", filter.getValue(), "policyCondition.policy.violationState"); + case "riskType" -> processArrayFilter(params, filterCriteria, "riskType", filter.getValue(), "type"); + case "policy" -> processArrayFilter(params, filterCriteria, "policy", filter.getValue(), "policyCondition.policy.uuid"); + case "analysisState" -> processArrayFilter(params, filterCriteria, "analysisState", filter.getValue(), "analysis.analysisState"); + case "occurredOnDateFrom" -> processDateFilter(params, filterCriteria, "occuredOnDateFrom", filter.getValue(), true); + case "occurredOnDateTo" -> processDateFilter(params, filterCriteria, "occuredOnDateTo", filter.getValue(), false); + case "textSearchField" -> processInputFilter(params, filterCriteria, "textInput", filter.getValue(), filters.get("textSearchInput")); + } + } + } + + private void processArrayFilter(Map params, List filterCriteria, String paramName, String filter, String column) { + if (filter != null && !filter.isEmpty()) { + StringBuilder filterBuilder = new StringBuilder("("); + String[] arrayFilter = filter.split(","); + for (int i = 0, arrayFilterLength = arrayFilter.length; i < arrayFilterLength; i++) { + filterBuilder.append(column).append(" == :").append(paramName).append(i); + switch (paramName) { + case "violationState" -> params.put(paramName + i, Policy.ViolationState.valueOf(arrayFilter[i])); + case "riskType" -> params.put(paramName + i, PolicyViolation.Type.valueOf(arrayFilter[i])); + case "policy" -> params.put(paramName + i, UUID.fromString(arrayFilter[i])); + case "analysisState" -> { + if (arrayFilter[i].equals("NOT_SET")) { + filterBuilder.append(" || ").append(column).append(" == null"); + } + params.put(paramName + i, ViolationAnalysisState.valueOf(arrayFilter[i])); + } + } + if (i < arrayFilterLength - 1) { + filterBuilder.append(" || "); + } + } + filterBuilder.append(")"); + filterCriteria.add(filterBuilder.toString()); + } + } + + private void processDateFilter(Map params, List filterCriteria, String paramName, String filter, boolean fromValue) { + if (filter != null && !filter.isEmpty()) { + params.put(paramName, DateUtil.fromISO8601(filter + (fromValue ? "T00:00:00" : "T23:59:59"))); + filterCriteria.add("(timestamp " + (fromValue ? ">= :" : "<= :") + paramName + ")"); + } + } + + private void processInputFilter(Map params, List filterCriteria, String paramName, String filter, String input) { + if (filter != null && !filter.isEmpty() && input != null && !input.isEmpty()) { + StringBuilder filterBuilder = new StringBuilder("("); + String[] inputFilter = filter.split(","); + for (int i = 0, inputFilterLength = inputFilter.length; i < inputFilterLength; i++) { + switch (inputFilter[i].toLowerCase()) { + case "policy_name" -> filterBuilder.append("policyCondition.policy.name"); + case "component" -> filterBuilder.append("component.name"); + case "license" -> filterBuilder.append("component.resolvedLicense.licenseId.toLowerCase().matches(:").append(paramName).append(") || component.license"); + case "project_name" -> filterBuilder.append("project.name.toLowerCase().matches(:").append(paramName).append(") || project.version"); + } + filterBuilder.append(".toLowerCase().matches(:").append(paramName).append(")"); + if (i < inputFilterLength - 1) { + filterBuilder.append(" || "); + } + } + params.put(paramName, ".*" + input.toLowerCase() + ".*"); + filterBuilder.append(")"); + filterCriteria.add(filterBuilder.toString()); + } + } + + @Override + void preprocessACLs(final Query query, final String inputFilter, final Map params, final boolean bypass) { + if (super.principal != null && isEnabled(ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED) && !bypass) { + final List teams; + if (super.principal instanceof UserPrincipal) { + final UserPrincipal userPrincipal = ((UserPrincipal) super.principal); + teams = userPrincipal.getTeams(); + if (super.hasAccessManagementPermission(userPrincipal)) { + query.setFilter(inputFilter); + return; + } + } else { + final ApiKey apiKey = ((ApiKey) super.principal); + teams = apiKey.getTeams(); + if (super.hasAccessManagementPermission(apiKey)) { + query.setFilter(inputFilter); + return; + } + } + if (teams != null && teams.size() > 0) { + final StringBuilder sb = new StringBuilder(); + for (int i = 0, teamsSize = teams.size(); i < teamsSize; i++) { + final Team team = super.getObjectById(Team.class, teams.get(i).getId()); + sb.append(" project.accessTeams.contains(:team").append(i).append(") "); + params.put("team" + i, team); + if (i < teamsSize-1) { + sb.append(" || "); + } + } + if (inputFilter != null) { + query.setFilter(inputFilter + " && (" + sb.toString() + ")"); + } else { + query.setFilter(sb.toString()); + } + } + } else { + query.setFilter(inputFilter); + } + } + /** * @since 4.12.0 */ diff --git a/src/main/java/org/dependencytrack/persistence/QueryManager.java b/src/main/java/org/dependencytrack/persistence/QueryManager.java index cc42de5c96..a610387b8c 100644 --- a/src/main/java/org/dependencytrack/persistence/QueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/QueryManager.java @@ -731,8 +731,8 @@ public PaginatedResult getPolicyViolations(final Component component, boolean in return getPolicyQueryManager().getPolicyViolations(component, includeSuppressed); } - public PaginatedResult getPolicyViolations(boolean includeSuppressed) { - return getPolicyQueryManager().getPolicyViolations(includeSuppressed); + public PaginatedResult getPolicyViolations(boolean includeSuppressed, boolean showInactive, Map filters) { + return getPolicyQueryManager().getPolicyViolations(includeSuppressed, showInactive, filters); } public ViolationAnalysis getViolationAnalysis(Component component, PolicyViolation policyViolation) { diff --git a/src/main/java/org/dependencytrack/resources/v1/PolicyViolationResource.java b/src/main/java/org/dependencytrack/resources/v1/PolicyViolationResource.java index f1cbdc357b..d7dc7e284c 100644 --- a/src/main/java/org/dependencytrack/resources/v1/PolicyViolationResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/PolicyViolationResource.java @@ -50,6 +50,8 @@ import javax.jdo.FetchPlan; import javax.jdo.PersistenceManager; import java.util.Collection; +import java.util.HashMap; +import java.util.Map; /** * JAX-RS resources for processing policy violations. @@ -83,9 +85,36 @@ public class PolicyViolationResource extends AlpineResource { }) @PermissionRequired(Permissions.Constants.VIEW_POLICY_VIOLATION) public Response getViolations(@Parameter(description = "Optionally includes suppressed violations") - @QueryParam("suppressed") boolean suppressed) { + @QueryParam("suppressed") boolean suppressed, + @Parameter(description = "Optionally includes inactive projects") + @QueryParam("showInactive") boolean showInactive, + @Parameter(description = "Filter by violation state") + @QueryParam("violationState") String violationState, + @Parameter(description = "Filter by risk type") + @QueryParam("riskType") String riskType, + @Parameter(description = "Filter by policy") + @QueryParam("policy") String policy, + @Parameter(description = "Filter by analysis state") + @QueryParam("analysisState") String analysisState, + @Parameter(description = "Filter occurred on from") + @QueryParam("occurredOnDateFrom") String occurredOnDateFrom, + @Parameter(description = "Filter occurred on to") + @QueryParam("occurredOnDateTo") String occurredOnDateTo, + @Parameter(description = "Filter the text input in these fields") + @QueryParam("textSearchField") String textSearchField, + @Parameter(description = "Filter by this text input") + @QueryParam("textSearchInput") String textSearchInput) { try (QueryManager qm = new QueryManager(getAlpineRequest())) { - final PaginatedResult result = qm.getPolicyViolations(suppressed); + Map filters = new HashMap<>(); + filters.put("violationState", violationState); + filters.put("riskType", riskType); + filters.put("policy", policy); + filters.put("analysisState", analysisState); + filters.put("occurredOnDateFrom", occurredOnDateFrom); + filters.put("occurredOnDateTo", occurredOnDateTo); + filters.put("textSearchField", textSearchField); + filters.put("textSearchInput", textSearchInput); + final PaginatedResult result = qm.getPolicyViolations(suppressed, showInactive, filters); return Response.ok(detachViolations(qm, result.getList(PolicyViolation.class))) .header(TOTAL_COUNT_HEADER, result.getTotal()) .build(); diff --git a/src/test/java/org/dependencytrack/resources/v1/PolicyViolationResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/PolicyViolationResourceTest.java index da4aba1971..8c2c130099 100644 --- a/src/test/java/org/dependencytrack/resources/v1/PolicyViolationResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/PolicyViolationResourceTest.java @@ -18,17 +18,22 @@ */ package org.dependencytrack.resources.v1; +import alpine.model.ConfigProperty; import alpine.server.filters.ApiFilter; import alpine.server.filters.AuthenticationFilter; import alpine.server.filters.AuthorizationFilter; + import org.dependencytrack.JerseyTestRule; import org.dependencytrack.ResourceTest; import org.dependencytrack.auth.Permissions; import org.dependencytrack.model.Component; +import org.dependencytrack.model.ConfigPropertyConstants; import org.dependencytrack.model.Policy; import org.dependencytrack.model.PolicyCondition; import org.dependencytrack.model.PolicyViolation; import org.dependencytrack.model.Project; +import org.dependencytrack.model.ViolationAnalysis; +import org.dependencytrack.model.ViolationAnalysisState; import org.glassfish.jersey.server.ResourceConfig; import org.junit.ClassRule; import org.junit.Test; @@ -115,11 +120,11 @@ public void getViolationsByProjectTest() { component0.setVersion("1.0"); component0 = qm.createComponent(component0, false); - var component1 = new Component(); - component1.setProject(project); - component1.setName("Acme Component 1"); - component1.setVersion("1.0"); - component1 = qm.createComponent(component1, false); + var componentA = new Component(); + componentA.setProject(project); + componentA.setName("Acme Component 1"); + componentA.setVersion("1.0"); + componentA = qm.createComponent(componentA, false); final Policy policy0 = qm.createPolicy("Blacklisted Version 0", Policy.Operator.ALL, Policy.ViolationState.FAIL); final PolicyCondition condition0 = qm.createPolicyCondition(policy0, PolicyCondition.Subject.VERSION, PolicyCondition.Operator.NUMERIC_EQUAL, "1.0"); @@ -134,7 +139,7 @@ public void getViolationsByProjectTest() { var violation = new PolicyViolation(); violation.setType(PolicyViolation.Type.OPERATIONAL); - violation.setComponent(componentFilter ? component0 : component1); + violation.setComponent(componentFilter ? component0 : componentA); violation.setPolicyCondition(conditionFilter ? condition0 : condition1); violation.setTimestamp(new Date()); violation = qm.persist(violation); @@ -307,4 +312,348 @@ public void getViolationsByComponentNotFoundTest() { assertThat(getPlainTextBody(response)).contains("component could not be found"); } + @Test + public void getViolationsWithAclEnabledTest() { + initializeWithPermissions(Permissions.VIEW_POLICY_VIOLATION); + + final Project projectA = qm.createProject("Acme Example", null, "1.0", null, null, null, true, false); + final Project projectA_child = qm.createProject("Acme Example - Child", null, "1.0", null, projectA, null, true, false); + final Project projectB = qm.createProject("Acme Example - Grandchild", null, "1.0", null, null, null, true, false); + + projectA.addAccessTeam(team); + + var componentA = new Component(); + componentA.setProject(projectA); + componentA.setName("Acme Component"); + componentA.setVersion("1.0"); + componentA = qm.createComponent(componentA, false); + + var componentB = new Component(); + componentB.setProject(projectA_child); + componentB.setName("Acme Component"); + componentB.setVersion("1.0"); + componentB = qm.createComponent(componentB, false); + + var componentC = new Component(); + componentC.setProject(projectB); + componentC.setName("Acme Component"); + componentC.setVersion("1.0"); + componentC = qm.createComponent(componentC, false); + + var componentD = new Component(); + componentD.setProject(projectA); + componentD.setName("Acme Component"); + componentD.setVersion("1.0"); + componentD = qm.createComponent(componentA, false); + + final Policy policy = qm.createPolicy("Blacklisted Version", Policy.Operator.ALL, Policy.ViolationState.FAIL); + final PolicyCondition condition = qm.createPolicyCondition(policy, PolicyCondition.Subject.VERSION, PolicyCondition.Operator.NUMERIC_EQUAL, "1.0"); + + var violationA = new PolicyViolation(); + violationA.setType(PolicyViolation.Type.OPERATIONAL); + violationA.setComponent(componentA); + violationA.setPolicyCondition(condition); + violationA.setTimestamp(new Date()); + violationA = qm.persist(violationA); + + var violationB = new PolicyViolation(); + violationB.setType(PolicyViolation.Type.OPERATIONAL); + violationB.setComponent(componentB); + violationB.setPolicyCondition(condition); + violationB.setTimestamp(new Date()); + violationB = qm.persist(violationB); + + var violationC = new PolicyViolation(); + violationC.setType(PolicyViolation.Type.OPERATIONAL); + violationC.setComponent(componentC); + violationC.setPolicyCondition(condition); + violationC.setTimestamp(new Date()); + violationC = qm.persist(violationC); + + var violationD = new PolicyViolation(); + violationD.setType(PolicyViolation.Type.OPERATIONAL); + violationD.setComponent(componentD); + violationD.setPolicyCondition(condition); + violationD.setTimestamp(new Date()); + violationD = qm.persist(violationD); + + final Response responseA = jersey.target(V1_POLICY_VIOLATION) + .request() + .header(X_API_KEY, apiKey) + .get(); + assertThat(responseA.getStatus()).isEqualTo(Response.Status.OK.getStatusCode()); + assertThat(responseA.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("4"); + assertThat(parseJsonArray(responseA)).hasSize(4); + + ConfigProperty aclToggle = qm.getConfigProperty(ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getGroupName(), ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyName()); + if (aclToggle == null) { + qm.createConfigProperty(ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getGroupName(), ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyName(), "true", ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyType(), ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getDescription()); + } else { + aclToggle.setPropertyValue("true"); + qm.persist(aclToggle); + } + + final Response responseB = jersey.target(V1_POLICY_VIOLATION) + .request() + .header(X_API_KEY, team.getApiKeys().get(0).getKey()) + .get(); + assertThat(responseB.getStatus()).isEqualTo(Response.Status.OK.getStatusCode()); + assertThat(responseB.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("2"); + + final JsonArray jsonArray = parseJsonArray(responseB); + assertThat(jsonArray).hasSize(2); + + final JsonObject jsonObjectA = jsonArray.getJsonObject(0); + assertThat(jsonObjectA.getString("uuid")).isEqualTo(violationD.getUuid().toString()); + assertThat(jsonObjectA.getString("type")).isEqualTo(PolicyViolation.Type.OPERATIONAL.name()); + assertThat(jsonObjectA.getJsonObject("policyCondition")).isNotNull(); + assertThat(jsonObjectA.getJsonObject("policyCondition").getJsonObject("policy")).isNotNull(); + assertThat(jsonObjectA.getJsonObject("policyCondition").getJsonObject("policy").getString("name")).isEqualTo("Blacklisted Version"); + assertThat(jsonObjectA.getJsonObject("policyCondition").getJsonObject("policy").getString("violationState")).isEqualTo("FAIL"); + assertThat(jsonObjectA.getJsonObject("project").getString("uuid")).isEqualTo(projectA.getUuid().toString()); + + final JsonObject jsonObjectB = jsonArray.getJsonObject(1); + assertThat(jsonObjectB.getString("uuid")).isEqualTo(violationA.getUuid().toString()); + assertThat(jsonObjectB.getString("type")).isEqualTo(PolicyViolation.Type.OPERATIONAL.name()); + assertThat(jsonObjectB.getJsonObject("policyCondition")).isNotNull(); + assertThat(jsonObjectB.getJsonObject("policyCondition").getJsonObject("policy")).isNotNull(); + assertThat(jsonObjectB.getJsonObject("policyCondition").getJsonObject("policy").getString("name")).isEqualTo("Blacklisted Version"); + assertThat(jsonObjectB.getJsonObject("policyCondition").getJsonObject("policy").getString("violationState")).isEqualTo("FAIL"); + assertThat(jsonObjectB.getJsonObject("project").getString("uuid")).isEqualTo(projectA.getUuid().toString()); + } + + @Test + public void getViolationsWithArrayFilter() { + initializeWithPermissions(Permissions.VIEW_POLICY_VIOLATION); + + final Project project = qm.createProject("Acme Example", null, "1.0", null, null, null, true, false); + + var component = new Component(); + component.setProject(project); + component.setName("Acme Component"); + component.setVersion("1.0"); + component = qm.createComponent(component, false); + + final Policy policyA = qm.createPolicy("Policy A", Policy.Operator.ALL, Policy.ViolationState.FAIL); + final PolicyCondition conditionA = qm.createPolicyCondition(policyA, PolicyCondition.Subject.VERSION, PolicyCondition.Operator.NUMERIC_EQUAL, "1.0"); + var violationA = new PolicyViolation(); + violationA.setType(PolicyViolation.Type.OPERATIONAL); + violationA.setComponent(component); + violationA.setPolicyCondition(conditionA); + violationA.setTimestamp(new Date()); + violationA = qm.persist(violationA); + + final Policy policyB = qm.createPolicy("Policy B", Policy.Operator.ALL, Policy.ViolationState.INFO); + final PolicyCondition conditionB = qm.createPolicyCondition(policyB, PolicyCondition.Subject.LICENSE, PolicyCondition.Operator.IS, "unresolved"); + var violationB = new PolicyViolation(); + violationB.setType(PolicyViolation.Type.LICENSE); + violationB.setComponent(component); + violationB.setPolicyCondition(conditionB); + violationB.setTimestamp(new Date()); + violationB = qm.persist(violationB); + + final Policy policyC = qm.createPolicy("Policy C", Policy.Operator.ALL, Policy.ViolationState.INFO); + final PolicyCondition conditionC = qm.createPolicyCondition(policyC, PolicyCondition.Subject.VERSION, PolicyCondition.Operator.NUMERIC_EQUAL, "1.0"); + ViolationAnalysis violationAnalysis = new ViolationAnalysis(); + violationAnalysis.setViolationAnalysisState(ViolationAnalysisState.REJECTED); + var violationC = new PolicyViolation(); + violationC.setType(PolicyViolation.Type.OPERATIONAL); + violationC.setComponent(component); + violationC.setPolicyCondition(conditionC); + violationC.setTimestamp(new Date()); + violationC.setAnalysis(violationAnalysis); + violationAnalysis.setPolicyViolation(violationC); + violationC = qm.persist(violationC); + + final Policy policyD = qm.createPolicy("Policy D", Policy.Operator.ALL, Policy.ViolationState.INFO); + final PolicyCondition conditionD = qm.createPolicyCondition(policyD, PolicyCondition.Subject.VERSION, PolicyCondition.Operator.NUMERIC_EQUAL, "1.0"); + var violationD = new PolicyViolation(); + violationD.setType(PolicyViolation.Type.OPERATIONAL); + violationD.setComponent(component); + violationD.setPolicyCondition(conditionD); + violationD.setTimestamp(new Date()); + violationD = qm.persist(violationD); + + final Response response = jersey.target(V1_POLICY_VIOLATION) + .request() + .header(X_API_KEY, apiKey) + .get(); + assertThat(response.getStatus()).isEqualTo(Response.Status.OK.getStatusCode()); + assertThat(response.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("4"); + assertThat(parseJsonArray(response)).hasSize(4); + + final Response responseA = jersey.target(V1_POLICY_VIOLATION).queryParam("violationState", "FAIL") + .request() + .header(X_API_KEY, apiKey) + .get(); + assertThat(responseA.getStatus()).isEqualTo(Response.Status.OK.getStatusCode()); + assertThat(responseA.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("1"); + final JsonArray jsonArrayA = parseJsonArray(responseA); + assertThat(jsonArrayA).hasSize(1); + assertThat(jsonArrayA.getJsonObject(0).getString("uuid")).isEqualTo(violationA.getUuid().toString()); + + + final Response responseB = jersey.target(V1_POLICY_VIOLATION).queryParam("riskType", "LICENSE") + .request() + .header(X_API_KEY, apiKey) + .get(); + assertThat(responseB.getStatus()).isEqualTo(Response.Status.OK.getStatusCode()); + assertThat(responseB.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("1"); + final JsonArray jsonArrayB = parseJsonArray(responseB); + assertThat(jsonArrayB).hasSize(1); + assertThat(jsonArrayB.getJsonObject(0).getString("uuid")).isEqualTo(violationB.getUuid().toString()); + assertThat(jsonArrayB.getJsonObject(0).getString("uuid")).isEqualTo(violationB.getUuid().toString()); + + final Response responseC = jersey.target(V1_POLICY_VIOLATION).queryParam("analysisState", "REJECTED") + .request() + .header(X_API_KEY, apiKey) + .get(); + assertThat(responseC.getStatus()).isEqualTo(Response.Status.OK.getStatusCode()); + assertThat(responseC.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("1"); + final JsonArray jsonArrayC = parseJsonArray(responseC); + assertThat(jsonArrayC).hasSize(1); + assertThat(jsonArrayC.getJsonObject(0).getString("uuid")).isEqualTo(violationC.getUuid().toString()); + assertThat(jsonArrayC.getJsonObject(0).getString("uuid")).isEqualTo(violationC.getUuid().toString()); + + final Response responseD = jersey.target(V1_POLICY_VIOLATION).queryParam("policy", policyD.getUuid().toString()) + .request() + .header(X_API_KEY, apiKey) + .get(); + assertThat(responseD.getStatus()).isEqualTo(Response.Status.OK.getStatusCode()); + assertThat(responseD.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("1"); + final JsonArray jsonArrayD = parseJsonArray(responseD); + assertThat(jsonArrayD).hasSize(1); + assertThat(jsonArrayD.getJsonObject(0).getString("uuid")).isEqualTo(violationD.getUuid().toString()); + assertThat(jsonArrayD.getJsonObject(0).getString("uuid")).isEqualTo(violationD.getUuid().toString()); + } + + @Test + public void getViolationsWithInputFilter() { + initializeWithPermissions(Permissions.VIEW_POLICY_VIOLATION); + + final Project projectA = qm.createProject("Project A", null, "1.0", null, null, null, true, false); + final Project projectB = qm.createProject("Project B", null, "1.0", null, null, null, true, false); + final Project projectC = qm.createProject("Project C", null, "1.0", null, null, null, true, false); + final Project projectD = qm.createProject("Project D", null, "1.0", null, null, null, true, false); + + var componentA = new Component(); + componentA.setProject(projectA); + componentA.setName("Component A"); + componentA.setVersion("1.0"); + componentA.setLicense("License A"); + componentA = qm.createComponent(componentA, false); + + var componentB = new Component(); + componentB.setProject(projectB); + componentB.setName("Component B"); + componentB.setVersion("1.0"); + componentB.setLicense("License B"); + componentB = qm.createComponent(componentB, false); + + var componentC = new Component(); + componentC.setProject(projectC); + componentC.setName("Component C"); + componentC.setVersion("1.0"); + componentC.setLicense("License C"); + componentC = qm.createComponent(componentC, false); + + var componentD = new Component(); + componentD.setProject(projectD); + componentD.setName("Component D"); + componentD.setVersion("1.0"); + componentD.setLicense("License D"); + componentD = qm.createComponent(componentD, false); + + final Policy policyA = qm.createPolicy("Policy A", Policy.Operator.ALL, Policy.ViolationState.FAIL); + final PolicyCondition conditionA = qm.createPolicyCondition(policyA, PolicyCondition.Subject.VERSION, PolicyCondition.Operator.NUMERIC_EQUAL, "1.0"); + var violationA = new PolicyViolation(); + violationA.setType(PolicyViolation.Type.OPERATIONAL); + violationA.setComponent(componentA); + violationA.setPolicyCondition(conditionA); + violationA.setTimestamp(new Date()); + violationA = qm.persist(violationA); + + final Policy policyB = qm.createPolicy("Policy B", Policy.Operator.ALL, Policy.ViolationState.FAIL); + final PolicyCondition conditionB = qm.createPolicyCondition(policyB, PolicyCondition.Subject.VERSION, PolicyCondition.Operator.NUMERIC_EQUAL, "1.0"); + var violationB = new PolicyViolation(); + violationB.setType(PolicyViolation.Type.OPERATIONAL); + violationB.setComponent(componentB); + violationB.setPolicyCondition(conditionB); + violationB.setTimestamp(new Date()); + violationB = qm.persist(violationB); + + final Policy policyC = qm.createPolicy("Policy C", Policy.Operator.ALL, Policy.ViolationState.FAIL); + final PolicyCondition conditionC = qm.createPolicyCondition(policyC, PolicyCondition.Subject.VERSION, PolicyCondition.Operator.NUMERIC_EQUAL, "1.0"); + var violationC = new PolicyViolation(); + violationC.setType(PolicyViolation.Type.OPERATIONAL); + violationC.setComponent(componentC); + violationC.setPolicyCondition(conditionC); + violationC.setTimestamp(new Date()); + violationC = qm.persist(violationC); + + final Policy policyD = qm.createPolicy("Policy D", Policy.Operator.ALL, Policy.ViolationState.FAIL); + final PolicyCondition conditionD = qm.createPolicyCondition(policyD, PolicyCondition.Subject.VERSION, PolicyCondition.Operator.NUMERIC_EQUAL, "1.0"); + var violationD = new PolicyViolation(); + violationD.setType(PolicyViolation.Type.OPERATIONAL); + violationD.setComponent(componentD); + violationD.setPolicyCondition(conditionD); + violationD.setTimestamp(new Date()); + violationD = qm.persist(violationD); + + final Response response = jersey.target(V1_POLICY_VIOLATION) + .request() + .header(X_API_KEY, apiKey) + .get(); + assertThat(response.getStatus()).isEqualTo(Response.Status.OK.getStatusCode()); + assertThat(response.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("4"); + assertThat(parseJsonArray(response)).hasSize(4); + + final Response responseA = jersey.target(V1_POLICY_VIOLATION) + .queryParam("textSearchField", "policy_name") + .queryParam("textSearchInput", "Policy A") + .request() + .header(X_API_KEY, apiKey) + .get(); + assertThat(responseA.getStatus()).isEqualTo(Response.Status.OK.getStatusCode()); + assertThat(responseA.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("1"); + final JsonArray jsonArrayA = parseJsonArray(responseA); + assertThat(jsonArrayA).hasSize(1); + assertThat(jsonArrayA.getJsonObject(0).getString("uuid")).isEqualTo(violationA.getUuid().toString()); + + final Response responseB = jersey.target(V1_POLICY_VIOLATION) + .queryParam("textSearchField", "component") + .queryParam("textSearchInput", "Component B") + .request() + .header(X_API_KEY, apiKey) + .get(); + assertThat(responseB.getStatus()).isEqualTo(Response.Status.OK.getStatusCode()); + assertThat(responseB.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("1"); + final JsonArray jsonArrayB = parseJsonArray(responseB); + assertThat(jsonArrayB).hasSize(1); + assertThat(jsonArrayB.getJsonObject(0).getString("uuid")).isEqualTo(violationB.getUuid().toString()); + + final Response responseC = jersey.target(V1_POLICY_VIOLATION) + .queryParam("textSearchField", "license") + .queryParam("textSearchInput", "License C") + .request() + .header(X_API_KEY, apiKey) + .get(); + assertThat(responseC.getStatus()).isEqualTo(Response.Status.OK.getStatusCode()); + assertThat(responseC.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("1"); + final JsonArray jsonArrayC = parseJsonArray(responseC); + assertThat(jsonArrayC).hasSize(1); + assertThat(jsonArrayC.getJsonObject(0).getString("uuid")).isEqualTo(violationC.getUuid().toString()); + + final Response responseD = jersey.target(V1_POLICY_VIOLATION) + .queryParam("textSearchField", "project_name") + .queryParam("textSearchInput", "Project D") + .request() + .header(X_API_KEY, apiKey) + .get(); + assertThat(responseD.getStatus()).isEqualTo(Response.Status.OK.getStatusCode()); + assertThat(responseD.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("1"); + final JsonArray jsonArrayD = parseJsonArray(responseD); + assertThat(jsonArrayD).hasSize(1); + assertThat(jsonArrayD.getJsonObject(0).getString("uuid")).isEqualTo(violationD.getUuid().toString()); + } } \ No newline at end of file