You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(core): integrate construct annotations into validation report (#37712)
### Reason for this change
Part of the [Validations RFC](aws/aws-cdk-rfcs#899).
This PR furthers the RFC by integrating construct annotations (warnings and errors added via `Annotations.of()` or `Validations.of()`) into the existing policy validation report. Currently, annotations are only surfaced through the CLI's standard metadata display and are not part of the validation report, meaning users who rely on the report for compliance visibility don't see annotation-based issues alongside plugin violations.
### Description of changes
Integrate construct annotations into the existing validation report pipeline by collecting annotation metadata post-synthesis and converting it into a `NamedValidationPluginReport` with source `"Construct Annotations"`. This is **not** the final unified report format discussed in the RFC — it is an integration of annotations as a "plugin" into the existing report framework and structure.
**`core/lib/private/synthesis.ts`:**
- `collectAnnotationReport()` — walks the full construct tree (including across Stage boundaries via `iterateDfsPreorder`) to collect `aws:cdk:warning` and `aws:cdk:error` metadata entries, converting them to `PolicyViolation` format. Violations are grouped by rule name + severity + description so that multiple constructs triggering the same rule appear as one violation with multiple resources.
- `extractRuleName()` — extracts the rule ID from `[ack: <id>]` tags when available; falls back to generic `aws-cdk:warning` / `aws-cdk:error` for untagged annotations (these cannot be acknowledged, so uniqueness is not required). Includes a coupling note documenting the dependency on the `ackTag()` format in `annotations.ts`.
- `findNearestResource()` — maps a construct to its nearest `CfnResource` for logical ID and template path resolution
- `invokeValidationPlugins()` — calls `collectAnnotationReport()` after plugin execution and merges the result into the `reports[]` array
**`core/lib/validation/private/report.ts`:**
- `formatJson` filter changed from `!rep.success` to `!rep.success || rep.violations.length > 0` so that warning-only reports (which are `success: true`) still render their violations. This is intentional — violations should always be visible regardless of the overall success status. This broadens behavior for all validation sources: a plugin returning `success: true` with violations would now appear in the report when it previously didn't.
- Report headers renamed from "Plugin Report" / "Plugin:" to "Validation Report" / "Source:" to accommodate non-plugin validation sources
**⚠️ User-visible output changes:** The report header and summary table labels have changed (`Plugin Report` → `Validation Report`, `Plugin:` → `Source:`, `Plugin` column → `Source`). CI scripts or tools that parse the text report output may need updating.
**`core/test/validation/validation.test.ts`:**
- 10 new tests covering: annotation warnings in report, annotation errors causing failure, acknowledged warnings excluded, partial acknowledgment via `Validations.of().acknowledge()`, annotations alongside plugins, annotations without plugins, orphan constructs (verifying construct path fallback), `Validations.of().addWarning`, `Validations.of().addError`, and `extractRuleName` regex coupling with `addWarningV2` format
- Updated test helpers to match renamed report headers
#### Sample output
Below is the validation report output showing a plugin (CfnGuardValidator) and construct annotations side-by-side:
```
Performing Policy Validations
Validation Report
-----------------
╔═══════════════════════════════╗
║ Validation Report ║
║ Source: CfnGuardValidator ║
║ Version: N/A ║
║ Status: failure ║
╚═══════════════════════════════╝
(Violations)
S3_BUCKET_VERSIONING_ENABLED (1 occurrences)
Severity: high
Occurrences:
- Construct Path: DemoStack/MyBucket/Resource
- Template Path: <outdir>/DemoStack.template.json
- Creation Stack:
└── DemoStack (DemoStack)
│ Construct: constructs.Construct
└── MyBucket (DemoStack/MyBucket)
└── Resource (DemoStack/MyBucket/Resource)
- Resource ID: MyBucketF68F3FF0
- Template Locations:
> Properties/VersioningConfiguration
Description: S3 Bucket should have versioning enabled
How to fix: Set the "VersioningConfiguration.Status" property to "Enabled"
╔═══════════════════════════════════╗
║ Validation Report ║
║ Source: Construct Annotations ║
║ Version: N/A ║
║ Status: failure ║
╚═══════════════════════════════════╝
(Violations)
@aws-cdk/aws-s3:bucketNotEncrypted (1 occurrences)
Severity: warning
Occurrences:
- Construct Path: DemoStack/MyBucket/Resource
- Template Path: <outdir>/DemoStack.template.json
- Creation Stack:
└── DemoStack (DemoStack)
└── MyBucket (DemoStack/MyBucket)
└── Resource (DemoStack/MyBucket/Resource)
- Resource ID: MyBucketF68F3FF0
- Template Locations:
Description: This bucket does not have default encryption enabled [ack: @aws-cdk/aws-s3:bucketNotEncrypted]
aws-cdk:error (1 occurrences)
Severity: error
Occurrences:
- Construct Path: DemoStack/MyQueue/Resource
- Template Path: <outdir>/DemoStack.template.json
- Creation Stack:
└── DemoStack (DemoStack)
└── MyQueue (DemoStack/MyQueue)
└── Resource (DemoStack/MyQueue/Resource)
- Resource ID: MyQueueE6CA6235
- Template Locations:
Description: Queue does not have a dead letter queue configured
annotation::TopicNoEncryption (1 occurrences)
Severity: warning
Occurrences:
- Construct Path: DemoStack/MyTopic/Resource
- Template Path: <outdir>/DemoStack.template.json
- Creation Stack:
└── DemoStack (DemoStack)
└── MyTopic (DemoStack/MyTopic)
└── Resource (DemoStack/MyTopic/Resource)
- Resource ID: MyTopic86869434
- Template Locations:
Description: SNS Topic is not encrypted at rest [ack: annotation::TopicNoEncryption]
Policy Validation Report Summary
╔═══════════════════════╤═════════╗
║ Source │ Status ║
╟───────────────────────┼─────────╢
║ CfnGuardValidator │ failure ║
╟───────────────────────┼─────────╢
║ Construct Annotations │ failure ║
╚═══════════════════════╧═════════╝
Validation failed. See the validation report above for details
```
### Describe any new or updated permissions being added
N/A
### Description of how you validated changes
- `tsc --noEmit` passes cleanly
- All 36 validation tests pass (26 existing + 10 new)
- Visual report output verified with a demo app exercising all three annotation paths (`Annotations.of().addWarningV2`, `Annotations.of().addError`, `Validations.of().addWarning`)
### Checklist
- [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md)
*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
0 commit comments