Skip to content

Commit

Permalink
Merge pull request #4306 from francislance/fix-component-hash-policy-…
Browse files Browse the repository at this point in the history
…evaluator
  • Loading branch information
nscuro authored Nov 12, 2024
2 parents 225b98e + dced0f4 commit 0737b1d
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,17 @@ public List<PolicyConditionViolation> evaluate(final Policy policy, final Compon
for (final PolicyCondition condition : super.extractSupportedConditions(policy)) {
LOGGER.debug("Evaluating component (" + component.getUuid() + ") against policy condition (" + condition.getUuid() + ")");
final Hash hash = extractHashValues(condition);
if (matches(hash, component)) {
if (conditionApplies(hash, component, condition.getOperator())) {
LOGGER.info("Adding violation for component: " + component.getName() + " with condition: " + condition.getOperator());
violations.add(new PolicyConditionViolation(condition, component));
} else {
LOGGER.info("No violation added for component: " + component.getName() + " with condition: " + condition.getOperator());
}
}
return violations;
}

private Hash extractHashValues(PolicyCondition condition) {

if (condition.getValue() == null) {
return null;
}
Expand All @@ -74,10 +76,30 @@ private Hash extractHashValues(PolicyCondition condition) {
);
}

private boolean matches(Hash hash, Component component) {
private boolean conditionApplies(Hash hash, Component component, PolicyCondition.Operator operator) {
boolean matchFound = matches(hash, component);
LOGGER.debug("Evaluating condition: " + operator + ", Match found: " + matchFound);

return switch (operator) {
case IS -> {
LOGGER.info("IS Result: " + !matchFound);
yield matchFound; // No Violation if the hash does not match
}
case IS_NOT -> {
LOGGER.info("IS_NOT Result: " + matchFound);
yield !matchFound; // Violation if the hash match
}
default -> {
LOGGER.error("Unsupported operator: " + operator);
yield false;
}
};
}

private boolean matches(Hash hash, Component component) {
if (hash != null && hash.getAlgorithm() != null && hash.getValue() != null) {
String value = StringUtils.trimToNull(hash.getValue());
LOGGER.debug("Matching Hash: " + value + " with Component Hash for Algorithm: " + hash.getAlgorithm());
if (Hash.Algorithm.MD5.getSpec().equalsIgnoreCase(hash.getAlgorithm())) {
return value.equalsIgnoreCase(component.getMd5());
} else if (Hash.Algorithm.SHA1.getSpec().equalsIgnoreCase(hash.getAlgorithm())) {
Expand All @@ -104,7 +126,7 @@ private boolean matches(Hash hash, Component component) {
return value.equalsIgnoreCase(component.getBlake2b_512());
}
}
LOGGER.info("No match found - hash or algorithm is null.");
return false;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,17 @@
* SPDX-License-Identifier: Apache-2.0
* Copyright (c) OWASP Foundation. All Rights Reserved.
*/

/*
* Logic Explanation:
* TEST OPERATOR POLICY HASH COMPONENT HASH OUTCOME EXPLANATION
* testIsConditionWithMatch IS da39...709 da39...709 Violation Hashes match, violation. (1)
* testIsConditionNoMatch IS da39...709 abcd...f12 No Violation Hashes differ, no violation. (0)
* testIsNotConditionWithMatch IS_NOT da39...709 da39...709 No Violation Hashes match, no violation. (0)
* testIsNotConditionNoMatch IS_NOT da39...709 abcd...f12 Violation Hashes differ, violation. (1)
*/
package org.dependencytrack.policy;

import org.dependencytrack.PersistenceCapableTest;
import org.dependencytrack.model.Component;
import org.dependencytrack.model.Policy;
import org.dependencytrack.model.PolicyCondition;
Expand All @@ -28,52 +36,92 @@

import java.util.List;

public class ComponentHashPolicyEvaluatorTest extends PersistenceCapableTest {
public class ComponentHashPolicyEvaluatorTest {

PolicyEvaluator evaluator;
private ComponentHashPolicyEvaluator evaluator;

@Before
public void initEvaluator() {
evaluator = new ComponentHashPolicyEvaluator();
evaluator.setQueryManager(qm);
}

@Test
public void hasMatch() {
String hashJson = "{ 'algorithm': 'SHA3-512', 'value': 'test_hash' }";
Policy policy = qm.createPolicy("Test Policy", Policy.Operator.ANY, Policy.ViolationState.INFO);
PolicyCondition condition = qm.createPolicyCondition(policy, PolicyCondition.Subject.COMPONENT_HASH, PolicyCondition.Operator.IS, hashJson);
public void testIsConditionWithMatch() {
String hashJson = "{ 'algorithm': 'SHA-1', 'value': 'da39a3ee5e6b4b0d3255bfef95601890afd80709' }";
Policy policy = new Policy();
policy.setOperator(Policy.Operator.ANY);
policy.setViolationState(Policy.ViolationState.FAIL);

PolicyCondition condition = new PolicyCondition();
condition.setSubject(PolicyCondition.Subject.COMPONENT_HASH);
condition.setOperator(PolicyCondition.Operator.IS);
condition.setValue(hashJson);
policy.addPolicyCondition(condition);

Component component = new Component();
component.setSha1("da39a3ee5e6b4b0d3255bfef95601890afd80709");

List<PolicyConditionViolation> violations = evaluator.evaluate(policy, component);
Assert.assertEquals(1, violations.size()); // No violation expected
}

@Test
public void testIsConditionNoMatch() {
String hashJson = "{ 'algorithm': 'SHA-1', 'value': 'da39a3ee5e6b4b0d3255bfef95601890afd80709' }";
Policy policy = new Policy();
policy.setOperator(Policy.Operator.ANY);
policy.setViolationState(Policy.ViolationState.FAIL);

PolicyCondition condition = new PolicyCondition();
condition.setSubject(PolicyCondition.Subject.COMPONENT_HASH);
condition.setOperator(PolicyCondition.Operator.IS);
condition.setValue(hashJson);
policy.addPolicyCondition(condition);

Component component = new Component();
component.setName("Test Component");
component.setSha3_512("test_hash");
component.setSha1("abcdef1234567890abcdef1234567890abcdef12");

List<PolicyConditionViolation> violations = evaluator.evaluate(policy, component);
Assert.assertEquals(1, violations.size());
PolicyConditionViolation violation = violations.get(0);
Assert.assertEquals(component, violation.getComponent());
Assert.assertEquals(condition, violation.getPolicyCondition());
Assert.assertEquals(0, violations.size()); // Violation expected
}

@Test
public void noMatch() {
String hashJson = "{ 'algorithm': 'MD5', 'value': 'test_hash' }";
Policy policy = qm.createPolicy("Test Policy", Policy.Operator.ANY, Policy.ViolationState.INFO);
qm.createPolicyCondition(policy, PolicyCondition.Subject.COMPONENT_HASH, PolicyCondition.Operator.IS, hashJson);
public void testIsNotConditionWithMatch() {
String hashJson = "{ 'algorithm': 'SHA-1', 'value': 'da39a3ee5e6b4b0d3255bfef95601890afd80709' }";
Policy policy = new Policy();
policy.setOperator(Policy.Operator.ANY);
policy.setViolationState(Policy.ViolationState.FAIL);

PolicyCondition condition = new PolicyCondition();
condition.setSubject(PolicyCondition.Subject.COMPONENT_HASH);
condition.setOperator(PolicyCondition.Operator.IS_NOT);
condition.setValue(hashJson);
policy.addPolicyCondition(condition);

Component component = new Component();
component.setName("Example Component");
component.setSha1("test_hash");
component.setSha1("da39a3ee5e6b4b0d3255bfef95601890afd80709");

List<PolicyConditionViolation> violations = evaluator.evaluate(policy, component);
Assert.assertEquals(0, violations.size());
Assert.assertEquals(0, violations.size()); // Violation expected
}

@Test
public void NoMatchNoHash() {
String hashJson = "{ 'algorithm': '', 'value': '' }";
Policy policy = qm.createPolicy("Test Policy", Policy.Operator.ANY, Policy.ViolationState.INFO);
qm.createPolicyCondition(policy, PolicyCondition.Subject.COMPONENT_HASH, PolicyCondition.Operator.IS, hashJson);
public void testIsNotConditionNoMatch() {
String hashJson = "{ 'algorithm': 'SHA-1', 'value': 'da39a3ee5e6b4b0d3255bfef95601890afd80709' }";
Policy policy = new Policy();
policy.setOperator(Policy.Operator.ANY);
policy.setViolationState(Policy.ViolationState.FAIL);

PolicyCondition condition = new PolicyCondition();
condition.setSubject(PolicyCondition.Subject.COMPONENT_HASH);
condition.setOperator(PolicyCondition.Operator.IS_NOT);
condition.setValue(hashJson);
policy.addPolicyCondition(condition);

Component component = new Component();
component.setName("Example Component");
component.setSha1("test_hash");
component.setSha1("abcdef1234567890abcdef1234567890abcdef12");

List<PolicyConditionViolation> violations = evaluator.evaluate(policy, component);
Assert.assertEquals(0, violations.size());
Assert.assertEquals(1, violations.size()); // No violation expected
}
}
}

0 comments on commit 0737b1d

Please sign in to comment.