Skip to content

Commit 318e2ac

Browse files
improved project ownersguo assignment and changes query to suport not behind condition cases
1 parent a78ddbc commit 318e2ac

File tree

3 files changed

+67
-73
lines changed

3 files changed

+67
-73
lines changed

assets/queries/terraform/gcp/logs_and_alerts_missing_audit_configuration_changes/query.rego

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ not_one_valid_log_and_alert_pair(log_resources, alert_resources) = results {
4848
alert_resources[_].value != []
4949
logs_filters_data := [log | log := get_data(log_resources[_].value[log_name], "google_logging_metric", log_name, log_resources[_].document_index)]
5050

51-
valid_logs_names := [logs_filters_data[i2].name | single_match(logs_filters_data[i2].filter)]#regex.match(regex_pattern,logs_filters_data[i2].filter)]
51+
valid_logs_names := [logs_filters_data[i2].name | single_match(logs_filters_data[i2].filter)]
5252

5353
alerts_filters_data := [alert | alert := get_data(alert_resources[_].value[name_al], "google_monitoring_alert_policy", name_al, log_resources[_].document_index)]
5454

assets/queries/terraform/gcp/logs_and_alerts_missing_project_ownership_assignment_and_changes/query.rego

Lines changed: 54 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,15 @@ package Cx
33
import data.generic.common as common_lib
44
import data.generic.terraform as tf_lib
55

6+
types := {"google_logging_metric", "google_monitoring_alert_policy"}
7+
8+
service_name_pattern := "protopayload\\.servicename=\"cloudresourcemanager\\.googleapis\\.com\""
9+
ownership_pattern_1 := "\\(projectownershiporprojectownerinvitee\\)"
10+
ownership_pattern_2 := "\\(projectownerinviteeorprojectownership\\)"
11+
binding_action_remove := "protopayload\\.servicedata\\.policydelta\\.bindingdeltas\\.action=\"remove\""
12+
binding_action_add := "protopayload\\.servicedata\\.policydelta\\.bindingdeltas\\.action=\"add\""
13+
binding_role_owner := "protopayload\\.servicedata\\.policydelta\\.bindingdeltas\\.role=\"roles/owner\""
14+
615
CxPolicy[result] {
716
log_resources := [{"value": object.get(input.document[index].resource, "google_logging_metric", []), "document_index": index}]
817
alert_resources := [{"value": object.get(input.document[index].resource, "google_monitoring_alert_policy", []), "document_index": index}]
@@ -23,9 +32,10 @@ CxPolicy[result] {
2332
not_one_valid_log_and_alert_pair(log_resources, alert_resources) = results {
2433
log_resources[_].value != []
2534
logs_filters_data := [log | log := get_data(log_resources[_].value[log_name], "google_logging_metric", log_name, log_resources[_].document_index)]
26-
results := [res | filters_data := logs_filters_data[i]
27-
keyActualValue := single_match(filters_data)
28-
keyActualValue != null
35+
36+
results := [res |
37+
filters_data := logs_filters_data[i]
38+
not single_match(filters_data.filter)
2939
res := {
3040
"documentId": input.document[logs_filters_data[i].doc_index].id,
3141
"resourceType": "google_logging_metric",
@@ -35,16 +45,16 @@ not_one_valid_log_and_alert_pair(log_resources, alert_resources) = results {
3545
"keyExpectedValue": "At least one 'google_logging_metric' resource should capture project ownership assignment and changes",
3646
"keyActualValue": "No 'google_logging_metric' resource captures project ownership assignment and changes",
3747
"searchLine": common_lib.build_search_line(logs_filters_data[i].searchArray, [])
38-
}]
48+
}
49+
]
3950
count(results) == count(logs_filters_data) # if a single filter is valid it should not flag
4051
} else = results {
4152
log_resources[_].value != []
4253
alert_resources[_].value != []
4354
logs_filters_data := [log | log := get_data(log_resources[_].value[log_name], "google_logging_metric", log_name, log_resources[_].document_index)]
4455

4556
valid_logs_names := [logs_filters_data[i2].name |
46-
lines := process_filter(logs_filters_data[i2].filter)
47-
is_improper_filter(lines) == null
57+
single_match(logs_filters_data[i2].filter)
4858
]
4959

5060
alerts_filters_data := [alert | alert := get_data(alert_resources[_].value[name_al], "google_monitoring_alert_policy", name_al, log_resources[_].document_index)]
@@ -96,24 +106,27 @@ get_data(resource, type, name, doc_index) = filter {
96106
}
97107
}
98108

99-
single_match(filter){
109+
single_match(filter) {
100110
processed_filter := lower(regex.replace(filter, "\\s+", ""))
101111
is_valid_filter(processed_filter)
102-
# lines := process_filter(filters_data.filter)
103-
# keyActualValue := is_improper_filter(lines)
112+
}
113+
114+
is_valid_filter(filter) {
115+
service_name_valid(filter) # checks if serviceName is defined to "cloudresourcemanager.googleapis.com"
116+
ownership_valid(filter) # checks if (ProjectOwnership OR projectOwnerInvitee) is present
117+
remove_owner_valid(filter) # checks if (action="REMOVE" AND role="roles/owner") is present
118+
add_owner_valid(filter) # checks if (action="ADD" AND role="roles/owner") is present
104119
}
105120

106121
has_regex_match_or_reference(alerts_filters_data, valid_logs_names) = true {
107-
lines := process_filter(alerts_filters_data[i].filter)
108-
is_improper_filter(lines) == null
122+
single_match(alerts_filters_data[i].filter)
109123
alerts_filters_data[i].resource.notification_channels
110124
} else = true {
111125
alerts_filters_data[i].allows_ref == true
112126
alerts_filters_data[i].resource.notification_channels
113127
contains(alerts_filters_data[i].filter, sprintf("logging.googleapis.com/user/%s",[valid_logs_names[_]]))
114-
} else = index {
115-
lines := process_filter(alerts_filters_data[index].filter)
116-
is_improper_filter(lines) == null
128+
} else = index { # correct filter but missing notification_channels
129+
single_match(alerts_filters_data[index].filter)
117130
} else = index {
118131
alerts_filters_data[index].allows_ref == true
119132
contains(alerts_filters_data[index].filter, sprintf("logging.googleapis.com/user/%s",[valid_logs_names[_]]))
@@ -145,71 +158,40 @@ get_results(alerts_filters_data, value) = results {
145158
}]
146159
}
147160

148-
# definir aqui algo como o is_improper_filter
149-
is_improper_filter(lines) = keyActualValue { # serviceName not defined to 'cloudresourcemanager.googleapis.com'
150-
not correct_proto_payload_service_name(lines)
151-
keyActualValue := "is applied to the wrong serviceName"
152-
} else = keyActualValue {
153-
not has_project_ownership_or_invitee(lines) # does not have ProjectOwnership or projectOwnerInvitee
154-
keyActualValue := "is not applied to ProjectOwnershop or projectOwnerInvitee"
155-
} else = keyActualValue { # bindingDeltas.action not defined to "REMOVED" and "ADD" for bingingDeltas.role="roles/owner"
156-
not has_add_or_remove_owner_or(lines)
157-
keyActualValue := "does not bind both the actions 'REMOVE' and 'ADD' to 'roles/owner' role"
158-
} else = null
159-
160-
process_filter(raw_filter) = lines {
161-
normalized := normalize_filter(raw_filter)
162-
marked := regex.replace(normalized, "\\)\\s+(AND|OR)\\s+\\(", ")\n$1(")
163-
lines := split(marked, "\n")
164-
}
165-
166-
normalize_filter(raw_filter) = normalized {
167-
step1 := regex.replace(raw_filter, "\\n", " ")
168-
step2 := regex.replace(step1, "\\s+", " ")
169-
step3 := regex.replace(step2, "\\(\\s+", "(")
170-
step4 := regex.replace(step3, "\\s+\\)", ")")
171-
step5 := regex.replace(step4, "\\s+AND\\s+", " AND ")
172-
step6 := regex.replace(step5, "\\s+OR\\s+", " OR ")
173-
normalized := regex.replace(step6, "\\s*=\\s*", "=")
174-
}
175-
176-
correct_proto_payload_service_name(lines) {
177-
regex.match("(?i)protoPayload\\.serviceName\\s*=\\s*(\\(((\\s*OR\\s*)?\".+\"(\\s*OR\\s*)?)*)?\"cloudresourcemanager.googleapis.com\"", concat("", lines))
178-
} else {
179-
regex.match("(?i)NOT\\s*protoPayload\\.serviceName\\s*!=\\s*\"cloudresourcemanager.googleapis.com\"", concat("", lines))
180-
}
181-
182-
has_project_ownership_or_invitee(lines) {
183-
regex.match("(?i)\\(\\s*(ProjectOwnership\\s*OR\\s*projectOwnerInvitee|projectOwnerInvitee\\s*OR\\s*ProjectOwnership)\\s*\\)", concat("", lines))
184-
}
185-
186-
has_add_or_remove_owner_or(lines) {
187-
has_or_add_owner(lines)
188-
has_or_remove_owner(lines)
161+
at_least_one_log(log_resources){
162+
log_resources[_].value != []
189163
}
190164

191-
has_or_add_owner(lines) {
192-
line := lines[_]
193-
startswith(trim(line, " "), "OR(")
194-
is_add_owner_block(line)
195-
}
196165

197-
has_or_remove_owner(lines) {
198-
line := lines[_]
199-
startswith(trim(line, " "), "OR(")
200-
is_remove_owner_block(line)
166+
service_name_valid(filter) { # checks if serviceName is defined to "cloudresourcemanager.googleapis.com"
167+
regex.match(service_name_pattern, filter)
168+
not regex.match(concat("", ["not", service_name_pattern]), filter)
201169
}
202170

203-
is_add_owner_block(line) {
204-
contains(line, "protoPayload.serviceData.policyDelta.bindingDeltas.action=\"ADD\"")
205-
contains(line, "protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\"")
171+
ownership_valid(filter) { # (ProjectOwnership OR projectOwnerInvitee)
172+
regex.match(ownership_pattern_1, filter)
173+
not regex.match(concat("", ["not", ownership_pattern_1]), filter)
174+
} else { # (projectOwnerInvitee OR ProjectOwnership)
175+
regex.match(ownership_pattern_2, filter)
176+
not regex.match(concat("", ["not", ownership_pattern_2]), filter)
206177
}
207178

208-
is_remove_owner_block(line) {
209-
contains(line, "protoPayload.serviceData.policyDelta.bindingDeltas.action=\"REMOVE\"")
210-
contains(line, "protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\"")
179+
remove_owner_valid(filter) { # action="REMOVE" AND role="roles/owner"
180+
pattern := concat("", ["\\(", binding_action_remove, "and", binding_role_owner, "\\)"])
181+
regex.match(pattern, filter)
182+
not regex.match(concat("", ["not", pattern]), filter)
183+
} else { # role="roles/owner" AND action="REMOVE"
184+
pattern := concat("", ["\\(", binding_role_owner, "and", binding_action_remove, "\\)"])
185+
regex.match(pattern, filter)
186+
not regex.match(concat("", ["not", pattern]), filter)
211187
}
212188

213-
at_least_one_log(log_resources){
214-
log_resources[_].value != []
189+
add_owner_valid(filter) { # action="ADD" AND role="roles/owner"
190+
pattern := concat("", ["\\(", binding_action_add, "and", binding_role_owner, "\\)"])
191+
regex.match(pattern, filter)
192+
not regex.match(concat("", ["not", pattern]), filter)
193+
} else { # role="roles/owner" AND action="ADD"
194+
pattern := concat("", ["\\(", binding_role_owner, "and", binding_action_add, "\\)"])
195+
regex.match(pattern, filter)
196+
not regex.match(concat("", ["not", pattern]), filter)
215197
}

assets/queries/terraform/gcp/logs_and_alerts_missing_project_ownership_assignment_and_changes/test/positive_expected_result.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,17 @@
4040
"severity": "MEDIUM",
4141
"line": 1,
4242
"fileName": "positive7.tf"
43+
},
44+
{
45+
"queryName": "Beta - Logs And Alerts Missing Project Ownership Assignment And Changes",
46+
"severity": "MEDIUM",
47+
"line": 1,
48+
"fileName": "positive8.tf"
49+
},
50+
{
51+
"queryName": "Beta - Logs And Alerts Missing Project Ownership Assignment And Changes",
52+
"severity": "MEDIUM",
53+
"line": 1,
54+
"fileName": "positive9.tf"
4355
}
4456
]

0 commit comments

Comments
 (0)