open-policy-agent / conftest

Write tests against structured configuration data using the Open Policy Agent Rego query language
https://conftest.dev
Other
2.86k stars 304 forks source link

Exceptions are always considered to be exceptions in output #548

Open jpreese opened 3 years ago

jpreese commented 3 years ago

If an exception exists, it is always considered to be an exception in the output -- regardless of if the policy failed. For example:

package main

deny_foo[msg] {
  false
  msg := "foo"
}

exception[rules] {
  rules = ["foo"]
}
echo "{}" | conftest test -

Results in:

EXCP - - main - data.main.exception[_][_] == "foo"

1 test, 0 passed, 0 warnings, 0 failures, 1 exception

However, the policy did not fail.

It feels more accurate to consider this a passed result and only trigger an exception in the output if a policy would have otherwise failed. Pending discussion from the community.

jpreese commented 3 years ago

Some thoughts from Slack:

moredhel commented 3 years ago

Hi,

just came across this. I have a similar issue when trying to enforce naming-conventions to an existing terraform project (while keeping a list of exceptions).

For example:

# main.tf
resource "null_resource" "no-exception-resource" {

}

resource "null_resource" "my-resource-name" {

}

resource "null_resource" "my_other_resource" {

}

with a rego file:

package main

names = { name | _ := input.resource[_][name] }

resource_name_exceptions = {
 "my-resource-name"
}

deny_no_dashes_in_resource_name[msg] {
    name = names[_]
    contains(name, "-")
    msg := sprintf("Name '%s' should not have any dashes in it", [name])
}

exception[rules] {
  name = names[_]
  not resource_name_exceptions[name]
  rules = ["no_dashes_in_resource_name"]
}
$ conftest test main.tf
EXCP - /Users/hamishhutchings/scratch/tf-policy/main.tf - main - data.main.exception[_][_] == "no_dashes_in_resource_name"

It's not a foolproof method, because there isn't any notification that an exception is happening, but I'm currently doing the following:

package main

names = { name | _ := input.resource[_][name] }

resource_name_exceptions = {
 "my-resource-name"
}

deny_no_dashes_in_resource_name[msg] {
    name = names[_]
    contains(name, "-")

    # Exceptions are currently limited to excluding a whole file.
    # So we need to filter out exceptions in the rule itself.
    not resource_name_exceptions[name]
    msg := sprintf("Name '%s' should not have any dashes in it", [name])
}
$ conftest test main.tf
FAIL - /Users/hamishhutchings/scratch/tf-policy/main.tf - main - Name 'no-exception-resource' should not have any dashes in it

I would suggest updating the the documentation linked here to explicitly state that an exclusion will exclude the whole file, as it's not very clear until one starts playing with it.


EDIT: I did a bit of playing, And expanding the above gives what I want:

package main

names = { name | _ := input.resource[_][name] }

resource_name_exceptions = {
 "my-resource-name"
}

deny_no_dashes_in_resource_name[msg] {
  false
}

exception[rules] {
    name = names[_]
    resource_name_exceptions[name]
    rules = ["no_dashes_in_resource_name"]
}

deny[msg] {
    name = names[_]
    contains(name, "-")

    # Exceptions are currently limited to excluding a whole file.
    # So we need to filter out exceptions in the rule itself.
    not resource_name_exceptions[name]
    msg := sprintf("Name '%s' should not have any dashes in it", [name])
}