Skip to content

Commit 3c09108

Browse files
authored
feat: policies
2 parents 620ca39 + 9a6aaa7 commit 3c09108

8 files changed

Lines changed: 285 additions & 0 deletions

File tree

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: Build and Upload Artifacts
2+
3+
on:
4+
workflow_call:
5+
6+
jobs:
7+
release:
8+
runs-on: ubuntu-latest
9+
steps:
10+
- name: Check out repository code
11+
uses: actions/checkout@v4
12+
- uses: actions/setup-go@v5
13+
- name: Setup OPA
14+
uses: open-policy-agent/setup-opa@v2
15+
with:
16+
version: latest
17+
- name: Run OPA Build
18+
run: |
19+
mkdir -p dist/
20+
opa build -b policies -o dist/bundle.tar.gz
21+
- name: Bundle
22+
uses: softprops/action-gh-release@v2
23+
with:
24+
files: dist/bundle.tar.gz
25+
- name: Install gooci cli
26+
run: go install github.com/compliance-framework/gooci@latest
27+
- name: Authenticate gooci cli
28+
run: gooci login ghcr.io --username ${{ github.actor }} --password ${{ secrets.GITHUB_TOKEN }}
29+
- name: gooci Upload Version
30+
run: gooci upload-single dist/bundle.tar.gz ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}:${{github.ref_name}}
31+
- name: gooci Upload Latest
32+
if: "!github.event.release.prerelease"
33+
run: gooci upload-single dist/bundle.tar.gz ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}:latest
34+

.github/workflows/push.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
name: Push
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches:
7+
- '*'
8+
9+
jobs:
10+
test:
11+
permissions:
12+
contents: read
13+
uses: ./.github/workflows/test.yml

.github/workflows/release.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
name: New Release
2+
3+
on:
4+
push:
5+
tags:
6+
- '*'
7+
8+
jobs:
9+
release:
10+
permissions:
11+
packages: write
12+
contents: write
13+
uses: ./.github/workflows/build-and-upload.yml

.github/workflows/test.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name: OPA Test
2+
3+
on:
4+
workflow_call:
5+
6+
jobs:
7+
test:
8+
runs-on: ubuntu-latest
9+
10+
steps:
11+
- name: Check out repository code
12+
uses: actions/checkout@v4
13+
14+
- name: Setup OPA
15+
uses: open-policy-agent/setup-opa@v2
16+
with:
17+
version: 1.6.0
18+
19+
- name: Run OPA Tests
20+
run: opa test policies
21+
22+
- name: Run OPA Check
23+
run: opa check policies

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
.idea/
2+
data/
3+
dist/
4+
*.tar.gz
5+
6+
testdata/

Makefile

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Check if OPA CLI is installed
2+
OPA := $(shell command -v opa 2> /dev/null)
3+
ifeq ($(OPA),)
4+
$(error "opa CLI not found. Please install it: https://www.openpolicyagent.org/docs/latest/cli/")
5+
endif
6+
7+
##@ Help
8+
help: ## Display this concise help, ie only the porcelain target
9+
@awk 'BEGIN {FS = ":.*##"; printf "\033[1mUsage\033[0m\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-30s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
10+
11+
help-all: ## Display all help items, ie including plumbing targets
12+
@awk 'BEGIN {FS = ":.*#"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?#/ { printf " \033[36m%-25s\033[0m %s\n", $$1, $$2 } /^#@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
13+
14+
##@ Policies
15+
test: ## Test policy files
16+
@OPA test policies
17+
18+
validate: ## Validate policy files
19+
@opa check policies
20+
21+
clean: # Cleanup build artifacts
22+
@rm -f dist/*
23+
24+
build: clean ## Build the policy bundle
25+
@mkdir -p dist/
26+
@opa build -b policies -o dist/bundle.tar.gz
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package compliance_framework.cloud_custodian_resources_detected
2+
3+
# Policy:
4+
# 1) any matched resources in a Cloud Custodian check should produce a violation.
5+
# 2) any execution error in a Cloud Custodian check should produce a violation.
6+
7+
check_name := object.get(object.get(input, "check", {}), "name", "unknown-check")
8+
9+
execution := object.get(input, "execution", {})
10+
result := object.get(input, "result", {})
11+
12+
check_status := object.get(execution, "status", "unknown")
13+
14+
resources := object.get(result, "resources", [])
15+
16+
resource_count := count(resources) if {
17+
is_array(resources)
18+
}
19+
20+
resource_count := 0 if {
21+
not is_array(resources)
22+
}
23+
24+
has_resources if {
25+
is_array(resources)
26+
resource_count > 0
27+
}
28+
29+
execution_error_message := object.get(execution, "error", "")
30+
execution_error_list := object.get(execution, "errors", [])
31+
32+
has_execution_error if {
33+
execution_error_message != ""
34+
}
35+
36+
has_execution_error if {
37+
is_array(execution_error_list)
38+
count(execution_error_list) > 0
39+
}
40+
41+
violation[{"remarks": msg}] if {
42+
has_resources
43+
msg := sprintf("Cloud Custodian check %q matched %d resource(s).", [check_name, resource_count])
44+
}
45+
46+
violation[{"remarks": msg}] if {
47+
has_execution_error
48+
msg := sprintf("Cloud Custodian check %q failed during execution (status=%q). error=%v errors=%v", [check_name, check_status, execution_error_message, execution_error_list])
49+
}
50+
51+
title := sprintf("Cloud Custodian check %q status=%q", [check_name, check_status])
52+
53+
description := sprintf("Cloud Custodian check %q evaluated with status %q and matched %d resource(s). Execution errors (if any) are treated as violations.", [check_name, check_status, resource_count])
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package compliance_framework.cloud_custodian_resources_detected_test
2+
3+
import data.compliance_framework.cloud_custodian_resources_detected
4+
5+
test_violation_when_resources_detected if {
6+
fixture := {
7+
"check": {"name": "ec2-public-ip-check"},
8+
"execution": {"status": "success", "error": "", "errors": []},
9+
"result": {
10+
"resources": [{"InstanceId": "i-123"}]
11+
}
12+
}
13+
14+
cloud_custodian_resources_detected.violation[{
15+
"remarks": "Cloud Custodian check \"ec2-public-ip-check\" matched 1 resource(s)."
16+
}] with input as fixture
17+
}
18+
19+
test_violation_when_execution_error_detected if {
20+
fixture := {
21+
"check": {"name": "ec2-public-ip-check"},
22+
"execution": {
23+
"status": "error",
24+
"error": "custodian execution failed",
25+
"errors": ["custodian execution failed"]
26+
},
27+
"result": {
28+
"resources": []
29+
}
30+
}
31+
32+
cloud_custodian_resources_detected.violation[{
33+
"remarks": "Cloud Custodian check \"ec2-public-ip-check\" failed during execution (status=\"error\"). error=custodian execution failed errors=[\"custodian execution failed\"]"
34+
}] with input as fixture
35+
}
36+
37+
test_violation_when_only_execution_errors_list_present if {
38+
fixture := {
39+
"check": {"name": "ec2-public-ip-check"},
40+
"execution": {
41+
"status": "error",
42+
"error": "",
43+
"errors": ["custodian execution failed"]
44+
},
45+
"result": {
46+
"resources": []
47+
}
48+
}
49+
50+
count(cloud_custodian_resources_detected.violation) == 1 with input as fixture
51+
52+
cloud_custodian_resources_detected.violation[{
53+
"remarks": "Cloud Custodian check \"ec2-public-ip-check\" failed during execution (status=\"error\"). error= errors=[\"custodian execution failed\"]"
54+
}] with input as fixture
55+
}
56+
57+
test_dynamic_title_and_description if {
58+
fixture := {
59+
"check": {"name": "ec2-public-ip-check"},
60+
"execution": {"status": "error", "error": "boom", "errors": ["boom"]},
61+
"result": {
62+
"resources": [{"InstanceId": "i-123"}, {"InstanceId": "i-456"}]
63+
}
64+
}
65+
66+
cloud_custodian_resources_detected.title == "Cloud Custodian check \"ec2-public-ip-check\" status=\"error\"" with input as fixture
67+
cloud_custodian_resources_detected.description == "Cloud Custodian check \"ec2-public-ip-check\" evaluated with status \"error\" and matched 2 resource(s). Execution errors (if any) are treated as violations." with input as fixture
68+
69+
count(cloud_custodian_resources_detected.violation) == 2 with input as fixture
70+
71+
cloud_custodian_resources_detected.violation[{
72+
"remarks": "Cloud Custodian check \"ec2-public-ip-check\" matched 2 resource(s)."
73+
}] with input as fixture
74+
75+
cloud_custodian_resources_detected.violation[{
76+
"remarks": "Cloud Custodian check \"ec2-public-ip-check\" failed during execution (status=\"error\"). error=boom errors=[\"boom\"]"
77+
}] with input as fixture
78+
}
79+
80+
test_no_violation_when_no_resources if {
81+
fixture := {
82+
"check": {"name": "ec2-public-ip-check"},
83+
"execution": {"status": "success", "error": "", "errors": []},
84+
"result": {
85+
"resources": []
86+
}
87+
}
88+
89+
count(cloud_custodian_resources_detected.violation) == 0 with input as fixture
90+
}
91+
92+
test_violation_when_check_missing_uses_unknown_check if {
93+
fixture := {
94+
"execution": {"status": "success", "error": "", "errors": []},
95+
"result": {
96+
"resources": [{"InstanceId": "i-123"}]
97+
}
98+
}
99+
100+
cloud_custodian_resources_detected.violation[{
101+
"remarks": "Cloud Custodian check \"unknown-check\" matched 1 resource(s)."
102+
}] with input as fixture
103+
}
104+
105+
test_no_violation_when_resources_is_not_array if {
106+
fixture := {
107+
"check": {"name": "ec2-public-ip-check"},
108+
"execution": {"status": "success", "error": "", "errors": []},
109+
"result": {
110+
"resources": null
111+
}
112+
}
113+
114+
count(cloud_custodian_resources_detected.violation) == 0 with input as fixture
115+
cloud_custodian_resources_detected.title == "Cloud Custodian check \"ec2-public-ip-check\" status=\"success\"" with input as fixture
116+
cloud_custodian_resources_detected.description == "Cloud Custodian check \"ec2-public-ip-check\" evaluated with status \"success\" and matched 0 resource(s). Execution errors (if any) are treated as violations." with input as fixture
117+
}

0 commit comments

Comments
 (0)