forked from GoogleCloudPlatform/policy-library
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgcp_resource_value_pattern_v1.yaml
234 lines (203 loc) · 8.55 KB
/
gcp_resource_value_pattern_v1.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
apiVersion: templates.gatekeeper.sh/v1alpha1
kind: ConstraintTemplate
metadata:
name: gcp-resource-value-pattern-v1
spec:
crd:
spec:
names:
kind: GCPResourceValuePatternConstraintV1
validation:
openAPIV3Schema:
required: ["mode", "asset_types", "field_name"]
properties:
mode:
type: string
enum: [allowlist, denylist]
description: "String identifying the operational mode, allowlist or denylist.
In allowlist mode, resources with field_name values that do NOT
match the pattern defined will generate a violation.
In denylist mode, resources with field_name values that match the
pattern defined will generate a violation.
Defaults to allowlist."
asset_types:
type: array
items:
type: string
description: "List of Google CAI asset types to apply this constraint to.
E.g. 'cloudresourcemanager.googleapis.com/Project'
E.g. 'compute.googleapis.com/Instance"
field_name:
type: string
description: "The (dot) path separated field name from the asset data to validate.
E.g. 'labels.billing_id' would indicate the value for 'data': {'labels': {'billing_id': 'x'}}
E.g. 'loggingService' would indicate the value for 'data': {'loggingService': 'y'}}"
pattern:
type: string
description: "A regular expression to validate the value of the
field name against. If the field is required and exists, the
regular expression must match the value otherwise the asset will
fail validation. Also ensure to use anchors ^$ if you need them
as by default no start and end anchors are defined.
E.g. '^\\d{4}$' will look for any four digits (1234)
E.g. 'logging.googleapis.com' will only look for 'logging.googleapis.com'"
optional:
type: boolean
description: "Set to true to make the field_name appearing in the
asset optional- a violation is NOT generated if the field_name does not exist.
Defaults to false (field_name MUST appear)."
targets:
validation.gcp.forsetisecurity.org:
rego: | #INLINE("validator/resource_value_pattern.rego")
#
# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package templates.gcp.GCPResourceValuePatternConstraintV1
import data.validator.gcp.lib as lib
###########################
# Matches any field, including nested fields in asset data against
# a regular expression pattern for validity checking.
# fields can be optional such that missing fields in data
# will not trigger a violation; or by default required
# such that missing fields will trigger a violation.
###########################
deny[{
"msg": message,
"details": metadata,
}] {
constraint := input.constraint
params := lib.get_constraint_params(constraint)
asset := input.asset
asset.asset_type = params.asset_types[_]
field_name := params.field_name
mode := lib.get_default(params, "mode", "allowlist")
rule_data := {
"is_optional": lib.get_default(params, "optional", false),
"has_field": has_field_by_path(asset.resource.data, field_name),
"has_pattern": lib.has_field(params, "pattern"),
"pattern": lib.get_default(params, "pattern", ""),
"value": get_default_by_path(asset.resource.data, field_name, ""),
}
is_not_valid(mode, rule_data)
message := sprintf("%v has %v violation for field named '%v' with a value '%v' matching pattern '%v'.", [
asset.name,
mode,
field_name,
rule_data.value,
rule_data.pattern,
])
metadata := {
"resource": asset.name,
"mode": mode,
}
}
###########################
# Rule Utilities
###########################
# is_not_valid evaluates to true if mode is allowlist and:
# optional = false; field_name exists in resource; pattern is NOT found
# optional = false; field_name does NOT exist in resource
# optional = true; field_name exists in resource; pattern is NOT found
is_not_valid(mode, rule_data) {
mode == "allowlist"
allowlist_violation(rule_data)
}
# is_not_valid evaluates to true if mode is denylist and:
# optional = false; field_name exists in resource; pattern is found
# optional = false; field_name does NOT exist in resource
# optional = true; field_name exists in resource; pattern is found
is_not_valid(mode, rule_data) {
mode == "denylist"
denylist_violation(rule_data)
}
denylist_violation(rule_data) {
is_optional_field_valid(rule_data)
is_denylist_pattern_valid(rule_data)
}
denylist_violation(rule_data) {
not is_optional_field_valid(rule_data)
}
is_optional_field_valid(rule_data) {
rule_data.has_field == true
}
is_optional_field_valid(rule_data) {
rule_data.is_optional == true
rule_data.has_field == false
}
is_denylist_pattern_valid(rule_data) {
rule_data.has_pattern == true
rule_data.has_field == true
re_match(rule_data.pattern, rule_data.value)
}
allowlist_violation(rule_data) {
is_optional_field_valid(rule_data)
not is_allowlist_pattern_valid(rule_data)
}
allowlist_violation(rule_data) {
not is_optional_field_valid(rule_data)
}
is_allowlist_pattern_valid(rule_data) {
rule_data.has_pattern == false
}
is_allowlist_pattern_valid(rule_data) {
rule_data.has_field == false
}
is_allowlist_pattern_valid(rule_data) {
rule_data.has_pattern == true
rule_data.has_field == true
re_match(rule_data.pattern, rule_data.value)
}
###########################
# Rule Library Functions
###########################
get_field_by_path(obj, path) = output {
split(path, ".", path_parts)
walk(obj, [path_parts, output])
}
# wrapper around walk to explicitly capture the output in order to generate a
# true / false output instead of undefined.
# see: https://openpolicyagent.slack.com/messages/C1H19LW4F/convo/C1H19LW4F-1552948594.244300/
_has_field_by_path(obj, path) {
_ := get_field_by_path(obj, path)
}
has_field_by_path(obj, path) {
_has_field_by_path(obj, path)
}
has_field_by_path(obj, path) = false {
not _has_field_by_path(obj, path)
}
get_default_by_path(obj, path, _default) = output {
has_field_by_path(obj, path)
output := get_field_by_path(obj, path)
}
get_default_by_path(obj, path, _default) = output {
false == has_field_by_path(obj, path)
output := _default
}
#ENDINLINE