Rule Constraint
Overview
A rule constraint is an additional constraint on pattern matching. This kind of constraints can be classified into the following:
- Pattern-based rule constraint
- Regex-based rule constraint
The remaining section describes what they are and how they behave with the following Terraform code example:
// (R1)resource "foobar" "foo" {attr1 = 1}// (R2)resource "foobar" "foo" {attr2 = 2}// (R3)resource "foobar" "foo" {size = 1}
Pattern-based Rule Constraint
A pattern-based rule constraint can filter the matches for the pattern with yet another pattern. For example, the following rule set defines a rule which matches a resource block whose body DOES include size = (blah blah)
:
version: '1'rules:- id: sample-policy-1language: hclpattern: |resource :[X] :[Y] {:[...Z]}constraints:- target: Zshould: matchpattern: size = :[_]message: |here comes your own message
On the other hand, the following rule set defines a rule which matches a resource block whose body does NOT include size = (blah blah)
.
version: '1'rules:- id: sample-policy-2language: hclpattern: |resource :[X] :[Y] {:[...Z]}constraints:- target: Zshould: not-matchpattern: size = :[_]message: |here comes your own message
sample-policy-1
matches (R3), and sample-policy-2
matches (R1) and (R2).
Regex-based Rule Constraint
A regex-based rule constraint can filter the matches for the pattern with a regular expression. For example, the following rule set defines a rule which matches a resource block whose body matches regular expressions .*attr.*
:
version: '1'rules:- id: sample-policy-3language: hclpattern: |resource :[X] :[Y] {:[...Z]}constraints:- target: Zshould: match-regexpattern: .*attr.*message: |here comes your own message
A regex-based rule constraint included in the following rule behaves the opposite of one in sample-policy-3
:
version: '1'rules:- id: sample-policy-4language: hclpattern: |resource :[X] :[Y] {:[...Z]}constraints:- target: Zshould: not-match-regexpattern: .*attr.*message: |here comes your own message
sample-policy-3
matches (R1) and (R2), and sample-policy-4
matches (R3).
Predicates
Predicates equal to available should
options. The currently available ones are:
- match
- no-match
- match-regex
- no-match-regex
- match-any-of
- not-match-any-of
- be-any-of
- not-be-any-of
The sections, Pattern-based Rule Constraint Predicate and Regex-based Rule Constraint have already explained the utilization of predicate options 1 - 4. Let's learn from 5 to 8.
A target Terraform file is below and each part shows you example rules and the expected results.
// (R1)resource "foobar" "foo" {attr1 = 1}// (R2)resource "foobar" "foo" {attr2 = 2}// (R3)resource "foobar" "foo" {size = 1}
match-any-of
The predicate match-any-of
supports both pattern-based and regex-based.
version: '1'rules:- id: sample-policy-match-any-oflanguage: hclpattern: |resource "foobar" :[Y] {:[...Z]}constraints:- target: Zshould: match-any-ofpatterns:- pattern: attr1 = 1- pattern: attr3 = 3message: |It includes either 'attr1 = 1' or 'attr3 = 3'
version: '1'rules:- id: sample-policy-match-any-oflanguage: hclpattern: |resource "foobar" :[Y] {:[...Z]}constraints:- target: Zshould: match-any-ofregex-patterns:- .*attr1.*- .*attr3.*message: |It includes either 'attr1 = 1' or 'attr3 = 3'
$ cat example.tf | shisho check policy.yaml[sample-policy-match-any-of]: It includes either 'attr1 = 1' or 'attr3 = 3'In /dev/stdin:|2 | resource "foobar" "foo" {3 | attr1 = 14 | }|
not-match-any-of
The predicate not-match-any-of
supports both pattern-based and regex-based.
version: '1'rules:- id: sample-policy-not-match-any-oflanguage: hclpattern: |resource "foobar" :[Y] {:[...Z]}constraints:- target: Zshould: not-match-any-ofpatterns:- pattern: attr1 = 1- pattern: attr3 = 3message: |It does not include either 'attr1 = 1' or 'attr3 = 3'
version: '1'rules:- id: sample-policy-not-match-any-oflanguage: hclpattern: |resource "foobar" :[Y] {:[...Z]}constraints:- target: Zshould: not-match-any-ofregex-patterns:- .*attr1.*- .*attr3.*message: |It does not include either 'attr1 = 1' or 'attr3 = 3'
$ cat example.tf | shisho check policy.yaml[sample-policy-not-match-any-of]: It does not include either 'attr1 = 1' or 'attr3 = 3'In /dev/stdin:|7 | resource "foobar" "foo" {8 | attr2 = 29 | }|
be-any-of
A sub-parameter strings
is available and this can have multiple values.
version: '1'rules:- id: sample-policy-be-any-oflanguage: hclpattern: |resource "foobar" :[Y] {:[...Z]}constraints:- target: Zshould: be-any-ofstrings:- attr1 = 1- attr3 = 3message: |It includes either 'attr1 = 1' or 'attr3 = 3'
$ cat example.tf | shisho check policy.yaml[sample-policy-be-any-of]: It includes either 'attr1 = 1' or 'attr3 = 3'In /dev/stdin:|2 | resource "foobar" "foo" {3 | attr1 = 14 | }|
not-be-any-of
A sub-parameter strings
is available and this is can have multiple values.
version: '1'rules:- id: sample-policy-not-be-any-oflanguage: hclpattern: |resource "foobar" :[Y] {:[...Z]}constraints:- target: Zshould: not-be-any-ofstrings:- attr1 = 1- attr3 = 3message: |It does not include either 'attr1 = 1' or 'attr3 = 3'
$ cat example.tf | shisho check policy.yaml[sample-policy-not-be-any-of]: It does not include either 'attr1 = 1' or 'attr3 = 3'In /dev/stdin:|12 | resource "foobar" "foo" {13 | size = 114 | }|[sample-policy-not-be-any-of]: It does not include either 'attr1 = 1' or 'attr3 = 3'In /dev/stdin:|7 | resource "foobar" "foo" {8 | attr2 = 29 | }|
Advanced Usage
The above sections explain the fundamental utilization of rule constraints. The sections demonstrate advanced techniques for more complex cases and why Shisho is powerful.
Shared Constraint
A shared constraint allows sharing metavariables among multiple patterns. For instance, in the following rule, the constraint with target: NAME behaves if they are placed in each pattern.
version: "1"rules:- id: "use-trusted-base-images"language: dockerfilemessage: |Use trusted base images if possible.patterns:- pattern: FROM :[NAME]- pattern: FROM :[NAME] AS :[ALIAS]- pattern: FROM :[NAME]@:[HASH]- pattern: FROM :[NAME]@:[HASH] AS :[ALIAS]- pattern: FROM :[NAME]::[TAG]- pattern: FROM :[NAME]::[TAG] AS :[ALIAS]- pattern: FROM :[NAME]::[TAG]@:[HASH]- pattern: FROM :[NAME]::[TAG]@:[HASH] AS :[ALIAS]constraints:- target: NAMEshould: be-any-ofstrings:- node- php
Suppose you apply the above rule to the following Dockerfile:
FROM node:10-alpineRUN mkdir /appCOPY . /appRUN chown -R node:node /appCMD ["node", "index.js"]
In this case, you'll get the following outputs from Shisho:
$ cat Dockerfile.sample | shisho check policy.yaml[use-trusted-base-images]: Use trusted base images if possible.In /dev/stdin:|1 | FROM node:10-alpine|
Nested Constraint
Nested constraints can take a pattern from the parent constraint and for rewrite options, you can use metavariables from the inside constraint pattern.
What the below example rule policy.yaml
does is:
- Search a
block
resource while capturing the body of the block as a metavariableX
- Search an
inner
block in the value of the metavariableX
while capturing the body of the block as a metavariableZ
- Search
test
attribute in the value of the metavariableZ
while capturing the value of the attribute as:[HOO]
- If it matches, extract the value of the metavariable
HOO
as a value of thetest
attribute inside theblock
block
version: "1"rules:- id: "test"language: hclmessage: |testpattern: |resource "block" :[NAME] {:[...X]}constraints:- target: Xshould: matchpattern: |inner {:[...Z]}constraints:- target: Zshould: matchpattern: |test = :[HOO]rewrite_options:- |resource "block" :[NAME] {test = :[HOO]}
This is a target file example.tf
.
resource "block" "foo" {inner {test = true}}
An expected result is below.
$ cat example.tf | shisho check policy.yaml[test]: test.In /dev/stdin:|1 | resource "block" "foo" {2 | inner {3 | test = true4 | }5 | }|Suggested changes (1):1 1 | resource "block" "foo" {2 | - inner {3 | - test = true4 | - }2 | + test = true5 3 | }