feat(config): add compliance guardrails for the SDK config#11669
feat(config): add compliance guardrails for the SDK config#11669pedrooot wants to merge 18 commits into
Conversation
|
✅ All necessary |
|
✅ Conflict Markers Resolved All conflict markers have been successfully resolved in this pull request. |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds per-requirement configuration validation across all compliance output paths. A new ChangesPer-requirement configuration validation
Sequence Diagram(s)sequenceDiagram
participant CSVAdapter as CSV Adapter
participant BuildReqStatus as build_requirement_config_status
participant EvalConstraints as evaluate_config_constraints
participant ApplyStatus as apply_config_status
participant Output as Output Model
CSVAdapter->>BuildReqStatus: compliance.Requirements, audit_config
BuildReqStatus->>EvalConstraints: constraint, audit_config
EvalConstraints-->>BuildReqStatus: (is_compliant, reason)
BuildReqStatus-->>CSVAdapter: {req_id: (bool, str)}
loop For each finding/requirement match
CSVAdapter->>ApplyStatus: finding.status, status_extended, config_status
ApplyStatus-->>CSVAdapter: (row_status, row_status_extended)
CSVAdapter->>Output: Status=row_status, StatusExtended=row_status_extended
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
🔒 Container Security ScanImage: ✅ No Vulnerabilities DetectedThe container image passed all security checks. No known CVEs were found.📋 Resources:
|
There was a problem hiding this comment.
Actionable comments posted: 9
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
prowler/lib/outputs/compliance/ccc/ccc.py (1)
58-78: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winResolve one final status before updating PASS/FAIL counts.
Because config validation makes
effective_statusrequirement-specific, the same finding can be added to both PASS and FAIL totals when mapped requirements disagree. Section aggregation has the same issue because the first seen status wins. Track the current resolved status and let config-forced FAIL override PASS.🤖 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 `@prowler/lib/outputs/compliance/ccc/ccc.py` around lines 58 - 78, The issue is that effective_status varies per requirement, allowing the same finding to be counted in both PASS and FAIL totals. Before updating the muted_count, fail_count, and pass_count lists, you need to resolve a single final status per finding that aggregates across all its requirements and sections. Implement logic where a config-forced FAIL status overrides PASS across all requirements for that finding, then use this resolved final status (instead of the per-requirement effective_status) to update the overall counts. The same resolved status should be determined once per finding before the section iteration loop, ensuring each finding is counted only once in the appropriate total.prowler/lib/outputs/compliance/c5/c5.py (1)
58-78: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winPrevent mixed requirement statuses from double-counting a finding.
effective_statuscan differ per requirement, butpass_countandfail_countare independent lists. A finding mapped to both a config-invalid requirement and a valid PASS requirement can be counted in both totals, and section counts become order-dependent. Use a single per-finding/per-section resolved status with FAIL taking precedence over PASS.🤖 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 `@prowler/lib/outputs/compliance/c5/c5.py` around lines 58 - 78, The issue is that findings can have different effective_status values across multiple requirements, and the current code with separate muted_count, fail_count, and pass_count lists can double-count a single finding if it has conflicting statuses. Instead of checking if index is not in fail_count or pass_count independently, track a single resolved status per finding that applies FAIL precedence (FAIL overrides PASS). Replace the three separate lists with a dictionary mapping index to a resolved status, then in the overview totals section check and update only the resolved status for each finding, ensuring FAIL takes precedence when a finding matches both FAIL and PASS requirements. Apply the same logic to the per-section counting to maintain consistency.prowler/lib/outputs/compliance/asd_essential_eight/asd_essential_eight.py (1)
58-80: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winGive FAIL precedence when one finding maps to mixed effective statuses.
effective_statusis per requirement now, so the sameindexcan land in bothpass_countandfail_count; per-section counts can also keep an earlier PASS and ignore a later config-forced FAIL. Track the resolved status per finding/section and let FAIL override PASS.Suggested direction
- pass_count = [] - fail_count = [] - muted_count = [] + pass_count = set() + fail_count = set() + muted_count = set() @@ - section_seen[section] = set() + section_seen[section] = {} @@ if finding.muted: - if index not in muted_count: - muted_count.append(index) + muted_count.add(index) + fail_count.discard(index) + pass_count.discard(index) elif effective_status == "FAIL": - if index not in fail_count: - fail_count.append(index) + fail_count.add(index) + pass_count.discard(index) elif effective_status == "PASS": - if index not in pass_count: - pass_count.append(index) + if index not in fail_count: + pass_count.add(index) @@ - if index not in section_seen[section]: - section_seen[section].add(index) - if finding.muted: - sections[section]["Muted"] += 1 - elif effective_status == "FAIL": - sections[section]["FAIL"] += 1 - elif effective_status == "PASS": - sections[section]["PASS"] += 1 + previous_status = section_seen[section].get(index) + if finding.muted: + if previous_status is None: + section_seen[section][index] = "Muted" + sections[section]["Muted"] += 1 + elif effective_status == "FAIL": + if previous_status == "PASS": + sections[section]["PASS"] -= 1 + if previous_status != "FAIL": + section_seen[section][index] = "FAIL" + sections[section]["FAIL"] += 1 + elif effective_status == "PASS" and previous_status is None: + section_seen[section][index] = "PASS" + sections[section]["PASS"] += 1🤖 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 `@prowler/lib/outputs/compliance/asd_essential_eight/asd_essential_eight.py` around lines 58 - 80, The issue is that the same finding index can have different effective statuses since effective_status is now per requirement, causing findings to be counted in multiple status lists or counted with incorrect status in sections. Instead of using lists (muted_count, fail_count, pass_count) to track which indices have been counted, change these to dictionaries that store the resolved status for each index. Similarly, change section_seen[section] from a set to a dictionary that tracks the resolved status per finding/section pair. When processing each finding, check if the index already exists in the status tracking dictionaries: if it does and the new effective_status is FAIL, let it override any previous PASS status; if the new status is less severe than what's already recorded (like PASS when FAIL is already set), skip updating. Apply the same precedence logic (FAIL overrides PASS) to both the overview-level counts (muted_count, fail_count, pass_count) and the per-section counts (sections[section]).prowler/lib/outputs/compliance/universal/universal_table.py (1)
185-217: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy liftDeduplicate after resolving all requirement statuses for the finding.
effective_statusnow varies by requirement, but grouped/split/scored modes still de-duplicate by findingindexin the overview and per-group/per-split scoring structures. A finding mapped to both a config-valid requirement and a config-invalid requirement can be counted as both PASS and FAIL, or the first processed requirement can lock in PASS/score before a later requirement should force FAIL. Collapse all matched requirement statuses first with FAIL precedence per finding and per group/split, or change the de-duplication keys to includereq.idif the table is meant to count requirement instances.Based on the review stack context that config status is resolved per requirement and invalid configs force FAIL.
Also applies to: 303-343, 435-486
🤖 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 `@prowler/lib/outputs/compliance/universal/universal_table.py` around lines 185 - 217, The current deduplication logic using finding index only (in muted_count, fail_count, pass_count lists and group_seen dictionary) does not account for the fact that effective_status now varies per requirement due to per-requirement config status resolution. A single finding mapped to multiple requirements can be counted with conflicting statuses. Either pre-compute a single effective_status per finding by collapsing all its matched requirement statuses with FAIL taking precedence before the group_key loop, or change the deduplication keys to include req.id alongside the finding index to track status per requirement instance instead of per finding.
🤖 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 `@prowler/lib/check/compliance_config_eval.py`:
- Around line 54-76: Update the function signature of
evaluate_config_constraints to use parameterized generic types for better type
safety. Replace the bare list type in config_requirements parameter with
list[dict] and the bare dict type in audit_config parameter with dict[str, Any].
Ensure you import Any from the typing module if not already imported. Apply this
same pattern to other public functions in the module that currently use bare
generic types like list, dict, or tuple without type parameters.
- Around line 110-124: The get_scan_audit_config function catches all exceptions
broadly with a bare except Exception clause, which can hide genuine bugs
unrelated to provider availability. Replace the broad exception handler with
more specific exception types that are expected when the provider is unavailable
or unavoidable (such as AttributeError and ImportError). Alternatively, if
keeping a broad exception handler, add logging to capture unexpected exceptions
before returning the empty dict so that real errors are not silently suppressed
and can aid in debugging.
- Around line 145-220: The public functions build_requirement_config_status,
resolve_requirement_config_status, apply_config_status, and get_effective_status
have inconsistent docstring formatting that does not comply with Google-style
documentation standards. Update each of these function docstrings to include
both Args and Returns sections in a structured format, matching the pattern
already established in evaluate_config_constraints. For
build_requirement_config_status add a Returns section, and for the other three
functions convert the narrative docstrings to include both Args and Returns
sections with proper parameter and return value documentation.
In `@prowler/lib/check/compliance_models.py`:
- Around line 307-334: In the Compliance_Requirement_ConfigConstraint class, the
Value field uses a bare list type without parameterization, reducing type
safety. Replace the bare list in the Union type definition with a parameterized
version such as list[Any] or list[Union[str, int, float]] to explicitly declare
what types are allowed in the list. This will improve type-checker coverage and
clarify the expected constraint value types for operations like subset and
superset.
In `@prowler/lib/outputs/compliance/kisa_ismsp/kisa_ismsp.py`:
- Around line 72-75: The current implementation allows the same index to be
added to both fail_count and pass_count when a single finding maps to multiple
requirements with different effective_status values. Instead of appending to
separate lists, use a dictionary to track one status per finding (index) with
FAIL taking precedence over PASS. When effective_status is FAIL, set the
finding's status to FAIL; when effective_status is PASS, only set the status if
the finding is not already marked as FAIL. Apply this same FAIL-precedence logic
to the section counts tracking as well (the code around lines 85-87 that also
processes different status values).
In `@prowler/lib/outputs/compliance/mitre_attack/mitre_attack.py`:
- Around line 63-66: When a finding maps to multiple requirements with different
statuses, FAIL should take precedence over PASS. In the section where
effective_status equals FAIL, after adding the index to fail_count, also remove
it from pass_count if it exists there. This ensures that when the same index
appears in both pass_count and fail_count across different requirements, the
FAIL status wins and prevents later config-invalid requirements from incorrectly
upgrading the tactic status. Apply the same logic to the corresponding code
section mentioned in the "Also applies to" reference.
In `@prowler/lib/outputs/compliance/prowler_threatscore/prowler_threatscore.py`:
- Around line 54-59: The effective_status is calculated per requirement, but the
finding counts (pass_count, fail_count) and deduplication logic (using
pillar_seen, counted_findings_per_pillar, counted_findings_generic) only
deduplicate by finding index. This causes the same finding to be counted
multiple times if it maps to multiple ThreatScore requirements with different
config outcomes. Before updating pass_count and fail_count at the locations
around lines 74-85 and 91-115, aggregate the effective_status across all
requirements for each finding with FAIL taking precedence (so any FAIL status
overrides PASS for that finding), then use the collapsed status to determine
counting. Alternatively, change the deduplication keys from just index to
include requirement.Id if per-requirement counting is intentional.
In `@tests/lib/check/compliance_config_constraint_model_test.py`:
- Line 76: Replace all four instances of the json.load(open(_CIS_6_0)) pattern
at lines 76, 83, 94, and 109 with a context manager using a with statement to
wrap the open() call. This ensures file handles are explicitly closed rather
than relying on garbage collection, following Python best practices for resource
management.
In
`@tests/lib/outputs/compliance/universal/universal_table_config_requirements_test.py`:
- Around line 60-73: The _render function uses a hardcoded
output_directory="/tmp" path which is OS-dependent and not portable across
different systems. Add tmp_path as a parameter to the _render function (which is
a pytest fixture provided automatically) and replace the hardcoded "/tmp" string
with str(tmp_path) in the output_directory argument of the get_universal_table
call to make the test portable.
---
Outside diff comments:
In `@prowler/lib/outputs/compliance/asd_essential_eight/asd_essential_eight.py`:
- Around line 58-80: The issue is that the same finding index can have different
effective statuses since effective_status is now per requirement, causing
findings to be counted in multiple status lists or counted with incorrect status
in sections. Instead of using lists (muted_count, fail_count, pass_count) to
track which indices have been counted, change these to dictionaries that store
the resolved status for each index. Similarly, change section_seen[section] from
a set to a dictionary that tracks the resolved status per finding/section pair.
When processing each finding, check if the index already exists in the status
tracking dictionaries: if it does and the new effective_status is FAIL, let it
override any previous PASS status; if the new status is less severe than what's
already recorded (like PASS when FAIL is already set), skip updating. Apply the
same precedence logic (FAIL overrides PASS) to both the overview-level counts
(muted_count, fail_count, pass_count) and the per-section counts
(sections[section]).
In `@prowler/lib/outputs/compliance/c5/c5.py`:
- Around line 58-78: The issue is that findings can have different
effective_status values across multiple requirements, and the current code with
separate muted_count, fail_count, and pass_count lists can double-count a single
finding if it has conflicting statuses. Instead of checking if index is not in
fail_count or pass_count independently, track a single resolved status per
finding that applies FAIL precedence (FAIL overrides PASS). Replace the three
separate lists with a dictionary mapping index to a resolved status, then in the
overview totals section check and update only the resolved status for each
finding, ensuring FAIL takes precedence when a finding matches both FAIL and
PASS requirements. Apply the same logic to the per-section counting to maintain
consistency.
In `@prowler/lib/outputs/compliance/ccc/ccc.py`:
- Around line 58-78: The issue is that effective_status varies per requirement,
allowing the same finding to be counted in both PASS and FAIL totals. Before
updating the muted_count, fail_count, and pass_count lists, you need to resolve
a single final status per finding that aggregates across all its requirements
and sections. Implement logic where a config-forced FAIL status overrides PASS
across all requirements for that finding, then use this resolved final status
(instead of the per-requirement effective_status) to update the overall counts.
The same resolved status should be determined once per finding before the
section iteration loop, ensuring each finding is counted only once in the
appropriate total.
In `@prowler/lib/outputs/compliance/universal/universal_table.py`:
- Around line 185-217: The current deduplication logic using finding index only
(in muted_count, fail_count, pass_count lists and group_seen dictionary) does
not account for the fact that effective_status now varies per requirement due to
per-requirement config status resolution. A single finding mapped to multiple
requirements can be counted with conflicting statuses. Either pre-compute a
single effective_status per finding by collapsing all its matched requirement
statuses with FAIL taking precedence before the group_key loop, or change the
deduplication keys to include req.id alongside the finding index to track status
per requirement instance instead of per finding.
🪄 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: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: b9a2fd2b-3eff-4efe-a516-4104b7c59721
⛔ Files ignored due to path filters (61)
prowler/compliance/alibabacloud/cis_2.0_alibabacloud.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/alibabacloud/prowler_threatscore_alibabacloud.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/asd_essential_eight_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/aws_account_security_onboarding_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/aws_ai_security_framework_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/aws_foundational_security_best_practices_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/aws_foundational_technical_review_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/aws_well_architected_framework_security_pillar_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/c5_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/ccc_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/cis_1.4_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/cis_1.5_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/cis_2.0_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/cis_3.0_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/cis_4.0_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/cis_5.0_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/cis_6.0_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/cisa_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/ens_rd2022_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/fedramp_20x_ksi_low_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/fedramp_low_revision_4_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/fedramp_moderate_revision_4_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/ffiec_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/gdpr_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/gxp_21_cfr_part_11_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/gxp_eu_annex_11_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/hipaa_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/iso27001_2013_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/iso27001_2022_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/kisa_isms_p_2023_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/kisa_isms_p_2023_korean_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/mitre_attack_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/nis2_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/nist_800_171_revision_2_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/nist_800_53_revision_4_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/nist_800_53_revision_5_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/nist_csf_1.1_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/nist_csf_2.0_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/pci_3.2.1_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/pci_4.0_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/prowler_threatscore_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/rbi_cyber_security_framework_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/secnumcloud_3.2_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/soc2_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/azure/c5_azure.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/azure/ccc_azure.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/azure/cis_4.0_azure.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/azure/cis_5.0_azure.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/azure/hipaa_azure.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/azure/nis2_azure.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/azure/secnumcloud_3.2_azure.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/azure/soc2_azure.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/csa_ccm_4.0.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/dora_2022_2554.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/gcp/ccc_gcp.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/kubernetes/cis_1.10_kubernetes.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/kubernetes/cis_1.11_kubernetes.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/kubernetes/cis_1.12_kubernetes.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/kubernetes/cis_1.8_kubernetes.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/kubernetes/pci_4.0_kubernetes.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/kubernetes/prowler_threatscore_kubernetes.jsonis excluded by!prowler/compliance/**/*.json
📒 Files selected for processing (60)
prowler/CHANGELOG.mdprowler/lib/check/compliance_config_eval.pyprowler/lib/check/compliance_models.pyprowler/lib/outputs/compliance/asd_essential_eight/asd_essential_eight.pyprowler/lib/outputs/compliance/asd_essential_eight/asd_essential_eight_aws.pyprowler/lib/outputs/compliance/aws_well_architected/aws_well_architected.pyprowler/lib/outputs/compliance/c5/c5.pyprowler/lib/outputs/compliance/c5/c5_aws.pyprowler/lib/outputs/compliance/c5/c5_azure.pyprowler/lib/outputs/compliance/c5/c5_gcp.pyprowler/lib/outputs/compliance/ccc/ccc.pyprowler/lib/outputs/compliance/ccc/ccc_aws.pyprowler/lib/outputs/compliance/ccc/ccc_azure.pyprowler/lib/outputs/compliance/ccc/ccc_gcp.pyprowler/lib/outputs/compliance/cis/cis.pyprowler/lib/outputs/compliance/cis/cis_alibabacloud.pyprowler/lib/outputs/compliance/cis/cis_aws.pyprowler/lib/outputs/compliance/cis/cis_azure.pyprowler/lib/outputs/compliance/cis/cis_gcp.pyprowler/lib/outputs/compliance/cis/cis_github.pyprowler/lib/outputs/compliance/cis/cis_googleworkspace.pyprowler/lib/outputs/compliance/cis/cis_kubernetes.pyprowler/lib/outputs/compliance/cis/cis_m365.pyprowler/lib/outputs/compliance/cis/cis_oraclecloud.pyprowler/lib/outputs/compliance/cisa_scuba/cisa_scuba_googleworkspace.pyprowler/lib/outputs/compliance/ens/ens.pyprowler/lib/outputs/compliance/ens/ens_aws.pyprowler/lib/outputs/compliance/ens/ens_azure.pyprowler/lib/outputs/compliance/ens/ens_gcp.pyprowler/lib/outputs/compliance/generic/generic.pyprowler/lib/outputs/compliance/generic/generic_table.pyprowler/lib/outputs/compliance/iso27001/iso27001_aws.pyprowler/lib/outputs/compliance/iso27001/iso27001_azure.pyprowler/lib/outputs/compliance/iso27001/iso27001_gcp.pyprowler/lib/outputs/compliance/iso27001/iso27001_kubernetes.pyprowler/lib/outputs/compliance/iso27001/iso27001_m365.pyprowler/lib/outputs/compliance/iso27001/iso27001_nhn.pyprowler/lib/outputs/compliance/kisa_ismsp/kisa_ismsp.pyprowler/lib/outputs/compliance/kisa_ismsp/kisa_ismsp_aws.pyprowler/lib/outputs/compliance/mitre_attack/mitre_attack.pyprowler/lib/outputs/compliance/mitre_attack/mitre_attack_aws.pyprowler/lib/outputs/compliance/mitre_attack/mitre_attack_azure.pyprowler/lib/outputs/compliance/mitre_attack/mitre_attack_gcp.pyprowler/lib/outputs/compliance/prowler_threatscore/prowler_threatscore.pyprowler/lib/outputs/compliance/prowler_threatscore/prowler_threatscore_alibaba.pyprowler/lib/outputs/compliance/prowler_threatscore/prowler_threatscore_aws.pyprowler/lib/outputs/compliance/prowler_threatscore/prowler_threatscore_azure.pyprowler/lib/outputs/compliance/prowler_threatscore/prowler_threatscore_gcp.pyprowler/lib/outputs/compliance/prowler_threatscore/prowler_threatscore_kubernetes.pyprowler/lib/outputs/compliance/prowler_threatscore/prowler_threatscore_m365.pyprowler/lib/outputs/compliance/universal/ocsf_compliance.pyprowler/lib/outputs/compliance/universal/universal_table.pytests/lib/check/compliance_config_constraint_model_test.pytests/lib/check/compliance_config_eval_test.pytests/lib/check/compliance_config_requirements_data_test.pytests/lib/outputs/compliance/cis/cis_aws_config_requirements_test.pytests/lib/outputs/compliance/cis/cis_azure_config_requirements_test.pytests/lib/outputs/compliance/ens/ens_aws_config_requirements_test.pytests/lib/outputs/compliance/universal/ocsf_compliance_config_requirements_test.pytests/lib/outputs/compliance/universal/universal_table_config_requirements_test.py
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
prowler/lib/check/compliance_config_eval.py (1)
1-51: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winUnknown Operators Should Fail Closed, Not Pass
Line 1-51 currently allows unknown operators in
_check_operatorto returnTrue(per changed-range behavior), which lets malformedConfigRequirementssilently pass. That weakens the compliance guardrail and can produce false PASS outcomes.💡 Proposed Fix
- # unknown operator - return True + # unknown operator must fail closed + return False🤖 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 `@prowler/lib/check/compliance_config_eval.py` around lines 1 - 51, The _check_operator function currently returns True for unknown operators (at the end of the function after all the if conditions), which allows malformed ConfigRequirements to silently pass when they should fail. Change the final return statement that handles unknown operators from return True to return False so that any unrecognized operator causes the compliance requirement check to fail conservatively instead of passing unsafely.
🤖 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 `@docs/developer-guide/security-compliance-framework.mdx`:
- Around line 484-485: The section header "## Configuration Guardrails for
Requirements" is missing the required VersionBadge component. Add the
<VersionBadge /> component immediately on the line following the section header
to indicate when this functionality was introduced in Prowler, as per the
documentation guidelines that require Version Badge components to be placed
directly after section headers for new features.
In `@prowler/lib/check/compliance_models.py`:
- Around line 339-341: The Provider field in the compliance model is not being
normalized or validated, which allows inconsistent values (different casing,
whitespace) to bypass constraint evaluation during exact string matching. Add a
field validator to the Provider field that normalizes the value by trimming
whitespace and converting to lowercase, ensuring consistent matching regardless
of input format. This should prevent silent constraint bypasses due to provider
name mismatches.
---
Outside diff comments:
In `@prowler/lib/check/compliance_config_eval.py`:
- Around line 1-51: The _check_operator function currently returns True for
unknown operators (at the end of the function after all the if conditions),
which allows malformed ConfigRequirements to silently pass when they should
fail. Change the final return statement that handles unknown operators from
return True to return False so that any unrecognized operator causes the
compliance requirement check to fail conservatively instead of passing unsafely.
🪄 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: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 9c9cee60-8666-4562-99b5-13b398a20f37
⛔ Files ignored due to path filters (2)
prowler/compliance/csa_ccm_4.0.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/dora_2022_2554.jsonis excluded by!prowler/compliance/**/*.json
📒 Files selected for processing (7)
docs/developer-guide/configurable-checks.mdxdocs/developer-guide/security-compliance-framework.mdxprowler/lib/check/compliance_config_eval.pyprowler/lib/check/compliance_models.pytests/lib/check/compliance_config_constraint_model_test.pytests/lib/check/compliance_config_eval_test.pytests/lib/check/compliance_config_requirements_data_test.py
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #11669 +/- ##
==========================================
- Coverage 94.12% 87.85% -6.28%
==========================================
Files 247 1607 +1360
Lines 36629 54473 +17844
==========================================
+ Hits 34477 47857 +13380
- Misses 2152 6616 +4464 Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@tests/lib/check/compliance_config_eval_test.py`:
- Around line 312-318: The test_works_with_fail_pass_only_counts test method
needs an additional assertion to test the "Muted" status case. After the
existing calls to accumulate_group_status with "PASS" and "FAIL" statuses, add
another call to accumulate_group_status with "Muted" as the status parameter
(keeping the same group index of 0 and the existing counts and seen
dictionaries), then add an assertion to verify the expected behavior when a
"Muted" status is encountered on a counts dictionary that only has "FAIL" and
"PASS" keys. This will test the potential KeyError path in the
accumulate_group_status function and ensure proper coverage of the Muted-less
counts scenario.
🪄 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: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 7914b117-e6b0-49a9-afd0-a170ac496ba9
📒 Files selected for processing (54)
prowler/lib/check/compliance_config_eval.pyprowler/lib/check/compliance_models.pyprowler/lib/outputs/compliance/asd_essential_eight/asd_essential_eight.pyprowler/lib/outputs/compliance/asd_essential_eight/asd_essential_eight_aws.pyprowler/lib/outputs/compliance/aws_well_architected/aws_well_architected.pyprowler/lib/outputs/compliance/c5/c5.pyprowler/lib/outputs/compliance/c5/c5_aws.pyprowler/lib/outputs/compliance/c5/c5_azure.pyprowler/lib/outputs/compliance/c5/c5_gcp.pyprowler/lib/outputs/compliance/ccc/ccc.pyprowler/lib/outputs/compliance/ccc/ccc_aws.pyprowler/lib/outputs/compliance/ccc/ccc_azure.pyprowler/lib/outputs/compliance/ccc/ccc_gcp.pyprowler/lib/outputs/compliance/cis/cis.pyprowler/lib/outputs/compliance/cis/cis_alibabacloud.pyprowler/lib/outputs/compliance/cis/cis_aws.pyprowler/lib/outputs/compliance/cis/cis_azure.pyprowler/lib/outputs/compliance/cis/cis_gcp.pyprowler/lib/outputs/compliance/cis/cis_github.pyprowler/lib/outputs/compliance/cis/cis_googleworkspace.pyprowler/lib/outputs/compliance/cis/cis_kubernetes.pyprowler/lib/outputs/compliance/cis/cis_m365.pyprowler/lib/outputs/compliance/cis/cis_oraclecloud.pyprowler/lib/outputs/compliance/cisa_scuba/cisa_scuba_googleworkspace.pyprowler/lib/outputs/compliance/ens/ens.pyprowler/lib/outputs/compliance/ens/ens_aws.pyprowler/lib/outputs/compliance/ens/ens_azure.pyprowler/lib/outputs/compliance/ens/ens_gcp.pyprowler/lib/outputs/compliance/generic/generic.pyprowler/lib/outputs/compliance/generic/generic_table.pyprowler/lib/outputs/compliance/iso27001/iso27001_aws.pyprowler/lib/outputs/compliance/iso27001/iso27001_azure.pyprowler/lib/outputs/compliance/iso27001/iso27001_gcp.pyprowler/lib/outputs/compliance/iso27001/iso27001_kubernetes.pyprowler/lib/outputs/compliance/iso27001/iso27001_m365.pyprowler/lib/outputs/compliance/iso27001/iso27001_nhn.pyprowler/lib/outputs/compliance/kisa_ismsp/kisa_ismsp.pyprowler/lib/outputs/compliance/kisa_ismsp/kisa_ismsp_aws.pyprowler/lib/outputs/compliance/mitre_attack/mitre_attack.pyprowler/lib/outputs/compliance/mitre_attack/mitre_attack_aws.pyprowler/lib/outputs/compliance/mitre_attack/mitre_attack_azure.pyprowler/lib/outputs/compliance/mitre_attack/mitre_attack_gcp.pyprowler/lib/outputs/compliance/prowler_threatscore/prowler_threatscore.pyprowler/lib/outputs/compliance/prowler_threatscore/prowler_threatscore_alibaba.pyprowler/lib/outputs/compliance/prowler_threatscore/prowler_threatscore_aws.pyprowler/lib/outputs/compliance/prowler_threatscore/prowler_threatscore_azure.pyprowler/lib/outputs/compliance/prowler_threatscore/prowler_threatscore_gcp.pyprowler/lib/outputs/compliance/prowler_threatscore/prowler_threatscore_kubernetes.pyprowler/lib/outputs/compliance/prowler_threatscore/prowler_threatscore_m365.pyprowler/lib/outputs/compliance/universal/ocsf_compliance.pyprowler/lib/outputs/compliance/universal/universal_table.pytests/lib/check/compliance_config_constraint_model_test.pytests/lib/check/compliance_config_eval_test.pytests/lib/outputs/compliance/universal/universal_table_config_requirements_test.py
💤 Files with no reviewable changes (40)
- prowler/lib/outputs/compliance/asd_essential_eight/asd_essential_eight_aws.py
- prowler/lib/outputs/compliance/c5/c5_azure.py
- prowler/lib/outputs/compliance/iso27001/iso27001_aws.py
- prowler/lib/outputs/compliance/iso27001/iso27001_kubernetes.py
- prowler/lib/outputs/compliance/cis/cis_googleworkspace.py
- prowler/lib/outputs/compliance/ccc/ccc_azure.py
- prowler/lib/outputs/compliance/iso27001/iso27001_nhn.py
- prowler/lib/outputs/compliance/c5/c5_aws.py
- prowler/lib/outputs/compliance/cis/cis_aws.py
- prowler/lib/outputs/compliance/iso27001/iso27001_m365.py
- prowler/lib/outputs/compliance/cis/cis_alibabacloud.py
- prowler/lib/outputs/compliance/ens/ens_aws.py
- prowler/lib/outputs/compliance/prowler_threatscore/prowler_threatscore_alibaba.py
- prowler/lib/outputs/compliance/prowler_threatscore/prowler_threatscore_azure.py
- prowler/lib/outputs/compliance/cis/cis_azure.py
- prowler/lib/outputs/compliance/ccc/ccc_gcp.py
- prowler/lib/outputs/compliance/kisa_ismsp/kisa_ismsp_aws.py
- prowler/lib/outputs/compliance/cis/cis_m365.py
- prowler/lib/outputs/compliance/cisa_scuba/cisa_scuba_googleworkspace.py
- prowler/lib/outputs/compliance/ccc/ccc_aws.py
- prowler/lib/outputs/compliance/prowler_threatscore/prowler_threatscore_gcp.py
- prowler/lib/outputs/compliance/ens/ens_azure.py
- prowler/lib/outputs/compliance/c5/c5_gcp.py
- prowler/lib/outputs/compliance/cis/cis_kubernetes.py
- prowler/lib/outputs/compliance/prowler_threatscore/prowler_threatscore_kubernetes.py
- prowler/lib/outputs/compliance/cis/cis_oraclecloud.py
- prowler/lib/outputs/compliance/prowler_threatscore/prowler_threatscore_m365.py
- prowler/lib/outputs/compliance/mitre_attack/mitre_attack_azure.py
- prowler/lib/outputs/compliance/universal/ocsf_compliance.py
- prowler/lib/outputs/compliance/cis/cis_github.py
- prowler/lib/outputs/compliance/mitre_attack/mitre_attack_aws.py
- prowler/lib/outputs/compliance/iso27001/iso27001_gcp.py
- prowler/lib/outputs/compliance/generic/generic_table.py
- prowler/lib/outputs/compliance/iso27001/iso27001_azure.py
- prowler/lib/outputs/compliance/ens/ens_gcp.py
- prowler/lib/outputs/compliance/mitre_attack/mitre_attack_gcp.py
- prowler/lib/outputs/compliance/prowler_threatscore/prowler_threatscore_aws.py
- prowler/lib/outputs/compliance/cis/cis_gcp.py
- prowler/lib/outputs/compliance/aws_well_architected/aws_well_architected.py
- prowler/lib/outputs/compliance/generic/generic.py
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@prowler/CHANGELOG.md`:
- Line 9: Update the PR number reference in the changelog entry from `#11667` to
`#11669` in the line describing the ConfigRequirements feature (the entry that
mentions "Per-requirement configuration validation for compliance frameworks via
`ConfigRequirements`"). Change both the PR number reference (`#11667`) and the
corresponding GitHub link URL to reflect the correct PR number (`#11669`).
🪄 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: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 3da0244d-2263-46b1-9cea-44c89de697e5
⛔ Files ignored due to path filters (1)
prowler/compliance/azure/hipaa_azure.jsonis excluded by!prowler/compliance/**/*.json
📒 Files selected for processing (5)
docs/developer-guide/security-compliance-framework.mdxprowler/CHANGELOG.mdprowler/lib/check/compliance_config_eval.pyprowler/lib/check/compliance_models.pytests/lib/check/compliance_config_eval_test.py
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@tests/lib/check/compliance_config_requirements_coverage_test.py`:
- Around line 208-211: The f-string prefix on the string literal
"_KEYS_NOT_AFFECTING_VERDICT:\n " is unnecessary because there are no variable
interpolations within that string, which triggers flake8 F541. Remove the f
prefix from this string literal to resolve the linting error, keeping it as a
regular string while the rest of the concatenation with the join operation
remains unchanged.
🪄 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: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: a2767c19-de54-4999-a42d-bf7834fafe5e
⛔ Files ignored due to path filters (57)
prowler/compliance/alibabacloud/cis_2.0_alibabacloud.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/alibabacloud/prowler_threatscore_alibabacloud.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/alibabacloud/secnumcloud_3.2_alibabacloud.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/asd_essential_eight_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/aws_ai_security_framework_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/aws_foundational_security_best_practices_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/aws_foundational_technical_review_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/aws_well_architected_framework_security_pillar_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/c5_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/ccc_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/cisa_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/ens_rd2022_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/fedramp_20x_ksi_low_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/fedramp_low_revision_4_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/fedramp_moderate_revision_4_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/ffiec_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/gdpr_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/gxp_21_cfr_part_11_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/hipaa_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/iso27001_2022_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/kisa_isms_p_2023_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/kisa_isms_p_2023_korean_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/mitre_attack_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/nis2_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/nist_800_171_revision_2_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/nist_800_53_revision_4_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/nist_800_53_revision_5_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/nist_csf_1.1_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/nist_csf_2.0_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/pci_3.2.1_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/pci_4.0_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/prowler_threatscore_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/rbi_cyber_security_framework_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/secnumcloud_3.2_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/aws/soc2_aws.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/azure/c5_azure.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/csa_ccm_4.0.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/dora_2022_2554.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/gcp/c5_gcp.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/gcp/fedramp_20x_ksi_low_gcp.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/gcp/hipaa_gcp.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/gcp/mitre_attack_gcp.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/gcp/nis2_gcp.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/gcp/rbi_cyber_security_framework_gcp.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/gcp/secnumcloud_3.2_gcp.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/gcp/soc2_gcp.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/github/cis_1.0_github.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/kubernetes/cis_1.10_kubernetes.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/kubernetes/cis_1.11_kubernetes.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/kubernetes/cis_1.12_kubernetes.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/kubernetes/cis_1.8_kubernetes.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/kubernetes/iso27001_2022_kubernetes.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/kubernetes/prowler_threatscore_kubernetes.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/m365/cis_4.0_m365.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/m365/cis_6.0_m365.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/m365/prowler_threatscore_m365.jsonis excluded by!prowler/compliance/**/*.jsonprowler/compliance/okta/okta_idaas_stig_v1r2_okta.jsonis excluded by!prowler/compliance/**/*.json
📒 Files selected for processing (3)
tests/lib/check/compliance_config_requirements_coverage_baseline.jsontests/lib/check/compliance_config_requirements_coverage_test.pytests/lib/outputs/compliance/iso27001/iso27001_aws_numeric_config_requirements_test.py
Description
Some compliance requirements only really pass if the checks behind them ran with a strict enough config. Right now you can loosen a check's config and the requirement still shows up as PASS even though it isn't actually met.
CIS AWS 6.0 req 2.11 is a good example ("credentials unused for 45 days or more are disabled"). It maps to
iam_user_accesskey_unused, which readsmax_unused_access_keys_days. Bump that to 120 days and the check happily passes, but the requirement clearly isn't satisfied.So a requirement can now declare what it expects from the config via a new
ConfigRequirementsfield. When we build the compliance outputs we check those expectations against the config the scan actually used (audit_config). If a check ran with a config too loose for the requirement, that requirement is forced to FAIL and we prepend[CONFIG NOT VALID]plus the reason tostatus_extended.Each constraint is
{Check, ConfigKey, Operator, Value}. Operators:lte/gte/eqfor scalars (thresholds, boolean toggles)in–> applied value must be one ofValuesubset–> applied list must be a subset ofValue(allowlists, e.g.recommended_minimal_tls_versions)superset–> applied list must be a superset ofValue(denylists, e.g.insecure_key_algorithms)The actual evaluation lives in one place (
prowler/lib/check/compliance_config_eval.py) so the SDK outputs and the App backend share the same logic. The FAIL override is applied to CSV, OCSF and the console tables.One thing worth calling out: if a
ConfigKeyisn't explicitly set, we skip the constraint and assume the check's default already matches what the requirement wants. So nothing changes for anyone running the default config.On the model side:
ConfigRequirementson the legacyCompliance_Requirementandconfig_requirementsonUniversalComplianceRequirement, wired throughadapt_legacy_to_universal. Then I added the actualConfigRequirementsentries to the relevant requirements across the AWS, Azure, GCP, Alibaba Cloud, Kubernetes, M365 and Okta frameworks, only for requirements whose control text prescribes the concrete value.Steps to review
Please add a detailed description of how to review this PR.
Checklist
Community Checklist
SDK/CLI
UI
API
License
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
Summary by CodeRabbit
[CONFIG NOT VALID]is appended with the first violated constraint reason.