Skip to content

Surface volatile-excluded rules as time-bounded exceptions in report output#3419

Open
arewm wants to merge 2 commits into
conforma:mainfrom
arewm:exception-report-output
Open

Surface volatile-excluded rules as time-bounded exceptions in report output#3419
arewm wants to merge 2 commits into
conforma:mainfrom
arewm:exception-report-output

Conversation

@arewm

@arewm arewm commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds Exceptions []evaluator.Result \json:"exceptions,omitempty"`to theInputstruct ininternal/input/report.go`, so volatile-excluded rules appear in the JSON report rather than silently disappearing
  • Preserves effective_until in keepSomeMetadataSingle alongside the existing effective_on and term keys
  • Changes the evaluator to surface rules excluded by VolatileConfig as Outcome.Exceptions entries with effective_on and effective_until timestamps stamped from the VolatileCriteria CRD fields
  • Fixes totalRules accounting in both base_evaluator.go and conftest_evaluator.go to count exceptions, preventing a spurious "no rules checked" error for all-excluded policies
  • Covers both the OPA (IncludeExcludePolicyResolver) and conftest evaluator paths

Motivation

Downstream tooling — in particular SVR (Simple Verification Result) attestation generators — needs to represent time-bounded policy exceptions rather than treat excluded rules as if they were never evaluated. Previously, a rule excluded via VolatileConfig disappeared entirely from the output: it appeared in neither violations, warnings, nor successes. Consumers had no way to distinguish "this rule passed" from "this rule was temporarily waived."

With this change, excluded rules surface in exceptions with effective_on and effective_until metadata, enabling consumers to apply the SVR time-bounded property pattern to represent waivers that expire automatically.

Test plan

  • Unit tests: go test -tags=unit ./internal/evaluator/... ./internal/output/... ./internal/input/... (348 pass)
  • Integration tests: go test -tags=integration ./internal/evaluator/... (36 pass, including 5 new subtests covering volatile exclusion surfacing, metadata stamping, and component-scoped exclusions with effective_on/effective_until assertions)
  • Existing TestConftestEvaluatorIncludeExclude updated to reflect the new behavior (excluded results now appear in Exceptions rather than being dropped)

🤖 Generated with Claude Code

Exception results were computed but not exposed to the report due to
missing exceptions field in Input struct. Preserve effective_until
metadata so downstream tooling can consume time-bounded expiry.

Assisted-by: Claude Code (Haiku 4.5)
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@arewm, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 55 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Enterprise

Run ID: e0d9a78b-ce5a-4c31-92f7-46636d26fbf0

📥 Commits

Reviewing files that changed from the base of the PR and between 6aff090 and 353c0b9.

📒 Files selected for processing (10)
  • internal/evaluator/DESIGN.md
  • internal/evaluator/base_evaluator.go
  • internal/evaluator/conftest_evaluator.go
  • internal/evaluator/conftest_evaluator_unit_filtering_test.go
  • internal/evaluator/criteria.go
  • internal/evaluator/criteria_test.go
  • internal/evaluator/filters.go
  • internal/evaluator/filters_test.go
  • internal/evaluator/opa_evaluator_integration_test.go
  • internal/input/report.go
📝 Walkthrough

Walkthrough

Configuration exclusions now surface as exception results with optional effective-time metadata. Exceptions are aggregated by evaluators and outputs, serialized in reports, and populated by validation and server report-building paths.

Changes

Configuration exclusion exception flow

Layer / File(s) Summary
Preserve exclusion metadata
internal/evaluator/criteria.go, internal/evaluator/criteria_test.go
Volatile exclusion criteria store and retrieve effectiveOn and effectiveUntil metadata by raw criterion value.
Return excluded rules as exceptions
internal/evaluator/filters.go, internal/evaluator/filters_test.go, internal/evaluator/opa_evaluator_integration_test.go, internal/evaluator/DESIGN.md
Post-evaluation filters return excluded rules as exceptions, select matching exclude patterns, and stamp available effective-time metadata.
Accumulate evaluator exception outcomes
internal/evaluator/base_evaluator.go, internal/evaluator/conftest_evaluator.go, internal/evaluator/conftest_evaluator_unit_filtering_test.go
Evaluator flows append configuration exceptions and track exception-only outcomes separately from rule totals.
Expose exceptions in reports
internal/output/output.go, internal/output/output_test.go, internal/input/report.go, internal/input/report_test.go, internal/server/handler.go, cmd/validate/input.go
Exception results are aggregated, metadata is retained, JSON reports serialize exceptions, and validation report inputs are populated.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Validator
  participant Evaluator
  participant UnifiedPostEvaluationFilter
  participant Output
  participant Report
  Validator->>Evaluator: validate input
  Evaluator->>UnifiedPostEvaluationFilter: filter policy results
  UnifiedPostEvaluationFilter-->>Evaluator: filtered results and config exceptions
  Evaluator-->>Output: store outcome exceptions
  Output-->>Report: aggregate exceptions
  Report-->>Validator: serialize exceptions
Loading

Suggested reviewers: joejstuart

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.79% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: volatile-excluded rules are surfaced as time-bounded exceptions in reports.
Description check ✅ Passed The description accurately summarizes exception surfacing, metadata handling, accounting changes, and test coverage.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@fullsend-ai-review

fullsend-ai-review Bot commented Jul 16, 2026

Copy link
Copy Markdown

🤖 Review · ❌ Terminated · Started 4:44 PM UTC · Ended 5:01 PM UTC
Commit: 87c4a29 · View workflow run →

@qodo-for-conforma

Copy link
Copy Markdown

Code Review by Qodo

Grey Divider

Sorry, something went wrong

We weren't able to complete the code review on our side. Please try again

Grey Divider

Qodo Logo

@qodo-for-conforma

Copy link
Copy Markdown

PR Summary by Qodo

Report volatile-excluded rules as time-bounded exceptions

✨ Enhancement 🐞 Bug fix 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• Surface config/volatile excluded rules as exceptions with effective_on/effective_until.
• Emit exceptions in JSON reports and in CLI/server report construction.
• Fix total rule counting for all-excluded policies and add coverage for both evaluators.
Diagram

graph TD
  A["Volatile/Config Exclude"] --> B["Criteria meta map"] --> D["Unified filter"] --> E["Outcome.Exceptions"] --> F["Output aggregator"] --> G["JSON report output"]
  C["OPA/Conftest eval"] --> D
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Model exclusions as Skipped (not Exceptions)
  • ➕ Keeps 'exceptions' reserved for explicit waivers/attestations rather than config behavior
  • ➕ May align better with some consumers’ semantics (not evaluated vs waived)
  • ➖ Loses the ability to represent time-bounded waivers cleanly using existing 'exception' semantics
  • ➖ Requires downstream changes to treat skipped as waivers and carry the same metadata anyway
2. Add a dedicated 'excluded' section distinct from Exceptions
  • ➕ Makes the provenance explicit: excluded-by-config vs waived/exception
  • ➕ Avoids overloading Exceptions for multiple concepts
  • ➖ New output surface area and schema changes for all consumers
  • ➖ Would still need time-bounded metadata plumbing; larger migration cost
3. Generate exception results directly at policy-resolution stage (before filtering results)
  • ➕ Cleaner separation: resolution emits explicit excluded entries without re-scoring in filter fallback
  • ➕ Avoids duplicating matching logic for legacy/non-EC paths
  • ➖ Requires broader refactor of resolver/filter responsibilities and data structures
  • ➖ Harder to preserve existing behavior for results without metadata codes

Recommendation: Current approach is reasonable: keep exclusion decisions in the unified post-evaluation filter (where include/exclude already lives) and surface excluded rules as Outcome.Exceptions, with volatile time bounds stamped via criteria-pattern metadata. This minimizes schema churn (reuses Exceptions) while enabling SVR-style time-bounded waivers; the added MatchingExcludeCriteria mapping is the key enabling detail.

Files changed (15) +673 / -34

Enhancement (7) +140 / -25
input.goInclude exceptions in validate-input report payload +1/-0

Include exceptions in validate-input report payload

• Populates input.Exceptions from Output so CLI JSON/report generation exposes excluded-as-exception rules. Keeps existing warning/success gating behavior intact.

cmd/validate/input.go

conftest_evaluator.goSurface config-excluded exceptions and preserve effective_until metadata +7/-5

Surface config-excluded exceptions and preserve effective_until metadata

• Adds metadataEffectiveUntil constant and threads configExceptions returned by the unified filter into Outcome.Exceptions. Updates totalRules counting to include exceptions and adjusts computeSuccesses call sites for new FilterResults signature.

internal/evaluator/conftest_evaluator.go

criteria.goPersist volatile criteria time metadata alongside exclude values +38/-4

Persist volatile criteria time metadata alongside exclude values

• Introduces criteriaItemMeta and a meta map keyed by raw criteria value. Volatile criteria collection now stores effective_on/effective_until for later stamping onto exception results, including component-scoped criteria via storeMeta.

internal/evaluator/criteria.go

filters.goReturn config-excluded results as exceptions and stamp time bounds +80/-15

Return config-excluded results as exceptions and stamp time bounds

• Extends PostEvaluationFilter.FilterResults to also return config-excluded exception results. Tracks which exclude pattern matched a rule (MatchingExcludeCriteria / bestMatchingPattern) and stamps effective_on/effective_until from volatile criteria metadata onto excluded results before emitting them as exceptions.

internal/evaluator/filters.go

report.goAdd exceptions to per-file JSON report schema +1/-0

Add exceptions to per-file JSON report schema

• Extends input.Input with Exceptions []evaluator.Result (omitempty) so reports can represent waived/excluded rules explicitly. Keeps existing violations/warnings/successes fields unchanged.

internal/input/report.go

output.goPreserve effective_until and expose Output.Exceptions aggregator +12/-1

Preserve effective_until and expose Output.Exceptions aggregator

• Updates metadata scrubbing to retain effective_until alongside code/effective_on/term for non-detailed output. Adds Output.Exceptions() to aggregate and sort exceptions across outcomes for callers building reports.

internal/output/output.go

handler.goInclude exceptions in server-generated reports +1/-0

Include exceptions in server-generated reports

• Populates inp.Exceptions from Output in the server evaluation flow, aligning HTTP report JSON with CLI behavior. Leaves warning/success gating logic intact.

internal/server/handler.go

Bug fix (1) +4 / -3
base_evaluator.goCarry config-excluded exceptions and fix totalRules accounting +4/-3

Carry config-excluded exceptions and fix totalRules accounting

• Updates post-processing to accept configExceptions from FilterResults and append them to Outcome.Exceptions. Fixes totalRules counting to include exceptions, preventing false 'no rules checked' when everything is excluded.

internal/evaluator/base_evaluator.go

Tests (6) +513 / -6
conftest_evaluator_unit_filtering_test.goUpdate conftest include/exclude expectations to use Exceptions +5/-2

Update conftest include/exclude expectations to use Exceptions

• Adjusts existing unit test so excluded results are asserted in Exceptions rather than disappearing. Validates both excluded rules are present in the exception output.

internal/evaluator/conftest_evaluator_unit_filtering_test.go

criteria_test.goAdd unit tests for criteria metadata storage and retrieval +60/-0

Add unit tests for criteria metadata storage and retrieval

• Adds coverage for addItemWithMeta/storeMeta/getMeta behavior, including zero-value behavior for non-volatile criteria. Ensures metadata is only stored when time fields are present.

internal/evaluator/criteria_test.go

filters_test.goAdd tests for volatile/permanent exclusion exceptions and metadata stamping +127/-4

Add tests for volatile/permanent exclusion exceptions and metadata stamping

• Updates call sites for the expanded FilterResults signature and adds focused tests verifying excluded results surface as exceptions. Asserts correct presence/absence of effective_on/effective_until for volatile vs permanent config exclusions.

internal/evaluator/filters_test.go

opa_evaluator_integration_test.goIntegration coverage: volatile exclusions surface as exceptions with timestamps +160/-0

Integration coverage: volatile exclusions surface as exceptions with timestamps

• Enhances existing component-scoped exclusion test to assert excluded rules appear in Outcome.Exceptions with stamped time metadata. Adds a new integration test suite covering exact-code and package-level volatile exclusions plus permanent exclusions.

internal/evaluator/opa_evaluator_integration_test.go

report_test.goVerify JSON report output includes exceptions and time metadata +73/-0

Verify JSON report output includes exceptions and time metadata

• Adds a report JSON test ensuring exceptions are serialized under each filepath with effective_on/effective_until preserved in metadata. Confirms overall report structure remains stable.

internal/input/report_test.go

output_test.goTest effective_until preservation and exception aggregation/sorting +88/-0

Test effective_until preservation and exception aggregation/sorting

• Adds coverage ensuring keepSomeMetadataSingle retains effective_until. Introduces Output.Exceptions() tests across empty/single/multi-outcome scenarios and verifies sorting by code.

internal/output/output_test.go

Documentation (1) +16 / -0
DESIGN.mdDocument config/volatile exclusions surfacing as exceptions +16/-0

Document config/volatile exclusions surfacing as exceptions

• Adds design notes explaining that excluded rules are emitted as Outcome.Exceptions and how volatile EffectiveOn/EffectiveUntil are stamped. Clarifies metadata lookup is keyed by raw criteria patterns, not resolved rule IDs.

internal/evaluator/DESIGN.md

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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 `@internal/evaluator/criteria.go`:
- Around line 41-46: Change Criteria.meta from value-based keys to full scoped
criterion identities, preserving separate metadata for static versus volatile
entries and for each component. Update the criterion construction and
policy-resolution paths that read or write meta so this identity is carried
through consistently. Add collision tests covering static plus volatile criteria
and the same value across multiple components, ensuring reporting retains each
entry’s correct expiry.
🪄 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: CHILL

Plan: Enterprise

Run ID: f2fb179e-b19f-4d96-80e2-0ae628a8ebf1

📥 Commits

Reviewing files that changed from the base of the PR and between 7b62cf5 and 485532a.

📒 Files selected for processing (15)
  • cmd/validate/input.go
  • internal/evaluator/DESIGN.md
  • internal/evaluator/base_evaluator.go
  • internal/evaluator/conftest_evaluator.go
  • internal/evaluator/conftest_evaluator_unit_filtering_test.go
  • internal/evaluator/criteria.go
  • internal/evaluator/criteria_test.go
  • internal/evaluator/filters.go
  • internal/evaluator/filters_test.go
  • internal/evaluator/opa_evaluator_integration_test.go
  • internal/input/report.go
  • internal/input/report_test.go
  • internal/output/output.go
  • internal/output/output_test.go
  • internal/server/handler.go

Comment on lines +41 to +46
// meta maps criteria value strings to their time metadata (volatile config only).
type Criteria struct {
digestItems map[string][]string
componentItems map[string][]string
defaultItems []string
meta map[string]criteriaItemMeta

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Preserve metadata per scoped criterion, not per value.

c.meta[value] = m overwrites metadata when the same value is reused—for example, a permanent Config.Exclude{"pkg.rule"} plus a volatile exclusion, or component-specific entries with different expiries. Downstream reporting may then claim the wrong expiry, including that a permanent exclusion expires.

Key metadata by full criterion identity/scope and carry that identity through policy resolution. Add collision tests for static+volatile and multi-component entries.

As per path instructions, “Focus on major issues impacting performance, readability, maintainability and security. Avoid nitpicks and avoid verbosity.”

Also applies to: 90-95, 229-243

🤖 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 `@internal/evaluator/criteria.go` around lines 41 - 46, Change Criteria.meta
from value-based keys to full scoped criterion identities, preserving separate
metadata for static versus volatile entries and for each component. Update the
criterion construction and policy-resolution paths that read or write meta so
this identity is carried through consistently. Add collision tests covering
static plus volatile criteria and the same value across multiple components,
ensuring reporting retains each entry’s correct expiry.

Source: Path instructions

@codecov

codecov Bot commented Jul 16, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.91667% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/evaluator/conftest_evaluator.go 75.00% 2 Missing ⚠️
Flag Coverage Δ
acceptance 54.31% <68.75%> (+0.04%) ⬆️
generative 16.70% <0.00%> (-0.11%) ⬇️
integration 28.26% <67.70%> (+0.28%) ⬆️
unit 71.89% <91.66%> (+0.14%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
cmd/validate/input.go 95.39% <100.00%> (+0.01%) ⬆️
internal/evaluator/base_evaluator.go 76.37% <100.00%> (-0.36%) ⬇️
internal/evaluator/criteria.go 97.27% <100.00%> (+0.39%) ⬆️
internal/evaluator/filters.go 84.26% <100.00%> (+1.63%) ⬆️
internal/input/report.go 88.34% <ø> (ø)
internal/output/output.go 98.36% <100.00%> (+0.05%) ⬆️
internal/server/handler.go 83.47% <100.00%> (+0.13%) ⬆️
internal/evaluator/conftest_evaluator.go 88.22% <75.00%> (-0.25%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@fullsend-ai-review

fullsend-ai-review Bot commented Jul 16, 2026

Copy link
Copy Markdown

Review — approve

Summary: This PR surfaces config-excluded rules (both Config.Exclude and VolatileConfig.Exclude) as exception entries in the evaluator output rather than silently dropping them. Volatile exclusions carry effective_on / effective_until timestamps for downstream SVR attestation generators. The implementation is well-structured, touching both the OPA and conftest evaluator paths consistently, with comprehensive test coverage across unit, filter, and integration layers.

Strengths

  • Correct dual-path implementation: Both IncludeExcludePolicyResolver (legacy/OPA) and ECPolicyResolver (conftest with pipeline intentions) paths correctly surface config exceptions with metadata stamping. The bestMatchingPattern function correctly identifies which criteria pattern triggered the exclusion to retrieve the right time metadata.
  • Safe behavioral change: The totalRules == 0 && allExceptions > 0 handling correctly warns instead of erroring when all rules are excluded by config, while preserving the fail-closed error for genuinely missing inputs.
  • Good metadata hygiene: keepSomeMetadataSingle is updated to preserve effective_until alongside existing preserved keys. omitempty on the Exceptions field in Input maintains backward compatibility — existing consumers without exceptions see no change.
  • Thorough testing: 5 new integration subtests, unit tests for criteriaItemMeta storage/retrieval, filter-level tests covering volatile vs. permanent exclusions, and report serialization tests.

Findings

[low] Duplicated metadata stamping block (internal/evaluator/filters.go)
The metadata copy-and-stamp logic (lines ~1072–1086 and ~1118–1131 in the modified file) is repeated verbatim in the ECPolicyResolver path and the legacy fallback path. Consider extracting a small helper like stampExceptionMeta(result Result, meta criteriaItemMeta) Result to reduce duplication and make future changes less error-prone.

[low] Potential exception double-counting edge case (internal/evaluator/filters.go:1139)
CategorizeResults unconditionally re-appends originalResult.Exceptions (line 1139). If a Rego-level exception's code also matches a config exclusion pattern, that result would appear in both the configExceptions return from FilterResults AND the originalResult.Exceptions appended by CategorizeResults, causing it to appear twice in the final result.Exceptions. This is a pre-existing pattern (the line is unchanged), but the new configExceptions append in the callers (base_evaluator.go:321, conftest_evaluator.go:668) widens the surface. The overlap scenario (Rego exception + config exclusion on the same rule code) is unlikely in practice but worth a defensive guard in follow-up.

[low] Inconsistent tab alignment in metadata constants (internal/evaluator/conftest_evaluator.go:179–183)
The new metadataEffectiveUntil and metadataSolution entries use wider alignment than the surrounding metadataTerm and metadataTitle constants, creating visual inconsistency. Minor, but the gofmt-canonical formatting would use consistent alignment across the entire const block.

Previous run

Review — PR #3419

Surface volatile-excluded rules as time-bounded exceptions in report output

Summary

This PR adds a significant new capability: rules excluded by VolatileConfig.Exclude (and Config.Exclude) now surface as Exceptions in evaluation output with effective_on/effective_until metadata from the CRD. The implementation touches the filtering pipeline (FilterResults signature change), criteria metadata tracking, report output, and both evaluator paths (OPA and conftest).

The approach is architecturally sound and the test coverage is thorough. However, there is one high-severity finding related to a safety invariant change, and several medium-severity issues that should be addressed before merging.


Findings

🔴 HIGH: totalRules safety invariant weakened — fail-closed becomes fail-open

Files: internal/evaluator/base_evaluator.go:323, internal/evaluator/conftest_evaluator.go:671

The totalRules == 0 guard is a fail-closed safety invariant. It ensures evaluation errors out when no policy rules were actually evaluated, catching misconfiguration and broken inputs. By counting exceptions toward totalRules, a scenario becomes possible where ALL rules are excluded via VolatileConfig.Exclude, producing:

  • 0 warnings, 0 failures, 0 successes, N exceptions
  • totalRules > 0 → guard passes
  • success: true (since len(violations) == 0)

For a supply chain security verification tool, this converts a fail-closed behavior to fail-open. CI/CD pipelines that check exit codes and success fields would proceed with unverified artifacts. The error message ("no successes, warnings, or failures") also becomes inaccurate since exceptions aren't mentioned.

Remediation: Do not count exceptions in totalRules. Exceptions represent rules that were not evaluated. Add a separate diagnostic if totalRules == 0 && len(allExceptions) > 0 — e.g., a warning "all rules were excluded by config, no actual policy checks performed." Alternatively, keep the error but include exception context.


🟡 MEDIUM: Rego exception duplication for config-excluded rules

Files: internal/evaluator/base_evaluator.go:318-319, internal/evaluator/conftest_evaluator.go:665-666, internal/evaluator/filters.go:1139

CategorizeResults at line 1139 unconditionally appends originalResult.Exceptions... to the exceptions output ("Add exceptions and skipped as-is"). When a Rego-level exception is also config-excluded:

  1. CategorizeResults adds it via line 1139
  2. FilterResults returns it in configExceptions
  3. result.Exceptions = append(result.Exceptions, configExceptions...) adds it again

The result is duplicated exception entries. While the Rego-exception + config-exclude combination is uncommon, the duplication path is real.

Remediation: Either deduplicate when appending configExceptions, or skip codes already present in exceptions from CategorizeResults.


🟡 MEDIUM: No linked issue for non-trivial change

Category: missing-authorization

This PR adds 673 lines across 15 files, introducing a new behavioral concept (exception surfacing with time-bounded metadata). The project's conventions (AGENTS.md: "Conventional commits with Jira key encouraged") indicate non-trivial changes should trace to authorized work. No issue is linked. The motivation is well-explained in the PR body, but traceability to a tracked work item is missing.

Remediation: Create a tracking issue describing the motivation, design, and impact on downstream consumers (SVR attestation generators). Link it to this PR.


🟡 MEDIUM: Metadata stamping code duplicated across filter branches

File: internal/evaluator/filters.go

The metadata copy-and-stamp logic (copy existing metadata map, conditionally stamp effective_on/effective_until) is duplicated near-identically in the ECPolicyResolver branch and the legacy fallback branch of UnifiedPostEvaluationFilter.FilterResults. The codebase uses base* helper extraction patterns elsewhere (e.g., basePolicyResolver.baseEvaluateRuleInclusion).

Remediation: Extract a helper function, e.g.:

func stampExceptionMeta(result Result, meta criteriaItemMeta) Result {
    if meta.effectiveOn == "" && meta.effectiveUntil == "" {
        return result
    }
    newMeta := make(map[string]interface{}, len(result.Metadata))
    for k, v := range result.Metadata {
        newMeta[k] = v
    }
    if meta.effectiveOn != "" {
        newMeta[metadataEffectiveOn] = meta.effectiveOn
    }
    if meta.effectiveUntil != "" {
        newMeta[metadataEffectiveUntil] = meta.effectiveUntil
    }
    result.Metadata = newMeta
    return result
}

🟢 LOW: storeMeta silently overwrites on duplicate criteria values

File: internal/evaluator/criteria.go

The meta map is keyed by raw criteria value (c.Value). If two VolatileCriteria entries share the same value but have different EffectiveOn/EffectiveUntil windows (both active at the current effective time), storeMeta silently overwrites. The surviving metadata depends on slice iteration order.

Remediation: Document as a known limitation, or adopt a "most restrictive window" merge strategy.


🟢 LOW: Text report and summary formats omit exceptions

Files: internal/input/templates/, internal/applicationsnapshot/templates/

The text report templates render violations, warnings, and successes but not exceptions. Users relying on text output will not see exception data. Additionally, applicationsnapshot.Component struct has Violations, Warnings, Successes but no Exceptions field — the two report formats diverge.

Remediation: Either add exception rendering to text templates and an Exceptions field to applicationsnapshot.Component, or document that exceptions are JSON/YAML only.


🟢 LOW: Double assignment pattern

Files: internal/evaluator/base_evaluator.go:318-319, internal/evaluator/conftest_evaluator.go:665-666

result.Exceptions = exceptions followed immediately by result.Exceptions = append(result.Exceptions, configExceptions...) is a double write. The codebase uses single-assignment for these fields elsewhere.

Remediation: Combine: result.Exceptions = append(exceptions, configExceptions...)


🟢 LOW: Constant alignment inconsistency

File: internal/evaluator/conftest_evaluator.go:176-183

The PR re-aligns metadataEffectiveOn, metadataEffectiveUntil, and metadataSolution but not the surrounding constants (metadataTerm, metadataTitle, metadataDependsOn, etc.), creating inconsistent column alignment within the const block.

Remediation: Re-align all constants in the block to the new longest identifier, or keep the original alignment.


What looks good

  • Comprehensive test coverage: unit tests for criteriaItemMeta, bestMatchingPattern, and configExceptions in FilterResults; integration tests covering exact-code, package-level, and component-scoped volatile exclusions; report JSON serialization test
  • The DESIGN.md addition clearly explains the metadata keying strategy
  • keepSomeMetadataSingle correctly preserves effective_until alongside existing keys
  • Output.Exceptions() follows the established Warnings() pattern (value receiver, make with cap 10, sortResults)
  • The omitempty tag on Input.Exceptions makes the JSON schema change additive and backward-compatible
  • All Go type changes are in internal/ packages, so no external API breakage

Labels: PR modifies evaluator filtering pipeline and report output format

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review

fullsend-ai-review Bot commented Jul 16, 2026

Copy link
Copy Markdown

🤖 Review · ⚠️ Cancelled · Started 6:16 PM UTC · Ended 6:20 PM UTC
Commit: 87c4a29 · View workflow run →

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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 `@internal/evaluator/filters_test.go`:
- Line 1922: Run the repository’s configured formatter, targeting
internal/evaluator/filters_test.go and correcting the formatting issue around
line 1922, then include the formatter-generated changes in the commit.
🪄 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: CHILL

Plan: Enterprise

Run ID: 55bff216-ad8b-48e1-ac55-2865c6f4153b

📥 Commits

Reviewing files that changed from the base of the PR and between 485532a and 6aff090.

📒 Files selected for processing (5)
  • internal/evaluator/base_evaluator.go
  • internal/evaluator/conftest_evaluator.go
  • internal/evaluator/filters.go
  • internal/evaluator/filters_test.go
  • internal/input/report.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • internal/input/report.go
  • internal/evaluator/filters.go

assert.NotContains(t, exceptions[0].Metadata, metadataEffectiveUntil)
})
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix the formatting failure before merge.

gci reports internal/evaluator/filters_test.go as improperly formatted at Line 1922. Run the repository formatter and commit the resulting change.

🧰 Tools
🪛 golangci-lint (2.12.2)

[error] 1922-1922: File is not properly formatted

(gci)

🤖 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 `@internal/evaluator/filters_test.go` at line 1922, Run the repository’s
configured formatter, targeting internal/evaluator/filters_test.go and
correcting the formatting issue around line 1922, then include the
formatter-generated changes in the commit.

Source: Linters/SAST tools

Expose all rule exceptions (config and Rego sources) with
effective_on/effective_until timestamps so downstream tooling can
represent time-bounded policy waivers instead of silently dropping
volatile exclusions. Consumers now see the complete policy picture.
The totalRules invariant is preserved by excluding exceptions from the
evaluated count and using a separate config-exclusion counter to detect
misconfiguration.

Assisted-by: Claude Code (Sonnet 4.6)
@arewm
arewm force-pushed the exception-report-output branch from 6aff090 to 353c0b9 Compare July 16, 2026 18:19
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 16, 2026

Copy link
Copy Markdown

🤖 Review · ❌ Terminated · Started 6:21 PM UTC · Ended 6:28 PM UTC
Commit: 87c4a29 · View workflow run →

@fullsend-ai-review
fullsend-ai-review Bot dismissed their stale review July 16, 2026 18:28

Superseded by updated review

configExceptions = append(configExceptions, result)
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[low] correctness

CategorizeResults unconditionally appends originalResult.Exceptions. Combined with the new configExceptions append in callers, a Rego-level exception whose code also matches a config exclusion would appear twice in result.Exceptions. The overlap scenario is unlikely but possible.

Suggested fix: In follow-up, either skip originalResult.Exceptions entries whose codes already appear in configExceptions, or filter them in CategorizeResults when config exceptions are present.

metadataSeverity = "severity"
metadataEffectiveOn = "effective_on"
metadataSolution = "solution"
metadataEffectiveOn = "effective_on"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[low] style

The new metadataEffectiveUntil and metadataSolution entries use wider tab alignment than metadataTerm and metadataTitle, creating visual inconsistency in the const block.

Suggested fix: Align all entries in the const block to the same tab stop.

@fullsend-ai-review fullsend-ai-review Bot added the ready-for-merge All reviewers approved — ready to merge label Jul 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request ready-for-merge All reviewers approved — ready to merge Review effort 4/5 size: XXL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant