Skip to content

Red PR: Demonstrate compliance gaps (no baseline fixes)#3

Open
joserodriguezthe1 wants to merge 4 commits into
GRCEngClub:mainfrom
joserodriguezthe1:red-pr-gap-demonstration
Open

Red PR: Demonstrate compliance gaps (no baseline fixes)#3
joserodriguezthe1 wants to merge 4 commits into
GRCEngClub:mainfrom
joserodriguezthe1:red-pr-gap-demonstration

Conversation

@joserodriguezthe1
Copy link
Copy Markdown

@joserodriguezthe1 joserodriguezthe1 commented May 12, 2026

Summary by CodeRabbit

  • New Features

    • Added compliance policy enforcement for security controls including least-privilege IAM access, audit logging, S3 versioning, KMS encryption, and TLS requirements.
    • Added infrastructure for audit trail logging and immutable evidence vault for compliance record retention.
  • Tests

    • Added comprehensive test coverage for all compliance policies.
  • Chores

    • Added automated compliance checking to CI/CD pipeline with policy validation and evidence artifacts.

Review Change Stack

@qodo-code-review
Copy link
Copy Markdown

ⓘ You've reached your Qodo monthly free-tier limit. Reviews pause until next month — upgrade your plan to continue now, or link your paid account if you already have one.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 12, 2026

📝 Walkthrough

Walkthrough

This pull request adds a comprehensive CMMC compliance framework consisting of five OPA/Rego policies that enforce cloud security controls, corresponding Terraform infrastructure to implement those controls, encrypted evidence storage with immutable compliance records, and a GitHub Actions pipeline that validates compliance on every pull request and push to main.

Changes

CMMC Compliance Framework and Implementation

Layer / File(s) Summary
Compliance Policy Rules and Tests
policies/ac315_least_privilege.rego, policies/au331_audit_logging.rego, policies/mp389_versioning.rego, policies/sc1311_cmk_encryption.rego, policies/sc138_tls_required.rego, policies/tests/*
Five OPA/Rego policies enforce CMMC controls: least-privilege IAM role actions (ac315), multi-region CloudTrail with log file validation (au331), S3 bucket versioning (mp389), S3 server-side encryption with customer-managed KMS (sc1311), and TLS enforcement for S3 buckets (sc138). Each policy includes compliant and noncompliant test fixtures.
Customer-Managed KMS Keys
terraform/kms.tf
Two AWS KMS customer-managed keys with key rotation and 7-day deletion windows: one for PHI encryption (aws_kms_key.phi) and one for evidence vault encryption (aws_kms_key.evidence), each with corresponding resource aliases.
CloudTrail Audit Logging and Evidence Vault
terraform/cloudtrail.tf, terraform/evidence_vault.tf
Multi-region CloudTrail trail (aws_cloudtrail.mgmt) delivers logs to an encrypted S3 bucket with AES256 encryption and public access restrictions; dedicated evidence vault bucket uses Object Lock with 1-day governance retention, KMS encryption, versioning, and S3 bucket key optimization.
Application Security Controls
terraform/baseline.tf
Application baseline implements the controls: S3 uploads bucket with customer-managed KMS encryption and TLS-only bucket policy, bucket versioning enabled, DynamoDB table with at-rest KMS encryption, Lambda security group with full egress, and IAM role policy granting scoped least-privilege for DynamoDB item operations, S3 object read/write, and limited KMS key usage.
GRC Gate Workflow and Orchestration
.github/workflows/grc-gate.yml, oscal/oscal/.trestle/config.ini
GitHub Actions workflow validates Terraform plans against all compliance policies via Conftest, conditionally applies on main, collects plan artifacts and receipts, signs the evidence bundle with Cosign, uploads to S3-backed evidence vault with version tracking, and retains artifacts for 90 days.

🎯 4 (Complex) | ⏱️ ~60 minutes

🐰 A hopping guide to compliance,
Five Rego rules keep us callous,
KMS keys guard PHI tight,
CloudTrail logs the night,
Evidence vaults hold the hallus! 🔐

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately describes the primary intent: demonstrating compliance gaps without implementing fixes, which aligns with the changeset of adding compliance policies, tests, and evidence collection infrastructure.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

🧹 Nitpick comments (3)
policies/tests/sc1311_cmk_encryption_test.rego (1)

23-29: ⚡ Quick win

Add a failing fixture for sse_algorithm: "AES256".

This closes an important gap between “missing encryption” and “wrong encryption type”.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@policies/tests/sc1311_cmk_encryption_test.rego` around lines 23 - 29, Add a
failing fixture where the S3 resource explicitly uses SSE with sse_algorithm set
to "AES256": update (or add) a noncompliant_input object in
policies/tests/sc1311_cmk_encryption_test.rego so the resource with address
"aws_s3_bucket.uploads" includes a server_side_encryption_configuration -> rule
-> apply_server_side_encryption_by_default object containing sse_algorithm:
"AES256" (so the test covers wrong encryption type vs missing encryption).
policies/tests/ac315_least_privilege_test.rego (1)

26-31: ⚡ Quick win

Add regression tests for nested modules and Effect: Deny cases.

Current tests won’t prevent future reintroduction of bypasses/false positives in this policy.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@policies/tests/ac315_least_privilege_test.rego` around lines 26 - 31, Add
regression tests that exercise nested module structures and explicit Effect:
"Deny" outputs so the policy cannot be bypassed; extend
policies/tests/ac315_least_privilege_test.rego by adding new test cases (e.g.,
test_nested_module_noncompliant and test_effect_deny_noncompliant) that invoke
ac315.deny with inputs containing nested module declarations and resources
marked with Effect: "Deny", assert that some msg in ac315.deny with those inputs
and that contains(msg, "AC.L2-3.1.5") holds, and also add matching compliant
inputs to assert count(ac315.deny) == 0 for nested/compliant scenarios to
prevent regressions.
policies/tests/sc138_tls_required_test.rego (1)

12-19: ⚡ Quick win

Strengthen the compliant fixture to validate actual TLS enforcement logic.

Presence of a bucket policy alone is insufficient; add policy-content checks to avoid false passes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@policies/tests/sc138_tls_required_test.rego` around lines 12 - 19, The
fixture for aws_s3_bucket_policy.uploads_tls only declares a policy resource but
doesn't include a policy document, so tests can false-pass; update the fixture
(aws_s3_bucket_policy.uploads_tls) to include a realistic "policy" or
"policy_document" JSON that asserts TLS enforcement (e.g., a Deny statement for
Principal:"*" on s3 actions/resources with Condition Bool
{"aws:SecureTransport":"false"} or an equivalent aws:SecureTransport check),
ensuring the policy references the bucket via the existing expressions.bucket
reference to validate actual TLS enforcement logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/grc-gate.yml:
- Around line 10-14: Remove the unnecessary GitHub Actions permission
"pull-requests: write" from the workflow permissions block (keeping only the
least-privilege keys currently in use: "id-token: write" and "contents: read");
locate the permissions mapping that includes "pull-requests: write" and either
delete that entry or set it to "pull-requests: none" so the workflow no longer
grants write access to pull requests.
- Around line 47-50: The "Install tfsec" step currently only downloads the
binary but doesn't execute or persist scan results; modify that step (or add
immediately after) to run tfsec against the repo (e.g., tfsec --format json -o
tfsec.json ./), let its exit code surface so the job fails on findings, and then
upload tfsec.json as evidence using actions/upload-artifact (artifact name like
"tfsec-results", path tfsec.json) so results are persisted for the gate; ensure
you reference the existing "Install tfsec" step/ID when chaining these commands
so the binary is used and no continue-on-error masks failures.

In `@policies/ac315_least_privilege.rego`:
- Around line 19-21: The policy currently checks wildcard_actions against every
statement and may flag Deny statements; restrict the check to only statements
with Effect == "Allow" by adding a guard that filters statements to those where
stmt.Effect == "Allow" before iterating over stmt.Action (e.g., in the rule that
uses policy.Statement, ensure you require stmt.Effect == "Allow" prior to the
some action in to_array(stmt.Action) and wildcard_actions[action] checks).
- Around line 16-17: The rule currently only checks
input.planned_values.root_module.resources and misses resources in nested
modules; add a helper (e.g., get_all_resources(module) or
collect_resources(module)) that returns module.resources plus resources from
module.child_modules recursively, then replace the comprehension "some r in
input.planned_values.root_module.resources" with "some r in
get_all_resources(input.planned_values.root_module)" (or equivalent) and keep
the existing filter r.type == "aws_iam_role_policy".

In `@policies/sc1311_cmk_encryption.rego`:
- Around line 13-22: The rule accesses r.expressions.rule[0] unsafely which can
fail if rule is missing or empty; update the deny clause to first check that
r.expressions.rule exists and is a non-empty array (e.g., ensure some idx with
r.expressions.rule[idx]) and only then extract
apply_server_side_encryption_by_default and sse_algorithm, so modify the
predicate in deny contains msg (referencing r.expressions.rule,
apply_server_side_encryption_by_default, and sse_algorithm) to guard array
access and skip resources that don't have those nested fields.

In `@terraform/baseline.tf`:
- Around line 94-99: The current egress block in the Lambda security group
allows 0.0.0.0/0 on all ports; change it to enforce least privilege by removing
the wide allow-all rule and adding explicit egress rules for only required
destinations (e.g., HTTPS port 443 to S3, DynamoDB, and KMS service CIDR ranges
or service-specific prefixes), or alternatively remove internet egress entirely
and create/attach VPC endpoints (aws_vpc_endpoint) for S3, DynamoDB and KMS and
update the security group egress to only allow traffic to those endpoints;
update the existing egress block (and any aws_security_group_rule resources)
accordingly so only the necessary ports/protocols and destination
CIDRs/endpoints are permitted.

In `@terraform/cloudtrail.tf`:
- Around line 15-20: The server-side encryption block for
aws_s3_bucket_server_side_encryption_configuration "trail" is using AES256;
replace it with SSE-KMS by creating or referencing a CMK (e.g.,
aws_kms_key.cloudtrail) and update the rule in the "trail" resource to set
sse_algorithm = "aws:kms" and kms_master_key_id = aws_kms_key.cloudtrail.arn;
ensure the CMK policy/grants allow CloudTrail to use the key and that
aws_s3_bucket.trail has the encryption configuration pointing to the KMS key.
- Line 7: The audit-log S3 bucket currently uses force_destroy = true which
allows destructive deletion; change this to disallow destructive deletes by
removing or setting force_destroy = false and add a lifecycle block with
prevent_destroy = true on the S3 bucket resource (the audit-log / CloudTrail
bucket resource that contains force_destroy) so Terraform refuses
accidental/dangerous deletions; ensure the resource that currently declares
force_destroy also includes lifecycle { prevent_destroy = true } to enforce
immutability of audit evidence.

In `@terraform/evidence_vault.tf`:
- Around line 21-32: The Object Lock default_retention in
aws_s3_bucket_object_lock_configuration.evidence is set to 1 day which is too
short; change the retention to a multi-year value or parameterize it: replace
the default_retention days = 1 with either years = <n> (e.g., years = 7) or
reference a new variable (e.g., var.evidence_retention_years) and set that
variable to the required number of years (1–7+ as required by policy), keeping
mode = "GOVERNANCE" and leaving depends_on as-is.

In `@terraform/kms.tf`:
- Line 7: The KMS key resource's deletion_window_in_days is set to 7 which is
too short for production; update the aws_kms_key resource(s) that contain the
deletion_window_in_days attribute (and any matching occurrences like the one at
lines referenced as 24-24) to a minimum of 30 days (or a configurable variable
default >=30) so production keys protecting PHI meet compliance and allow
recovery time; ensure you update any variable defaults and
documentation/comments that reference the old 7-day window.
- Around line 5-15: The aws_kms_key "phi" resource lacks an explicit key policy;
create and attach a least-privilege policy by building a policy document (e.g.,
data "aws_iam_policy_document" "phi_kms_policy") that explicitly grants only the
required principals (specific IAM roles/users and AWS services like
kms:Encrypt/Decrypt for S3/DynamoDB service principals or your application
roles) and denies broad access, then supply that JSON to the aws_kms_key "phi"
policy attribute (or use aws_kms_key_policy) so the CMK no longer relies on the
AWS default policy and enforces restricted usage and admin permissions. Ensure
you reference and include the same principal ARNs your system uses and include a
separate admin/steward principal with kms:* for key management only.

---

Nitpick comments:
In `@policies/tests/ac315_least_privilege_test.rego`:
- Around line 26-31: Add regression tests that exercise nested module structures
and explicit Effect: "Deny" outputs so the policy cannot be bypassed; extend
policies/tests/ac315_least_privilege_test.rego by adding new test cases (e.g.,
test_nested_module_noncompliant and test_effect_deny_noncompliant) that invoke
ac315.deny with inputs containing nested module declarations and resources
marked with Effect: "Deny", assert that some msg in ac315.deny with those inputs
and that contains(msg, "AC.L2-3.1.5") holds, and also add matching compliant
inputs to assert count(ac315.deny) == 0 for nested/compliant scenarios to
prevent regressions.

In `@policies/tests/sc1311_cmk_encryption_test.rego`:
- Around line 23-29: Add a failing fixture where the S3 resource explicitly uses
SSE with sse_algorithm set to "AES256": update (or add) a noncompliant_input
object in policies/tests/sc1311_cmk_encryption_test.rego so the resource with
address "aws_s3_bucket.uploads" includes a server_side_encryption_configuration
-> rule -> apply_server_side_encryption_by_default object containing
sse_algorithm: "AES256" (so the test covers wrong encryption type vs missing
encryption).

In `@policies/tests/sc138_tls_required_test.rego`:
- Around line 12-19: The fixture for aws_s3_bucket_policy.uploads_tls only
declares a policy resource but doesn't include a policy document, so tests can
false-pass; update the fixture (aws_s3_bucket_policy.uploads_tls) to include a
realistic "policy" or "policy_document" JSON that asserts TLS enforcement (e.g.,
a Deny statement for Principal:"*" on s3 actions/resources with Condition Bool
{"aws:SecureTransport":"false"} or an equivalent aws:SecureTransport check),
ensuring the policy references the bucket via the existing expressions.bucket
reference to validate actual TLS enforcement logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 52acea8c-5c93-4d3c-abb9-475441fc9930

📥 Commits

Reviewing files that changed from the base of the PR and between ff0edf9 and 5f632c1.

⛔ Files ignored due to path filters (8)
  • oscal/oscal/dist/assessment-plans/.keep is excluded by !**/dist/**
  • oscal/oscal/dist/assessment-results/.keep is excluded by !**/dist/**
  • oscal/oscal/dist/catalogs/.keep is excluded by !**/dist/**
  • oscal/oscal/dist/component-definitions/.keep is excluded by !**/dist/**
  • oscal/oscal/dist/mapping-collections/.keep is excluded by !**/dist/**
  • oscal/oscal/dist/plan-of-action-and-milestones/.keep is excluded by !**/dist/**
  • oscal/oscal/dist/profiles/.keep is excluded by !**/dist/**
  • oscal/oscal/dist/system-security-plans/.keep is excluded by !**/dist/**
📒 Files selected for processing (28)
  • .github/workflows/grc-gate.yml
  • README.md
  • WRITEUP.md
  • oscal/oscal/.trestle/.keep
  • oscal/oscal/.trestle/config.ini
  • oscal/oscal/assessment-plans/.keep
  • oscal/oscal/assessment-results/.keep
  • oscal/oscal/catalogs/.keep
  • oscal/oscal/component-definitions/.keep
  • oscal/oscal/mapping-collections/.keep
  • oscal/oscal/plan-of-action-and-milestones/.keep
  • oscal/oscal/profiles/.keep
  • oscal/oscal/system-security-plans/.keep
  • policies/ac315_least_privilege.rego
  • policies/au331_audit_logging.rego
  • policies/mp389_versioning.rego
  • policies/sc1311_cmk_encryption.rego
  • policies/sc138_tls_required.rego
  • policies/tests/ac315_least_privilege_test.rego
  • policies/tests/au331_audit_logging_test.rego
  • policies/tests/mp389_versioning_test.rego
  • policies/tests/sc1311_cmk_encryption_test.rego
  • policies/tests/sc138_tls_required_test.rego
  • scripts/verify-evidence.sh
  • terraform/baseline.tf
  • terraform/cloudtrail.tf
  • terraform/evidence_vault.tf
  • terraform/kms.tf

Comment on lines +10 to +14
permissions:
id-token: write
contents: read
pull-requests: write

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reduce GitHub token permissions to least privilege.

pull-requests: write is not used here and increases blast radius unnecessarily.

Suggested fix
 permissions:
   id-token: write
   contents: read
-  pull-requests: write
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
permissions:
id-token: write
contents: read
pull-requests: write
permissions:
id-token: write
contents: read
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/grc-gate.yml around lines 10 - 14, Remove the unnecessary
GitHub Actions permission "pull-requests: write" from the workflow permissions
block (keeping only the least-privilege keys currently in use: "id-token: write"
and "contents: read"); locate the permissions mapping that includes
"pull-requests: write" and either delete that entry or set it to "pull-requests:
none" so the workflow no longer grants write access to pull requests.

Comment on lines +47 to +50
- name: Install tfsec
run: |
curl -fsSL https://github.com/aquasecurity/tfsec/releases/download/v1.28.14/tfsec-linux-amd64 \
-o /usr/local/bin/tfsec && chmod +x /usr/local/bin/tfsec
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

tfsec is installed but not part of the gate.

Right now this step adds runtime but no control signal; execute it and persist results in evidence.

Suggested fix
       - name: Install tfsec
         run: |
           curl -fsSL https://github.com/aquasecurity/tfsec/releases/download/v1.28.14/tfsec-linux-amd64 \
             -o /usr/local/bin/tfsec && chmod +x /usr/local/bin/tfsec

+      - name: Step 2a - tfsec scan
+        working-directory: ${{ env.TF_WORKING_DIR }}
+        run: |
+          tfsec --format json --out $GITHUB_WORKSPACE/evidence/tfsec-results.json .
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Install tfsec
run: |
curl -fsSL https://github.com/aquasecurity/tfsec/releases/download/v1.28.14/tfsec-linux-amd64 \
-o /usr/local/bin/tfsec && chmod +x /usr/local/bin/tfsec
- name: Install tfsec
run: |
curl -fsSL https://github.com/aquasecurity/tfsec/releases/download/v1.28.14/tfsec-linux-amd64 \
-o /usr/local/bin/tfsec && chmod +x /usr/local/bin/tfsec
- name: Step 2a - tfsec scan
working-directory: ${{ env.TF_WORKING_DIR }}
run: |
tfsec --format json --out $GITHUB_WORKSPACE/evidence/tfsec-results.json .
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/grc-gate.yml around lines 47 - 50, The "Install tfsec"
step currently only downloads the binary but doesn't execute or persist scan
results; modify that step (or add immediately after) to run tfsec against the
repo (e.g., tfsec --format json -o tfsec.json ./), let its exit code surface so
the job fails on findings, and then upload tfsec.json as evidence using
actions/upload-artifact (artifact name like "tfsec-results", path tfsec.json) so
results are persisted for the gate; ensure you reference the existing "Install
tfsec" step/ID when chaining these commands so the binary is used and no
continue-on-error masks failures.

Comment on lines +16 to +17
some r in input.planned_values.root_module.resources
r.type == "aws_iam_role_policy"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Policy misses IAM policies declared in child modules.

The rule only inspects root_module.resources, so wildcard actions inside nested modules bypass this control.

Suggested fix
+iam_role_policies contains r if {
+  walk(input.planned_values.root_module, [_, r])
+  r.type == "aws_iam_role_policy"
+}
+
 deny contains msg if {
-  some r in input.planned_values.root_module.resources
-  r.type == "aws_iam_role_policy"
+  some r in iam_role_policies
   policy := json.unmarshal(r.values.policy)
   some stmt in policy.Statement
   some action in to_array(stmt.Action)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
some r in input.planned_values.root_module.resources
r.type == "aws_iam_role_policy"
iam_role_policies contains r if {
walk(input.planned_values.root_module, [_, r])
r.type == "aws_iam_role_policy"
}
deny contains msg if {
some r in iam_role_policies
policy := json.unmarshal(r.values.policy)
some stmt in policy.Statement
some action in to_array(stmt.Action)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@policies/ac315_least_privilege.rego` around lines 16 - 17, The rule currently
only checks input.planned_values.root_module.resources and misses resources in
nested modules; add a helper (e.g., get_all_resources(module) or
collect_resources(module)) that returns module.resources plus resources from
module.child_modules recursively, then replace the comprehension "some r in
input.planned_values.root_module.resources" with "some r in
get_all_resources(input.planned_values.root_module)" (or equivalent) and keep
the existing filter r.type == "aws_iam_role_policy".

Comment on lines +19 to +21
some stmt in policy.Statement
some action in to_array(stmt.Action)
wildcard_actions[action]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Filter wildcard checks to Effect: Allow statements only.

Current logic can flag defensive Deny statements and fail compliant policies.

Suggested fix
   policy := json.unmarshal(r.values.policy)
   some stmt in policy.Statement
+  upper(stmt.Effect) == "ALLOW"
   some action in to_array(stmt.Action)
   wildcard_actions[action]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
some stmt in policy.Statement
some action in to_array(stmt.Action)
wildcard_actions[action]
some stmt in policy.Statement
upper(stmt.Effect) == "ALLOW"
some action in to_array(stmt.Action)
wildcard_actions[action]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@policies/ac315_least_privilege.rego` around lines 19 - 21, The policy
currently checks wildcard_actions against every statement and may flag Deny
statements; restrict the check to only statements with Effect == "Allow" by
adding a guard that filters statements to those where stmt.Effect == "Allow"
before iterating over stmt.Action (e.g., in the rule that uses policy.Statement,
ensure you require stmt.Effect == "Allow" prior to the some action in
to_array(stmt.Action) and wildcard_actions[action] checks).

Comment on lines +13 to +22
deny contains msg if {
some r in input.configuration.root_module.resources
r.type == "aws_s3_bucket_server_side_encryption_configuration"
algo := r.expressions.rule[0].apply_server_side_encryption_by_default[0].sse_algorithm.constant_value
algo != "aws:kms"
msg := sprintf(
"[SC.L2-3.13.11] %s: S3 bucket uses %s instead of aws:kms with a CMK. Remediation: set sse_algorithm = aws:kms and provide kms_master_key_id.",
[r.address, algo]
)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add defensive check for rule array access.

Line 16 accesses r.expressions.rule[0] without verifying the array exists or has elements. If rule is empty, undefined, or not an array, the policy evaluation will fail rather than gracefully handling the missing configuration.

🛡️ Proposed fix to add defensive checks
 deny contains msg if {
   some r in input.configuration.root_module.resources
   r.type == "aws_s3_bucket_server_side_encryption_configuration"
+  count(r.expressions.rule) > 0
+  r.expressions.rule[0].apply_server_side_encryption_by_default
   algo := r.expressions.rule[0].apply_server_side_encryption_by_default[0].sse_algorithm.constant_value
   algo != "aws:kms"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
deny contains msg if {
some r in input.configuration.root_module.resources
r.type == "aws_s3_bucket_server_side_encryption_configuration"
algo := r.expressions.rule[0].apply_server_side_encryption_by_default[0].sse_algorithm.constant_value
algo != "aws:kms"
msg := sprintf(
"[SC.L2-3.13.11] %s: S3 bucket uses %s instead of aws:kms with a CMK. Remediation: set sse_algorithm = aws:kms and provide kms_master_key_id.",
[r.address, algo]
)
}
deny contains msg if {
some r in input.configuration.root_module.resources
r.type == "aws_s3_bucket_server_side_encryption_configuration"
count(r.expressions.rule) > 0
r.expressions.rule[0].apply_server_side_encryption_by_default
algo := r.expressions.rule[0].apply_server_side_encryption_by_default[0].sse_algorithm.constant_value
algo != "aws:kms"
msg := sprintf(
"[SC.L2-3.13.11] %s: S3 bucket uses %s instead of aws:kms with a CMK. Remediation: set sse_algorithm = aws:kms and provide kms_master_key_id.",
[r.address, algo]
)
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@policies/sc1311_cmk_encryption.rego` around lines 13 - 22, The rule accesses
r.expressions.rule[0] unsafely which can fail if rule is missing or empty;
update the deny clause to first check that r.expressions.rule exists and is a
non-empty array (e.g., ensure some idx with r.expressions.rule[idx]) and only
then extract apply_server_side_encryption_by_default and sse_algorithm, so
modify the predicate in deny contains msg (referencing r.expressions.rule,
apply_server_side_encryption_by_default, and sse_algorithm) to guard array
access and skip resources that don't have those nested fields.

Comment thread terraform/cloudtrail.tf

resource "aws_s3_bucket" "trail" {
bucket = "${local.name_prefix}-cloudtrail-${local.suffix}"
force_destroy = true
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Do not allow destructive deletion of the audit-log bucket.

force_destroy = true permits wiping CloudTrail evidence, which is a compliance blocker for audit integrity.

Suggested fix
 resource "aws_s3_bucket" "trail" {
   bucket        = "${local.name_prefix}-cloudtrail-${local.suffix}"
-  force_destroy = true
+  force_destroy = false
+
+  lifecycle {
+    prevent_destroy = true
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
force_destroy = true
force_destroy = false
lifecycle {
prevent_destroy = true
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@terraform/cloudtrail.tf` at line 7, The audit-log S3 bucket currently uses
force_destroy = true which allows destructive deletion; change this to disallow
destructive deletes by removing or setting force_destroy = false and add a
lifecycle block with prevent_destroy = true on the S3 bucket resource (the
audit-log / CloudTrail bucket resource that contains force_destroy) so Terraform
refuses accidental/dangerous deletions; ensure the resource that currently
declares force_destroy also includes lifecycle { prevent_destroy = true } to
enforce immutability of audit evidence.

Comment thread terraform/cloudtrail.tf
Comment on lines +15 to +20
resource "aws_s3_bucket_server_side_encryption_configuration" "trail" {
bucket = aws_s3_bucket.trail.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Use SSE-KMS for CloudTrail logs instead of SSE-S3.

For this compliance profile, AES256 is weaker than CMK-backed encryption and can create control drift.

Suggested fix
 resource "aws_s3_bucket_server_side_encryption_configuration" "trail" {
   bucket = aws_s3_bucket.trail.id
   rule {
     apply_server_side_encryption_by_default {
-      sse_algorithm = "AES256"
+      sse_algorithm     = "aws:kms"
+      kms_master_key_id = aws_kms_key.cloudtrail.arn
     }
   }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@terraform/cloudtrail.tf` around lines 15 - 20, The server-side encryption
block for aws_s3_bucket_server_side_encryption_configuration "trail" is using
AES256; replace it with SSE-KMS by creating or referencing a CMK (e.g.,
aws_kms_key.cloudtrail) and update the rule in the "trail" resource to set
sse_algorithm = "aws:kms" and kms_master_key_id = aws_kms_key.cloudtrail.arn;
ensure the CMK policy/grants allow CloudTrail to use the key and that
aws_s3_bucket.trail has the encryption configuration pointing to the KMS key.

Comment on lines +21 to +32
resource "aws_s3_bucket_object_lock_configuration" "evidence" {
bucket = aws_s3_bucket.evidence.id

rule {
default_retention {
mode = "GOVERNANCE"
days = 1
}
}

depends_on = [aws_s3_bucket_versioning.evidence]
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Evidence retention period is insufficient for compliance.

The Object Lock retention is set to only 1 day, which is far too short for compliance evidence that must satisfy audit and accountability requirements. CMMC AU.L2-3.3.1 and typical regulatory frameworks require audit evidence retention for 1-7 years depending on the specific compliance regime.

📦 Proposed fix to increase retention period
   rule {
     default_retention {
       mode = "GOVERNANCE"
-      days = 1
+      days = 2555  # 7 years
     }
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
resource "aws_s3_bucket_object_lock_configuration" "evidence" {
bucket = aws_s3_bucket.evidence.id
rule {
default_retention {
mode = "GOVERNANCE"
days = 1
}
}
depends_on = [aws_s3_bucket_versioning.evidence]
}
resource "aws_s3_bucket_object_lock_configuration" "evidence" {
bucket = aws_s3_bucket.evidence.id
rule {
default_retention {
mode = "GOVERNANCE"
days = 2555 # 7 years
}
}
depends_on = [aws_s3_bucket_versioning.evidence]
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@terraform/evidence_vault.tf` around lines 21 - 32, The Object Lock
default_retention in aws_s3_bucket_object_lock_configuration.evidence is set to
1 day which is too short; change the retention to a multi-year value or
parameterize it: replace the default_retention days = 1 with either years = <n>
(e.g., years = 7) or reference a new variable (e.g.,
var.evidence_retention_years) and set that variable to the required number of
years (1–7+ as required by policy), keeping mode = "GOVERNANCE" and leaving
depends_on as-is.

Comment thread terraform/kms.tf
Comment on lines +5 to +15
resource "aws_kms_key" "phi" {
description = "CMK for Acme Health PHI data - S3 and DynamoDB"
deletion_window_in_days = 7
enable_key_rotation = true

tags = {
Name = "${local.name_prefix}-phi-cmk"
ControlRef = "SC.L2-3.13.11"
DataClass = "phi"
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift

Consider defining explicit KMS key policies.

Neither KMS key has an explicit key policy defined, which means they will use AWS's default key policy. For CMMC compliance and defense-in-depth, explicitly define key policies that enforce least-privilege access and restrict key usage to authorized principals only.

Also applies to: 22-31

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@terraform/kms.tf` around lines 5 - 15, The aws_kms_key "phi" resource lacks
an explicit key policy; create and attach a least-privilege policy by building a
policy document (e.g., data "aws_iam_policy_document" "phi_kms_policy") that
explicitly grants only the required principals (specific IAM roles/users and AWS
services like kms:Encrypt/Decrypt for S3/DynamoDB service principals or your
application roles) and denies broad access, then supply that JSON to the
aws_kms_key "phi" policy attribute (or use aws_kms_key_policy) so the CMK no
longer relies on the AWS default policy and enforces restricted usage and admin
permissions. Ensure you reference and include the same principal ARNs your
system uses and include a separate admin/steward principal with kms:* for key
management only.

Comment thread terraform/kms.tf

resource "aws_kms_key" "phi" {
description = "CMK for Acme Health PHI data - S3 and DynamoDB"
deletion_window_in_days = 7
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Increase KMS key deletion window for production compliance.

The deletion_window_in_days = 7 is dangerously short for production KMS keys protecting PHI and compliance evidence. AWS recommends a minimum of 30 days to prevent accidental data loss and allow sufficient time for key recovery.

🔐 Proposed fix to increase deletion window
 resource "aws_kms_key" "phi" {
   description             = "CMK for Acme Health PHI data - S3 and DynamoDB"
-  deletion_window_in_days = 7
+  deletion_window_in_days = 30
   enable_key_rotation     = true
 resource "aws_kms_key" "evidence" {
   description             = "CMK for evidence vault encryption"
-  deletion_window_in_days = 7
+  deletion_window_in_days = 30
   enable_key_rotation     = true

Also applies to: 24-24

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@terraform/kms.tf` at line 7, The KMS key resource's deletion_window_in_days
is set to 7 which is too short for production; update the aws_kms_key
resource(s) that contain the deletion_window_in_days attribute (and any matching
occurrences like the one at lines referenced as 24-24) to a minimum of 30 days
(or a configurable variable default >=30) so production keys protecting PHI meet
compliance and allow recovery time; ensure you update any variable defaults and
documentation/comments that reference the old 7-day window.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant