Skip to content

Commit aa66fed

Browse files
committed
fix: add supports for parsing custom compliance messages
fix: adds better remarks and descriptions Signed-off-by: Gustavo Carvalho <gustavo.carvalho@container-solutions.com>
1 parent 0bd9235 commit aa66fed

3 files changed

Lines changed: 273 additions & 17 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,6 @@ Policies are written in [Rego](https://www.openpolicyagent.org/docs/latest/polic
2222

2323
Use the standardized per-resource payload generated by the Cloud Custodian plugin as policy input. The plugin evaluates one resource/check pair at a time with `schema_version: v2`; matched resources are marked `assessment.status: non_compliant`, and baseline resources that did not match the Cloud Custodian check are marked `assessment.status: compliant`.
2424

25+
Cloud Custodian policies may include a plugin-only `non_compliance_message` string. When a resource is marked `non_compliant`, that message is appended to the evidence description. The plugin removes this field before executing the policy with Cloud Custodian.
26+
2527
Risk templates should dedupe by individual cloud resource using the payload labels `resource_type` and `resource_id`.

policies/cloud_custodian_resources_detected.rego

Lines changed: 137 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ _check := object.get(input, "check", {})
107107
_resource := object.get(input, "resource", {})
108108
_assessment := object.get(input, "assessment", {})
109109
_execution := object.get(input, "execution", {})
110+
_raw_policy := object.get(input, "raw_policy", {})
110111

111112
input_schema_version := _default_string(object.get(input, "schema_version", ""), "unknown-schema-version")
112113

@@ -178,10 +179,44 @@ inventory_status := _default_string(object.get(_assessment, "inventory_status",
178179

179180
execution_status := _default_string(object.get(_execution, "status", ""), "unknown-execution-status")
180181

182+
execution_exit_code := object.get(_execution, "exit_code", "unknown-exit-code")
183+
181184
execution_error := _default_string(object.get(_execution, "error", ""), "")
182185

186+
execution_stderr := _default_string(object.get(_execution, "stderr", ""), "")
187+
183188
execution_errors := object.get(_execution, "errors", [])
184189

190+
execution_error_messages_from_array := messages if {
191+
is_array(execution_errors)
192+
messages := [msg |
193+
some msg in execution_errors
194+
is_string(msg)
195+
msg != ""
196+
]
197+
count(messages) > 0
198+
} else := []
199+
200+
execution_error_messages := execution_error_messages_from_array if {
201+
count(execution_error_messages_from_array) > 0
202+
} else := [execution_error] if {
203+
execution_error != ""
204+
} else := [execution_stderr] if {
205+
execution_stderr != ""
206+
} else := []
207+
208+
execution_error_details := concat("; ", execution_error_messages) if {
209+
count(execution_error_messages) > 0
210+
} else := "no detailed error message was provided"
211+
212+
raw_policy_name := _default_string(object.get(_raw_policy, "name", ""), check_name)
213+
214+
raw_policy_resource := _default_string(object.get(_raw_policy, "resource", ""), resource_type)
215+
216+
non_compliance_message := _default_string(object.get(_raw_policy, "non_compliance_message", ""), "")
217+
218+
matched_resource_count := object.get(_assessment, "matched_resource_count", "unknown")
219+
185220
supported_input if {
186221
input_schema_version == "v2"
187222
input_source == "cloud-custodian"
@@ -196,8 +231,7 @@ has_execution_error if {
196231
}
197232

198233
has_execution_error if {
199-
is_array(execution_errors)
200-
count(execution_errors) > 0
234+
count(execution_error_messages_from_array) > 0
201235
}
202236

203237
_base_labels := {
@@ -240,21 +274,62 @@ labels := object.union(
240274
_region_label,
241275
)
242276

243-
violation[{"id": violation_id, "remarks": msg}] if {
277+
is_non_compliant if {
244278
supported_input
245279
assessment_status == "non_compliant"
246-
msg := sprintf("Cloud Custodian check %q marked resource %q as non-compliant (matched=%v, inventory_status=%q).", [check_name, resource_ref, assessment_matched, inventory_status])
247280
}
248281

249-
violation[{"id": execution_violation_id, "remarks": msg}] if {
282+
is_compliant if {
283+
supported_input
284+
assessment_status == "compliant"
285+
}
286+
287+
is_execution_failed if {
250288
supported_input
251289
has_execution_error
252-
msg := sprintf("Cloud Custodian check %q failed while evaluating resource %q (execution_status=%q, error=%v, errors=%v).", [check_name, resource_ref, execution_status, execution_error, execution_errors])
253290
}
254291

255-
violation[{"id": unsupported_input_violation_id, "remarks": msg}] if {
292+
non_compliant_remark := sprintf("Resource %q failed Cloud Custodian policy %q (resource=%q); the resource was found by this policy run (matched=%v, inventory_status=%q, matched_resource_count=%v).", [resource_ref, raw_policy_name, raw_policy_resource, assessment_matched, inventory_status, matched_resource_count]) if {
293+
is_non_compliant
294+
}
295+
296+
execution_error_remark := sprintf("Cloud Custodian policy %q ran with errors while evaluating resource %q (execution_status=%q, exit_code=%v). Errors: %s.", [raw_policy_name, resource_ref, execution_status, execution_exit_code, execution_error_details]) if {
297+
is_execution_failed
298+
}
299+
300+
unsupported_input_remark := sprintf("Unsupported Cloud Custodian policy input: expected source=%q schema_version=%q but received source=%q schema_version=%q.", ["cloud-custodian", "v2", input_source, input_schema_version]) if {
301+
not supported_input
302+
}
303+
304+
violation[{"id": violation_id, "remarks": non_compliant_remark}] if {
305+
is_non_compliant
306+
}
307+
308+
violation[{"id": execution_violation_id, "remarks": execution_error_remark}] if {
309+
is_execution_failed
310+
}
311+
312+
violation[{"id": unsupported_input_violation_id, "remarks": unsupported_input_remark}] if {
313+
not supported_input
314+
}
315+
316+
remarks := sprintf("%s %s", [non_compliant_remark, execution_error_remark]) if {
317+
is_non_compliant
318+
is_execution_failed
319+
}
320+
321+
remarks := non_compliant_remark if {
322+
is_non_compliant
323+
not is_execution_failed
324+
}
325+
326+
remarks := execution_error_remark if {
327+
not is_non_compliant
328+
is_execution_failed
329+
}
330+
331+
remarks := unsupported_input_remark if {
256332
not supported_input
257-
msg := sprintf("Unsupported Cloud Custodian policy input: expected source=%q schema_version=%q but received source=%q schema_version=%q.", ["cloud-custodian", "v2", input_source, input_schema_version])
258333
}
259334

260335
title := "Cloud Custodian policy received unsupported input" if {
@@ -269,6 +344,59 @@ description := sprintf("Cloud Custodian policy expected source=%q schema_version
269344
not supported_input
270345
}
271346

272-
description := sprintf("Cloud Custodian check %q evaluated resource %q with assessment status %q and execution status %q.", [check_name, resource_ref, assessment_status, execution_status]) if {
347+
description_base := sprintf("Cloud Custodian check %q failed for resource %q.", [check_name, resource_ref]) if {
348+
supported_input
349+
is_non_compliant
350+
}
351+
352+
description_base := sprintf("Cloud Custodian check %q could not evaluate resource %q.", [check_name, resource_ref]) if {
353+
supported_input
354+
not is_non_compliant
355+
execution_status == "error"
356+
}
357+
358+
description_base := sprintf("Cloud Custodian check %q passed for resource %q.", [check_name, resource_ref]) if {
359+
supported_input
360+
is_compliant
361+
execution_status != "error"
362+
}
363+
364+
description_base := sprintf("Cloud Custodian check %q evaluated resource %q.", [check_name, resource_ref]) if {
365+
supported_input
366+
not is_non_compliant
367+
not is_compliant
368+
execution_status != "error"
369+
}
370+
371+
has_non_compliance_message if {
372+
is_non_compliant
373+
non_compliance_message != ""
374+
}
375+
376+
description_execution_error := sprintf("Execution errors: %s.", [execution_error_details]) if {
377+
execution_status == "error"
378+
}
379+
380+
description := sprintf("%s %s %s", [description_base, non_compliance_message, description_execution_error]) if {
381+
supported_input
382+
has_non_compliance_message
383+
execution_status == "error"
384+
}
385+
386+
description := sprintf("%s %s", [description_base, non_compliance_message]) if {
387+
supported_input
388+
has_non_compliance_message
389+
execution_status != "error"
390+
}
391+
392+
description := sprintf("%s %s", [description_base, description_execution_error]) if {
393+
supported_input
394+
not has_non_compliance_message
395+
execution_status == "error"
396+
}
397+
398+
description := description_base if {
273399
supported_input
400+
not has_non_compliance_message
401+
execution_status != "error"
274402
}

0 commit comments

Comments
 (0)