Skip to content

feat(core): integrate construct annotations into validation report#37712

Merged
mergify[bot] merged 40 commits intomainfrom
conroy/annotations-in-validation-report
May 5, 2026
Merged

feat(core): integrate construct annotations into validation report#37712
mergify[bot] merged 40 commits intomainfrom
conroy/annotations-in-validation-report

Conversation

@kaizencc
Copy link
Copy Markdown
Contributor

@kaizencc kaizencc commented Apr 28, 2026

Reason for this change

Part of the Validations RFC.

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 ReportValidation 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

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license

kaizencc and others added 25 commits April 15, 2026 21:58
The merge from main duplicated full-body deprecated interfaces
(from #37613) alongside the extends-based aliases that this PR
uses. Restored report.ts and validation.ts to their correct
pre-merge state.
The merge from main kept both the full-body deprecated Beta1
interfaces (from #37613) and the extends-based aliases from
this branch, causing duplicate identifiers. Fixed by keeping
the #37613 full-body approach with correct Beta1 names and
removing the extends-based aliases.
- Remove KNOWN_PREFIXES array; use colon detection in ensurePrefix
  instead of maintaining a hardcoded list of known prefixes. Any
  'prefix:id' format is treated as already-prefixed; only bare IDs
  get the 'annotation:' prefix added.
- Replace findLast (ES2023) with filter + last element for ES2020
  compatibility.
- Revert unrelated Location string test changes (scope creep).
@kaizencc kaizencc requested a review from a team as a code owner April 28, 2026 00:49
@github-actions github-actions Bot added the p2 label Apr 28, 2026
@aws-cdk-automation aws-cdk-automation requested a review from a team April 28, 2026 00:49
@aws-cdk-automation
Copy link
Copy Markdown
Collaborator

➡️ PR build request submitted to test-main-pipeline ⬅️

A maintainer must now check the pipeline and add the pr-linter/cli-integ-tested label once the pipeline succeeds.

@mergify
Copy link
Copy Markdown
Contributor

mergify Bot commented May 5, 2026

Thank you for contributing! Your pull request will be updated from main and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork).

@mergify
Copy link
Copy Markdown
Contributor

mergify Bot commented May 5, 2026

Merge Queue Status

  • Entered queue2026-05-05 02:45 UTC · Rule: default-squash
  • Checks passed · in-place
  • Merged2026-05-05 03:14 UTC · at 89546651b793ea9fd960eb7d1c8225fc23a6b411 · squash

This pull request spent 29 minutes 27 seconds in the queue, including 29 minutes 11 seconds running CI.

Required conditions to merge

@mergify mergify Bot temporarily deployed to automation May 5, 2026 02:45 Inactive
@mergify mergify Bot temporarily deployed to automation May 5, 2026 02:45 Inactive
@mergify mergify Bot requested a deployment to test-pipeline May 5, 2026 02:46 Waiting
@mergify
Copy link
Copy Markdown
Contributor

mergify Bot commented May 5, 2026

Thank you for contributing! Your pull request will be updated from main and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork).

@mergify mergify Bot merged commit 438bd0b into main May 5, 2026
16 of 18 checks passed
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 5, 2026

Comments on closed issues and PRs are hard for our team to see.
If you need help, please open a new issue that references this one.

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

Labels

contribution/core This is a PR that came from AWS. p2 pr-linter/exempt-integ-test The PR linter will not require integ test changes pr-linter/exempt-readme The PR linter will not require README changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants