Skip to content

Commit

Permalink
Merge pull request #3746 from 2000rosser/issue-3703
Browse files Browse the repository at this point in the history
Add EPSS conditions to policies
  • Loading branch information
nscuro authored May 23, 2024
2 parents 0aee910 + cc24d08 commit 0d8a768
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 2 deletions.
3 changes: 2 additions & 1 deletion src/main/java/org/dependencytrack/model/PolicyCondition.java
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ public enum Subject {
COMPONENT_HASH,
CWE,
VULNERABILITY_ID,
VERSION_DISTANCE
VERSION_DISTANCE,
EPSS
}

@PrimaryKey
Expand Down
88 changes: 88 additions & 0 deletions src/main/java/org/dependencytrack/policy/EpssPolicyEvaluator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* This file is part of Dependency-Track.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
* Copyright (c) OWASP Foundation. All Rights Reserved.
*/
package org.dependencytrack.policy;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

import org.dependencytrack.model.Component;
import org.dependencytrack.model.Policy;
import org.dependencytrack.model.PolicyCondition;
import org.dependencytrack.model.Vulnerability;

import alpine.common.logging.Logger;

public class EpssPolicyEvaluator extends AbstractPolicyEvaluator {
private static final Logger LOGGER = Logger.getLogger(EpssPolicyEvaluator.class);

/**
* {@inheritDoc}
*/
@Override
public PolicyCondition.Subject supportedSubject() {
return PolicyCondition.Subject.EPSS;
}

/**
* {@inheritDoc}
*/
@Override
public List<PolicyConditionViolation> evaluate(final Policy policy, final Component component) {
final List<PolicyConditionViolation> violations = new ArrayList<>();

final List<PolicyCondition> policyConditions = super.extractSupportedConditions(policy);
if (policyConditions.isEmpty()) {
return violations;
}

for (final Vulnerability vulnerability : qm.getAllVulnerabilities(component, false)) {
for (final PolicyCondition condition: policyConditions) {
LOGGER.debug("Evaluating component (" + component.getUuid() + ") against policy condition (" + condition.getUuid() + ")");
if (matches(condition.getOperator(), vulnerability.getEpssScore(), condition.getValue())) {
violations.add(new PolicyConditionViolation(condition, component));
}
}

}
return violations;
}

public boolean matches(final PolicyCondition.Operator operator, final BigDecimal vulnerabilityEpss, final String conditionValue) {

if (conditionValue == null || vulnerabilityEpss == null) {
return false;
}
BigDecimal conditionDecimalValue = new BigDecimal(conditionValue);

return switch (operator) {
case NUMERIC_GREATER_THAN -> vulnerabilityEpss.compareTo(conditionDecimalValue) > 0;
case NUMERIC_GREATER_THAN_OR_EQUAL -> vulnerabilityEpss.compareTo(conditionDecimalValue) >= 0;
case NUMERIC_EQUAL -> vulnerabilityEpss.compareTo(conditionDecimalValue) == 0;
case NUMERIC_NOT_EQUAL -> vulnerabilityEpss.compareTo(conditionDecimalValue) != 0;
case NUMERIC_LESSER_THAN_OR_EQUAL -> vulnerabilityEpss.compareTo(conditionDecimalValue) <= 0;
case NUMERIC_LESS_THAN -> vulnerabilityEpss.compareTo(conditionDecimalValue) < 0;
default -> {
LOGGER.warn("Operator %s is not supported for EPSS conditions".formatted(operator));
yield false;
}
};

}
}
3 changes: 2 additions & 1 deletion src/main/java/org/dependencytrack/policy/PolicyEngine.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public PolicyEngine() {
evaluators.add(new CwePolicyEvaluator());
evaluators.add(new VulnerabilityIdPolicyEvaluator());
evaluators.add(new VersionDistancePolicyEvaluator());
evaluators.add(new EpssPolicyEvaluator());
}

public List<PolicyViolation> evaluate(final List<Component> components) {
Expand Down Expand Up @@ -139,7 +140,7 @@ public PolicyViolation.Type determineViolationType(final PolicyCondition.Subject
return null;
}
return switch (subject) {
case CWE, SEVERITY, VULNERABILITY_ID -> PolicyViolation.Type.SECURITY;
case CWE, SEVERITY, VULNERABILITY_ID, EPSS -> PolicyViolation.Type.SECURITY;
case AGE, COORDINATES, PACKAGE_URL, CPE, SWID_TAGID, COMPONENT_HASH, VERSION, VERSION_DISTANCE ->
PolicyViolation.Type.OPERATIONAL;
case LICENSE, LICENSE_GROUP -> PolicyViolation.Type.LICENSE;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* This file is part of Dependency-Track.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
* Copyright (c) OWASP Foundation. All Rights Reserved.
*/
package org.dependencytrack.policy;

import java.math.BigDecimal;
import java.util.List;

import org.dependencytrack.PersistenceCapableTest;
import org.dependencytrack.model.Component;
import org.dependencytrack.model.Policy;
import org.dependencytrack.model.PolicyCondition;
import org.dependencytrack.model.Project;
import org.dependencytrack.model.Vulnerability;
import org.dependencytrack.tasks.scanners.AnalyzerIdentity;
import org.junit.Assert;
import org.junit.Test;

public class EpssPolicyEvaluatorTest extends PersistenceCapableTest {
CwePolicyEvaluator cwePolicyEvaluator = new CwePolicyEvaluator();

@Test
public void hasMatch() {
Policy policy = qm.createPolicy("Test Policy", Policy.Operator.ANY, Policy.ViolationState.INFO);
PolicyCondition condition = qm.createPolicyCondition(policy, PolicyCondition.Subject.EPSS, PolicyCondition.Operator.NUMERIC_LESS_THAN, "0.99");
Project project = new Project();
project.setName("My Project");
Component component = new Component();
component.setName("Test Component");
component.setVersion("1.0");
component.setProject(project);
Vulnerability vulnerability = new Vulnerability();
vulnerability.setVulnId("12345");
vulnerability.setSource(Vulnerability.Source.INTERNAL);
vulnerability.setEpssScore(BigDecimal.valueOf(0.33));
qm.persist(project);
qm.persist(component);
qm.persist(vulnerability);
qm.addVulnerability(vulnerability, component, AnalyzerIdentity.INTERNAL_ANALYZER);
PolicyEvaluator evaluator = new EpssPolicyEvaluator();
evaluator.setQueryManager(qm);
List<PolicyConditionViolation> violations = evaluator.evaluate(policy, component);
Assert.assertEquals(1, violations.size());
PolicyConditionViolation violation = violations.get(0);
Assert.assertEquals(component.getId(), violation.getComponent().getId());
Assert.assertEquals(condition, violation.getPolicyCondition());
}

@Test
public void wrongOperator() {
Policy policy = qm.createPolicy("Test Policy", Policy.Operator.ANY, Policy.ViolationState.INFO);
qm.createPolicyCondition(policy, PolicyCondition.Subject.EPSS, PolicyCondition.Operator.MATCHES, "0.99");
Project project = new Project();
project.setName("My Project");
Component component = new Component();
component.setName("Test Component");
component.setVersion("1.0");
component.setProject(project);
Vulnerability vulnerability = new Vulnerability();
vulnerability.setVulnId("12345");
vulnerability.setSource(Vulnerability.Source.INTERNAL);
vulnerability.setEpssScore(BigDecimal.valueOf(0.33));
qm.persist(project);
qm.persist(component);
qm.persist(vulnerability);
qm.addVulnerability(vulnerability, component, AnalyzerIdentity.INTERNAL_ANALYZER);
PolicyEvaluator evaluator = new EpssPolicyEvaluator();
evaluator.setQueryManager(qm);
List<PolicyConditionViolation> violations = evaluator.evaluate(policy, component);
Assert.assertEquals(0, violations.size());
}
}

0 comments on commit 0d8a768

Please sign in to comment.