conftest icon indicating copy to clipboard operation
conftest copied to clipboard

Failure vs successes counts are inconsistent

Open consolethinks opened this issue 1 year ago • 4 comments

Conftest doesn't count passed tests in the total amount of tests, or number of passed tests when the policy is not in the main namespace. It comes across as if the test doesn't even exist when it passes.

If, for example, I have a common namespace and a safety namespace, in addition to the main namespace, and I either pass --all-namespaces or list them separately using --namespace [name], only the policies in the main namespace will have its passed policies counted, any other namespaces' passed policies will be ignored from the total and passed counts.

consolethinks avatar Jul 29 '22 08:07 consolethinks

Hi @consolethinks I'm not able to reproduce this issue with conftest v0.33.2. Please provide snippets and the exact conftest command you are using so I can reproduce the issue.

jalseth avatar Jul 31 '22 18:07 jalseth

After doing some testing, it's not related to namespaces after all. One policy in a specific namespace seems to be the culprit.

To reproduce my issue, I'm giving an example terraform plan outputted as json: tfplan.json.zip

And here are two policies:

package common

key_val_valid_pascal_case(key, val) {
    is_pascal_case(key)
    is_pascal_case(val)
}

is_pascal_case(string) {
    re_match(`^([A-Z][a-z0-9]+)+`, string)
}


deny[msg] {
    changeset := input.resource_changes[_]
    tags := changeset.change.after.tags
    some key
    not is_pascal_case(tags[key])
    msg := sprintf("general - Non-pascal case value in tags - %v - key: %v, value: %v", [changeset.address, key, tags[key]])
}

deny[msg] {
    false
    msg := ""
}

The following command is used: conftest test tfplan.json --policy [folder containing policies] --all-namespaces

If the first policy fails for multiple resources, it will make Conftest say that there are 0 passing tests. If it is disablbed (by renaming the first deny to something else for example), Conftest will show one passing test.

consolethinks avatar Aug 02 '22 10:08 consolethinks

Thank you for the extra info, I have been able to reproduce the issue though the cause is not immediately clear.

jalseth avatar Aug 03 '22 01:08 jalseth

I've spent a little time digging and better understand the issue now. The root of the problem is due to the nature of OPA/Rego and the fact that rules are queried by name and the results are unioned. While conftest can know that there are multiple deny rules in a given policy source, it cannot know which of those caused violations as it only queries the single data.<namespace>.deny rule and all results are merged within the OPA engine. Because a single deny[msg] can produce multiple violations (as is the case here), conftest cannot meaningfully keep track of the count of failures vs. successes. I think this is a #wontfix as it is just how OPA works at a fundamental level.

The best way forward if these counts matter to your use case is to have only one deny rule per namespace and have the namespace be scoped to a specific policy violation. I've linked the relevant parts of the code below. Let me know if you have any questions.

https://github.com/open-policy-agent/conftest/blob/3aada8c2505a431ab939c4eb48fc07d34126ffe8/policy/engine.go#L314-L346 https://github.com/open-policy-agent/conftest/blob/3aada8c2505a431ab939c4eb48fc07d34126ffe8/output/standard.go#L58-L105

jalseth avatar Aug 10 '22 04:08 jalseth