Skip to content

Commit 99b80eb

Browse files
authored
chore(actions): add pr-check-compliance-mapping action (#10526)
1 parent d18c5a8 commit 99b80eb

File tree

1 file changed

+180
-0
lines changed

1 file changed

+180
-0
lines changed
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
name: 'Tools: Check Compliance Mapping'
2+
3+
on:
4+
pull_request:
5+
types:
6+
- 'opened'
7+
- 'synchronize'
8+
- 'reopened'
9+
- 'labeled'
10+
- 'unlabeled'
11+
branches:
12+
- 'master'
13+
- 'v5.*'
14+
15+
concurrency:
16+
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
17+
cancel-in-progress: true
18+
19+
jobs:
20+
check-compliance-mapping:
21+
if: contains(github.event.pull_request.labels.*.name, 'no-compliance-check') == false
22+
runs-on: ubuntu-latest
23+
timeout-minutes: 15
24+
permissions:
25+
contents: read
26+
pull-requests: write
27+
28+
steps:
29+
- name: Harden Runner
30+
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
31+
with:
32+
egress-policy: block
33+
allowed-endpoints: >
34+
api.github.com:443
35+
github.com:443
36+
37+
- name: Checkout repository
38+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
39+
with:
40+
fetch-depth: 0
41+
# zizmor: ignore[artipacked]
42+
persist-credentials: true # Required by tj-actions/changed-files to fetch PR branch
43+
44+
- name: Get changed files
45+
id: changed-files
46+
uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4
47+
with:
48+
files: |
49+
prowler/providers/**/services/**/*.metadata.json
50+
prowler/compliance/**/*.json
51+
52+
- name: Check if new checks are mapped in compliance
53+
id: compliance-check
54+
run: |
55+
ADDED_METADATA="${STEPS_CHANGED_FILES_OUTPUTS_ADDED_FILES}"
56+
ALL_CHANGED="${STEPS_CHANGED_FILES_OUTPUTS_ALL_CHANGED_FILES}"
57+
58+
# Filter only new metadata files (new checks)
59+
new_checks=""
60+
for f in $ADDED_METADATA; do
61+
case "$f" in *.metadata.json) new_checks="$new_checks $f" ;; esac
62+
done
63+
64+
if [ -z "$(echo "$new_checks" | tr -d ' ')" ]; then
65+
echo "No new checks detected."
66+
echo "has_new_checks=false" >> "$GITHUB_OUTPUT"
67+
exit 0
68+
fi
69+
70+
# Collect compliance files changed in this PR
71+
changed_compliance=""
72+
for f in $ALL_CHANGED; do
73+
case "$f" in prowler/compliance/*.json) changed_compliance="$changed_compliance $f" ;; esac
74+
done
75+
76+
UNMAPPED=""
77+
MAPPED=""
78+
79+
for metadata_file in $new_checks; do
80+
check_dir=$(dirname "$metadata_file")
81+
check_id=$(basename "$check_dir")
82+
provider=$(echo "$metadata_file" | cut -d'/' -f3)
83+
84+
# Read CheckID from the metadata JSON for accuracy
85+
if [ -f "$metadata_file" ]; then
86+
json_check_id=$(python3 -c "import json; print(json.load(open('$metadata_file')).get('CheckID', ''))" 2>/dev/null || echo "")
87+
if [ -n "$json_check_id" ]; then
88+
check_id="$json_check_id"
89+
fi
90+
fi
91+
92+
# Search for the check ID in compliance files changed in this PR
93+
found_in=""
94+
for comp_file in $changed_compliance; do
95+
if grep -q "\"${check_id}\"" "$comp_file" 2>/dev/null; then
96+
found_in="${found_in}$(basename "$comp_file" .json), "
97+
fi
98+
done
99+
100+
if [ -n "$found_in" ]; then
101+
found_in=$(echo "$found_in" | sed 's/, $//')
102+
MAPPED="${MAPPED}- \`${check_id}\` (\`${provider}\`): ${found_in}"$'\n'
103+
else
104+
UNMAPPED="${UNMAPPED}- \`${check_id}\` (\`${provider}\`)"$'\n'
105+
fi
106+
done
107+
108+
echo "has_new_checks=true" >> "$GITHUB_OUTPUT"
109+
110+
if [ -n "$UNMAPPED" ]; then
111+
echo "has_unmapped=true" >> "$GITHUB_OUTPUT"
112+
else
113+
echo "has_unmapped=false" >> "$GITHUB_OUTPUT"
114+
fi
115+
116+
{
117+
echo "unmapped<<EOF"
118+
echo -e "${UNMAPPED}"
119+
echo "EOF"
120+
} >> "$GITHUB_OUTPUT"
121+
122+
{
123+
echo "mapped<<EOF"
124+
echo -e "${MAPPED}"
125+
echo "EOF"
126+
} >> "$GITHUB_OUTPUT"
127+
env:
128+
STEPS_CHANGED_FILES_OUTPUTS_ADDED_FILES: ${{ steps.changed-files.outputs.added_files }}
129+
STEPS_CHANGED_FILES_OUTPUTS_ALL_CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}
130+
131+
- name: Manage compliance review label
132+
if: steps.compliance-check.outputs.has_new_checks == 'true'
133+
env:
134+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
135+
PR_NUMBER: ${{ github.event.pull_request.number }}
136+
HAS_UNMAPPED: ${{ steps.compliance-check.outputs.has_unmapped }}
137+
run: |
138+
LABEL_NAME="needs-compliance-review"
139+
140+
if [ "$HAS_UNMAPPED" = "true" ]; then
141+
echo "Adding compliance review label to PR #${PR_NUMBER}..."
142+
gh pr edit "$PR_NUMBER" --add-label "$LABEL_NAME" --repo "${{ github.repository }}" || true
143+
else
144+
echo "Removing compliance review label from PR #${PR_NUMBER}..."
145+
gh pr edit "$PR_NUMBER" --remove-label "$LABEL_NAME" --repo "${{ github.repository }}" || true
146+
fi
147+
148+
- name: Find existing compliance comment
149+
if: steps.compliance-check.outputs.has_new_checks == 'true' && github.event.pull_request.head.repo.full_name == github.repository
150+
id: find-comment
151+
uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad # v4.0.0
152+
with:
153+
issue-number: ${{ github.event.pull_request.number }}
154+
comment-author: 'github-actions[bot]'
155+
body-includes: '<!-- compliance-mapping-check -->'
156+
157+
- name: Create or update compliance comment
158+
if: steps.compliance-check.outputs.has_new_checks == 'true' && github.event.pull_request.head.repo.full_name == github.repository
159+
uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0
160+
with:
161+
issue-number: ${{ github.event.pull_request.number }}
162+
comment-id: ${{ steps.find-comment.outputs.comment-id }}
163+
edit-mode: replace
164+
body: |
165+
<!-- compliance-mapping-check -->
166+
## Compliance Mapping Review
167+
168+
This PR adds new checks. Please verify that they have been mapped to the relevant compliance framework requirements.
169+
170+
${{ steps.compliance-check.outputs.unmapped != '' && format('### New checks not mapped to any compliance framework in this PR
171+
172+
{0}
173+
174+
> Please review whether these checks should be added to compliance framework requirements in `prowler/compliance/<provider>/`. Each compliance JSON has a `Checks` array inside each requirement — add the check ID there if it satisfies that requirement.', steps.compliance-check.outputs.unmapped) || '' }}
175+
176+
${{ steps.compliance-check.outputs.mapped != '' && format('### New checks already mapped in this PR
177+
178+
{0}', steps.compliance-check.outputs.mapped) || '' }}
179+
180+
Use the `no-compliance-check` label to skip this check.

0 commit comments

Comments
 (0)