DependencyTrack / dependency-track

Dependency-Track is an intelligent Component Analysis platform that allows organizations to identify and reduce risk in the software supply chain.
https://dependencytrack.org/
Apache License 2.0
2.7k stars 578 forks source link

Component Hash policy condition only supports positive matches #4230

Open francislance opened 1 month ago

francislance commented 1 month ago

Current Behavior

When you create a policy for component hash (I tested Sha1 and 256) the conditions does not behave as what it implies to be.

Steps to Reproduce

  1. Create a policy: (example) { "operator": "IS_NOT", "subject": "COMPONENT_HASH", "value": "{\"algorithm\":\"SHA-1\",\"value\":\"e1166b586cf9ca990ede7f3329853c0fe3547aa9\"}", "uuid": "5ad3139e-6f19-492a-b4ea-403d10a95a14" }

Observation:

Expected Behavior

Dependency-Track Version

4.12.0

Dependency-Track Distribution

Container Image

Database Server

PostgreSQL

Database Server Version

No response

Browser

Google Chrome

Checklist

francislance commented 4 weeks ago

I think, while reading this file contents: ComponentHashPolicyEvaluator.java

public List<PolicyConditionViolation> evaluate(final Policy policy, final Component component) {
        final List<PolicyConditionViolation> violations = new ArrayList<>();
        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)) {
                violations.add(new PolicyConditionViolation(condition, component));
            }
        }
        return violations;
    }

for these lines:

 if (matches(hash, component)) {
                violations.add(new PolicyConditionViolation(condition, component));
            }

Would it mean that it only adds a violation if the hash matches and not really handling any other comparison? If so, perhaps we can improve this.

Hope can get some help on this case. Thank you

francislance commented 3 weeks ago

I had made small changes to the code to add a logic to check operator of IS and IS_NOT as follows:

    private boolean conditionApplies(Hash hash, Component component, PolicyCondition.Operator operator) {
        boolean matchFound = matches(hash, component);

        switch (operator) {
            case IS:
                return matchFound;  // Violation if the hash match

            case IS_NOT:
                return !matchFound;  // Violation if the hash does not match

            default:
                LOGGER.warn("Unsupported operator: " + operator);
                return false;
        }
    }

I have tested the above in my local and appears to be working. Here is the link to the whole code: ComponentHashPolicyEvaluator.java

Hope to hear from Developers about this soon.

Thank you

nscuro commented 3 weeks ago

@francislance Thanks for looking into this. Your proposed change makes sense.

Separately, I think we really need to add some validation to PolicyConditionResource such that clients are informed immediately when the condition they created/updated is not supported.

francislance commented 3 weeks ago

Thanks @nscuro

Shall I do a pull request for this?

nscuro commented 3 weeks ago

That would be fantastic!

francislance commented 3 weeks ago

@nscuro just wanted to know to which branch should i be requesting to merge it? Thank you

Edit: I just created the pull request here: https://github.com/DependencyTrack/dependency-track/pull/4306

Thank you

francislance commented 2 weeks ago

Hi @nscuro, I am re-evaluating this and would like to get your opinion if it makes sense for your to do the logic this way:

Operator Policy Hash Component Hash Outcome Explanation
IS 1ef6...7c9 1ef6...7c9 No Violation Hashes match, so no violation.
IS 1ef6...7c9 705e...af8 Violation Hashes differ, so violation occurs.
IS_NOT 1ef6...7c9 e116...7aa9 No Violation Hashes differ, so no violation.
IS_NOT 1ef6...7c9 1ef6...7c9 Violation Hashes match, so violation occurs.

I made a mistake on how I evaluated the logic earlier so I am changing it as below. Let me know how you think about it. Then I will be updating the code and adding the automated test later on. Thank you. :pray:

:x: earlier code:

    private boolean conditionApplies(Hash hash, Component component, PolicyCondition.Operator operator) {
        boolean matchFound = matches(hash, component);

        switch (operator) {
            case IS:
                return matchFound;  // Violation if the hash match

            case IS_NOT:
                return !matchFound;  // Violation if the hash does not match

            default:
                LOGGER.warn("Unsupported operator: " + operator);
                return false;
        }
    }

:white_check_mark: new code:

    private boolean conditionApplies(Hash hash, Component component, PolicyCondition.Operator operator) {
        boolean matchFound = matches(hash, component);

        switch (operator) {
            case IS:
                return !matchFound;  // Violation if the hash does not match

            case IS_NOT:
                return matchFound;  // Violation if the hash matches

            default:
                LOGGER.warn("Unsupported operator: " + operator);
                return false;
        }
    }
nscuro commented 2 weeks ago

@francislance Actually the previous logic was correct. Violations are supposed to occur when their respective conditions match.

francislance commented 2 weeks ago

I see.

Edit: I have reverted to earlier described logic @nscuro Thank you 🙏