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

Port : Global Audit View: Policy Violations #949

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
136 changes: 130 additions & 6 deletions src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -31,6 +35,7 @@
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;
Expand All @@ -39,6 +44,7 @@
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;
Expand Down Expand Up @@ -389,22 +395,31 @@
}

/**
* 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<String, String> filters) {
final PaginatedResult result;
final Query<PolicyViolation> query = pm.newQuery(PolicyViolation.class);
final Map<String, Object> params = new HashMap<>();
final List<String> 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 be 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;
Expand Down Expand Up @@ -707,4 +722,113 @@
return modified;
});
}

private void processViolationsFilters(Map<String, String> filters, Map<String, Object> params, List<String> filterCriteria) {
for (Map.Entry<String, String> filter : filters.entrySet()) {
switch (filter.getKey()) {

Check warning on line 728 in src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java#L728

Switch statements should be exhaustive, add a default case (or missing enum branches)
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<String, Object> params, List<String> 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) {

Check warning on line 746 in src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java#L746

Switch statements should be exhaustive, add a default case (or missing enum branches)
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<String, Object> params, List<String> 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<String, Object> params, List<String> 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()) {

Check warning on line 778 in src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java#L778

Switch statements should be exhaustive, add a default case (or missing enum branches)
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<String, Object> params, final boolean bypass) {
if (super.principal != null && isEnabled(ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED) && !bypass) {
final List<Team> 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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -846,8 +846,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<String, String> filters) {
return getPolicyQueryManager().getPolicyViolations(includeSuppressed, showInactive, filters);
}

public ViolationAnalysis getViolationAnalysis(Component component, PolicyViolation policyViolation) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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<String, String> 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();
Expand Down
Loading
Loading