regal
regal copied to clipboard
Rule: `multi-prefix-suffix-match`
Few policies seem to make use of the new strings.any_prefix_match and strings.any_suffix_match functions.
These functions are extremely performant even when dealing with huge datasets. They have a benefit on smaller collections too though — their built-in "OR"-characteristic can often help simplify policy!
Example from the gatekeeper-library project:
Before
package k8sallowedrepos
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
satisfied := [good | repo = input.parameters.repos[_] ; good = startswith(container.image, repo)]
not any(satisfied)
msg := sprintf("container <%v> has an invalid image repo <%v>, allowed repos are %v", [container.name, container.image, input.parameters.repos])
}
violation[{"msg": msg}] {
container := input.review.object.spec.initContainers[_]
satisfied := [good | repo = input.parameters.repos[_] ; good = startswith(container.image, repo)]
not any(satisfied)
msg := sprintf("initContainer <%v> has an invalid image repo <%v>, allowed repos are %v", [container.name, container.image, input.parameters.repos])
}
violation[{"msg": msg}] {
container := input.review.object.spec.ephemeralContainers[_]
satisfied := [good | repo = input.parameters.repos[_] ; good = startswith(container.image, repo)]
not any(satisfied)
msg := sprintf("ephemeralContainer <%v> has an invalid image repo <%v>, allowed repos are %v", [container.name, container.image, input.parameters.repos])
}
After
package k8sallowedrepos
violation[{"msg": msg}] {
container_type := ["containers", "initContainers", "ephemeralContainers"][_]
container := input.review.object.spec[container_type][_]
not strings.any_prefix_match(container.image, input.parameters.repos)
msg := sprintf(
"%v <%v> has an invalid image repo <%v>, allowed repos are %v",
[trim_right(container_type, "s"), container.name, container.image, input.parameters.repos],
)
}
A rule that identifies calls to startswith
and endswith
using vars bound in iteration, and recommends considering the built-in functions created for that purpose, would be a great way to have more policy authors discover these functions, and possibly simplify some policies in the process.
The implementation might be a little tricky, as we'd need to consider not just if "loop vars" are used in these calls, but also if they are used elsewhere in the iteration. If that's the case, it's might not be worth replacing the built-in function call, but as it could be, so I imagine we could have a configuration option to toggle this for those willing to accept a few false positives. Those can always be dismissed using ignore directives.
A first iteration of this rule could identify the most simple case, i.e. startswith("foo", strings[_])
as that can always be replaced, and for the better. We can then expand to cover more advanced conditions in later updates.