@mavogel/awscdk-rootmail 0.0.336 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/.codegraph/.gitignore +5 -0
  2. package/.jsii +460 -8
  3. package/CLAUDE.md +1 -1
  4. package/README.md +6 -1
  5. package/docs/plans/2026-08-09-bump-mvc-projen-cdk-nag-v3.md +199 -0
  6. package/integ-tests/integ.rootmail-custom-santa.ts +2 -2
  7. package/integ-tests/integ.rootmail-filter-subject.ts +2 -2
  8. package/integ-tests/integ.rootmail.ts +2 -2
  9. package/lib/hosted-zone-dkim-propagation.js +46 -15
  10. package/lib/hosted-zone-dkim-verification-records.js +47 -12
  11. package/lib/rootmail-autowire-dns.js +47 -15
  12. package/lib/rootmail.js +13 -16
  13. package/lib/ses-receipt-ruleset-activation.js +53 -15
  14. package/lib/ses-receive.js +12 -12
  15. package/node_modules/cdk-nag/.jsii +6331 -2945
  16. package/node_modules/cdk-nag/.jsii.tabl.json +1 -1
  17. package/node_modules/cdk-nag/API.md +490 -1778
  18. package/node_modules/cdk-nag/MIGRATION.md +239 -0
  19. package/node_modules/cdk-nag/README.md +88 -513
  20. package/node_modules/cdk-nag/lib/index.d.ts +0 -3
  21. package/node_modules/cdk-nag/lib/index.js +1 -4
  22. package/node_modules/cdk-nag/lib/nag-logger.d.ts +5 -145
  23. package/node_modules/cdk-nag/lib/nag-logger.js +4 -199
  24. package/node_modules/cdk-nag/lib/nag-pack.d.ts +57 -44
  25. package/node_modules/cdk-nag/lib/nag-pack.js +140 -114
  26. package/node_modules/cdk-nag/lib/nag-rules.d.ts +0 -2
  27. package/node_modules/cdk-nag/lib/nag-rules.js +3 -5
  28. package/node_modules/cdk-nag/lib/packs/aws-solutions.d.ts +4 -15
  29. package/node_modules/cdk-nag/lib/packs/aws-solutions.js +21 -40
  30. package/node_modules/cdk-nag/lib/packs/hipaa-security.d.ts +4 -2
  31. package/node_modules/cdk-nag/lib/packs/hipaa-security.js +31 -37
  32. package/node_modules/cdk-nag/lib/packs/nist-800-53-r4.d.ts +4 -2
  33. package/node_modules/cdk-nag/lib/packs/nist-800-53-r4.js +29 -35
  34. package/node_modules/cdk-nag/lib/packs/nist-800-53-r5.d.ts +4 -2
  35. package/node_modules/cdk-nag/lib/packs/nist-800-53-r5.js +30 -36
  36. package/node_modules/cdk-nag/lib/packs/pci-dss-321.d.ts +4 -2
  37. package/node_modules/cdk-nag/lib/packs/pci-dss-321.js +29 -35
  38. package/node_modules/cdk-nag/lib/packs/serverless.d.ts +4 -2
  39. package/node_modules/cdk-nag/lib/packs/serverless.js +14 -20
  40. package/node_modules/cdk-nag/lib/rules/ec2/EC2IMDSv2Enabled.js +5 -1
  41. package/node_modules/cdk-nag/package.json +5 -5
  42. package/package.json +16 -17
  43. package/node_modules/cdk-nag/lib/ignore-suppression-conditions.d.ts +0 -59
  44. package/node_modules/cdk-nag/lib/ignore-suppression-conditions.js +0 -97
  45. package/node_modules/cdk-nag/lib/models/nag-suppression.d.ts +0 -30
  46. package/node_modules/cdk-nag/lib/models/nag-suppression.js +0 -7
  47. package/node_modules/cdk-nag/lib/nag-suppressions.d.ts +0 -30
  48. package/node_modules/cdk-nag/lib/nag-suppressions.js +0 -96
  49. package/node_modules/cdk-nag/lib/utils/nag-suppression-helper.d.ts +0 -20
  50. package/node_modules/cdk-nag/lib/utils/nag-suppression-helper.js +0 -119
@@ -41,33 +41,35 @@ For a full list of options See `NagPackProps` in the [API.md](./API.md#struct-na
41
41
  <details>
42
42
  <summary>Including in an application</summary>
43
43
 
44
- ```typescript
45
- import { App, Aspects } from 'aws-cdk-lib';
46
- import { CdkTestStack } from '../lib/cdk-test-stack';
47
- import { AwsSolutionsChecks } from 'cdk-nag';
44
+ ```ts nofixture
45
+ import { App, Validations } from 'aws-cdk-lib';
46
+ import { AwsSolutionsChecks, NIST80053R5Checks } from 'cdk-nag';
47
+
48
+ declare const CdkTestStack: any;
48
49
 
49
50
  const app = new App();
50
51
  new CdkTestStack(app, 'CdkNagDemo');
51
52
  // Simple rule informational messages using the AWS Solutions Rule pack
52
- Aspects.of(app).add(new AwsSolutionsChecks());
53
+ Validations.of(app).addPlugins(new AwsSolutionsChecks(app));
53
54
  // Multiple rule packs can be run against the same app
54
- Aspects.of(app).add(new NIST80053R5Checks());
55
+ Validations.of(app).addPlugins(new NIST80053R5Checks(app));
55
56
  // Additional explanations on the purpose of triggered rules
56
- // Aspects.of(stack).add(new AwsSolutionsChecks({ verbose: true }));
57
+ // Validations.of(app).addPlugins(new AwsSolutionsChecks(app, { verbose: true }));
57
58
  ```
58
59
 
59
60
  </details>
60
61
 
61
- ## Suppressing a Rule
62
+ ## Acknowledging a Rule
63
+
64
+ Use CDK's native `Validations.of()` API to acknowledge (suppress) rule violations on specific constructs.
62
65
 
63
66
  <details>
64
- <summary>Example 1) Default Construct</summary>
67
+ <summary>Example 1) Acknowledging a rule on a construct</summary>
65
68
 
66
- ```typescript
69
+ ```ts nofixture
67
70
  import { SecurityGroup, Vpc, Peer, Port } from 'aws-cdk-lib/aws-ec2';
68
- import { Stack, StackProps } from 'aws-cdk-lib';
71
+ import { Stack, StackProps, Validations } from 'aws-cdk-lib';
69
72
  import { Construct } from 'constructs';
70
- import { NagSuppressions } from 'cdk-nag';
71
73
 
72
74
  export class CdkTestStack extends Stack {
73
75
  constructor(scope: Construct, id: string, props?: StackProps) {
@@ -76,36 +78,10 @@ export class CdkTestStack extends Stack {
76
78
  vpc: new Vpc(this, 'vpc'),
77
79
  });
78
80
  test.addIngressRule(Peer.anyIpv4(), Port.allTraffic());
79
- NagSuppressions.addResourceSuppressions(test, [
80
- { id: 'AwsSolutions-EC23', reason: 'lorem ipsum' },
81
- ]);
82
- }
83
- }
84
- ```
85
-
86
- </details>
87
-
88
- <details>
89
- <summary>Example 2) On Multiple Constructs</summary>
90
-
91
- ```typescript
92
- import { SecurityGroup, Vpc, Peer, Port } from 'aws-cdk-lib/aws-ec2';
93
- import { Stack, StackProps } from 'aws-cdk-lib';
94
- import { Construct } from 'constructs';
95
- import { NagSuppressions } from 'cdk-nag';
96
-
97
- export class CdkTestStack extends Stack {
98
- constructor(scope: Construct, id: string, props?: StackProps) {
99
- super(scope, id, props);
100
- const vpc = new Vpc(this, 'vpc');
101
- const test1 = new SecurityGroup(this, 'test', { vpc });
102
- test1.addIngressRule(Peer.anyIpv4(), Port.allTraffic());
103
- const test2 = new SecurityGroup(this, 'test', { vpc });
104
- test2.addIngressRule(Peer.anyIpv4(), Port.allTraffic());
105
- NagSuppressions.addResourceSuppressions(
106
- [test1, test2],
107
- [{ id: 'AwsSolutions-EC23', reason: 'lorem ipsum' }]
108
- );
81
+ Validations.of(test).acknowledge({
82
+ id: 'AwsSolutions-EC23',
83
+ reason: 'This security group is used for internal testing only.',
84
+ });
109
85
  }
110
86
  }
111
87
  ```
@@ -113,322 +89,59 @@ export class CdkTestStack extends Stack {
113
89
  </details>
114
90
 
115
91
  <details>
116
- <summary>Example 3) Child Constructs</summary>
92
+ <summary>Example 2) Acknowledging a rule on a stack</summary>
117
93
 
118
- ```typescript
119
- import { User, PolicyStatement } from 'aws-cdk-lib/aws-iam';
120
- import { Stack, StackProps } from 'aws-cdk-lib';
121
- import { Construct } from 'constructs';
122
- import { NagSuppressions } from 'cdk-nag';
123
-
124
- export class CdkTestStack extends Stack {
125
- constructor(scope: Construct, id: string, props?: StackProps) {
126
- super(scope, id, props);
127
- const user = new User(this, 'rUser');
128
- user.addToPolicy(
129
- new PolicyStatement({
130
- actions: ['s3:PutObject'],
131
- resources: ['arn:aws:s3:::bucket_name/*'],
132
- })
133
- );
134
- // Enable adding suppressions to child constructs
135
- NagSuppressions.addResourceSuppressions(
136
- user,
137
- [
138
- {
139
- id: 'AwsSolutions-IAM5',
140
- reason: 'lorem ipsum',
141
- appliesTo: ['Resource::arn:aws:s3:::bucket_name/*'], // optional
142
- },
143
- ],
144
- true
145
- );
146
- }
147
- }
148
- ```
149
-
150
- </details>
151
-
152
- <details>
153
- <summary>Example 4) Stack Level </summary>
94
+ ```ts nofixture
95
+ import { App, Validations } from 'aws-cdk-lib';
96
+ import { AwsSolutionsChecks } from 'cdk-nag';
154
97
 
155
- ```typescript
156
- import { App, Aspects } from 'aws-cdk-lib';
157
- import { CdkTestStack } from '../lib/cdk-test-stack';
158
- import { AwsSolutionsChecks, NagSuppressions } from 'cdk-nag';
98
+ declare const CdkTestStack: any;
159
99
 
160
100
  const app = new App();
161
101
  const stack = new CdkTestStack(app, 'CdkNagDemo');
162
- Aspects.of(app).add(new AwsSolutionsChecks());
163
- NagSuppressions.addStackSuppressions(stack, [
164
- { id: 'AwsSolutions-EC23', reason: 'lorem ipsum' },
165
- ]);
102
+ Validations.of(app).addPlugins(new AwsSolutionsChecks(app));
103
+ Validations.of(stack).acknowledge({
104
+ id: 'AwsSolutions-EC23',
105
+ reason: 'All security groups in this stack are internal only.',
106
+ });
166
107
  ```
167
108
 
168
109
  </details>
169
110
 
170
111
  <details>
171
- <summary>Example 5) Construct path</summary>
172
-
173
- If you received the following error on synth/deploy
174
-
175
- ```bash
176
- [Error at /StackName/Custom::CDKBucketDeployment8675309/ServiceRole/Resource] AwsSolutions-IAM4: The IAM user, role, or group uses AWS managed policies
177
- ```
178
-
179
- ```typescript
180
- import { Bucket } from 'aws-cdk-lib/aws-s3';
181
- import { BucketDeployment } from 'aws-cdk-lib/aws-s3-deployment';
182
- import { Stack, StackProps } from 'aws-cdk-lib';
183
- import { Construct } from 'constructs';
184
- import { NagSuppressions } from 'cdk-nag';
185
-
186
- export class CdkTestStack extends Stack {
187
- constructor(scope: Construct, id: string, props?: StackProps) {
188
- super(scope, id, props);
189
- new BucketDeployment(this, 'rDeployment', {
190
- sources: [],
191
- destinationBucket: Bucket.fromBucketName(this, 'rBucket', 'foo'),
192
- });
193
- NagSuppressions.addResourceSuppressionsByPath(
194
- this,
195
- '/StackName/Custom::CDKBucketDeployment8675309/ServiceRole/Resource',
196
- [{ id: 'AwsSolutions-IAM4', reason: 'at least 10 characters' }]
197
- );
198
- }
199
- }
200
- ```
112
+ <summary>Example 3) Acknowledging a specific finding</summary>
201
113
 
202
- </details>
114
+ Certain rules report multiple findings per resource (e.g., IAM wildcard permissions). Each finding has its own ID in the format `RuleId[FindingId]`.
203
115
 
204
- <details>
205
- <summary>Example 6) Granular Suppressions of findings</summary>
206
-
207
- Certain rules support granular suppressions of `findings`. If you received the following errors on synth/deploy
116
+ If you received the following errors on synth/deploy:
208
117
 
209
118
  ```bash
210
- [Error at /StackName/rFirstUser/DefaultPolicy/Resource] AwsSolutions-IAM5[Action::s3:*]: The IAM entity contains wildcard permissions and does not have a cdk-nag rule suppression with evidence for those permission.
211
- [Error at /StackName/rFirstUser/DefaultPolicy/Resource] AwsSolutions-IAM5[Resource::*]: The IAM entity contains wildcard permissions and does not have a cdk-nag rule suppression with evidence for those permission.
212
- [Error at /StackName/rSecondUser/DefaultPolicy/Resource] AwsSolutions-IAM5[Action::s3:*]: The IAM entity contains wildcard permissions and does not have a cdk-nag rule suppression with evidence for those permission.
213
- [Error at /StackName/rSecondUser/DefaultPolicy/Resource] AwsSolutions-IAM5[Resource::*]: The IAM entity contains wildcard permissions and does not have a cdk-nag rule suppression with evidence for those permission.
119
+ [Error at /StackName/rUser/DefaultPolicy/Resource] AwsSolutions-IAM5[Action::s3:*]: The IAM entity contains wildcard permissions.
120
+ [Error at /StackName/rUser/DefaultPolicy/Resource] AwsSolutions-IAM5[Resource::*]: The IAM entity contains wildcard permissions.
214
121
  ```
215
122
 
216
- By applying the following suppressions
123
+ You can acknowledge a specific finding:
217
124
 
218
- ```typescript
219
- import { User } from 'aws-cdk-lib/aws-iam';
220
- import { Stack, StackProps } from 'aws-cdk-lib';
125
+ ```ts nofixture
126
+ import { User, PolicyStatement } from 'aws-cdk-lib/aws-iam';
127
+ import { Stack, StackProps, Validations } from 'aws-cdk-lib';
221
128
  import { Construct } from 'constructs';
222
- import { NagSuppressions } from 'cdk-nag';
223
129
 
224
130
  export class CdkTestStack extends Stack {
225
131
  constructor(scope: Construct, id: string, props?: StackProps) {
226
132
  super(scope, id, props);
227
- const firstUser = new User(this, 'rFirstUser');
228
- firstUser.addToPolicy(
229
- new PolicyStatement({
230
- actions: ['s3:*'],
231
- resources: ['*'],
232
- })
233
- );
234
- const secondUser = new User(this, 'rSecondUser');
235
- secondUser.addToPolicy(
133
+ const user = new User(this, 'rUser');
134
+ user.addToPolicy(
236
135
  new PolicyStatement({
237
136
  actions: ['s3:*'],
238
137
  resources: ['*'],
239
138
  })
240
139
  );
241
- const thirdUser = new User(this, 'rSecondUser');
242
- thirdUser.addToPolicy(
243
- new PolicyStatement({
244
- actions: ['sqs:CreateQueue'],
245
- resources: [`arn:aws:sqs:${this.region}:${this.account}:*`],
246
- })
247
- );
248
- NagSuppressions.addResourceSuppressions(
249
- firstUser,
250
- [
251
- {
252
- id: 'AwsSolutions-IAM5',
253
- reason:
254
- "Only suppress AwsSolutions-IAM5 's3:*' finding on First User.",
255
- appliesTo: ['Action::s3:*'],
256
- },
257
- ],
258
- true
259
- );
260
- NagSuppressions.addResourceSuppressions(
261
- secondUser,
262
- [
263
- {
264
- id: 'AwsSolutions-IAM5',
265
- reason: 'Suppress all AwsSolutions-IAM5 findings on Second User.',
266
- },
267
- ],
268
- true
269
- );
270
- NagSuppressions.addResourceSuppressions(
271
- thirdUser,
272
- [
273
- {
274
- id: 'AwsSolutions-IAM5',
275
- reason: 'Suppress AwsSolutions-IAM5 on the SQS resource.',
276
- appliesTo: [
277
- {
278
- regex: '/^Resource::arn:aws:sqs:(.*):\\*$/g',
279
- },
280
- ],
281
- },
282
- ],
283
- true
284
- );
285
- }
286
- }
287
- ```
288
-
289
- You would see the following error on synth/deploy
290
-
291
- ```bash
292
- [Error at /StackName/rFirstUser/DefaultPolicy/Resource] AwsSolutions-IAM5[Resource::*]: The IAM entity contains wildcard permissions and does not have a cdk-nag rule suppression with evidence for those permission.
293
- ```
294
-
295
- </details>
296
-
297
- ## Suppressing Rule Validation Failures
298
-
299
- When a rule validation fails it is handled similarly to a rule violation, and can be suppressed in the same manner. The `ID` for a rule failure is `CdkNagValidationFailure`.
300
-
301
- If a rule is suppressed in a non-granular manner (i.e. `appliesTo` is not set, see example 1 above) then validation failures on that rule are also suppressed.
302
-
303
- Validation failure suppression respects any applied [Suppression Ignore Conditions](#conditionally-ignoring-suppressions)
304
-
305
- <details>
306
- <summary>Example 1) Suppress all Validation Failures on a Resource</summary>
307
-
308
- ```typescript
309
- import { SecurityGroup, Vpc, Peer, Port } from 'aws-cdk-lib/aws-ec2';
310
- import { Stack, StackProps } from 'aws-cdk-lib';
311
- import { Construct } from 'constructs';
312
- import { NagSuppressions } from 'cdk-nag';
313
-
314
- export class CdkTestStack extends Stack {
315
- constructor(scope: Construct, id: string, props?: StackProps) {
316
- super(scope, id, props);
317
- const test = new SecurityGroup(this, 'test', {
318
- vpc: new Vpc(this, 'vpc'),
140
+ // Only acknowledge the s3:* action — Resource::* still triggers
141
+ Validations.of(user).acknowledge({
142
+ id: 'AwsSolutions-IAM5[Action::s3:*]',
143
+ reason: 'Need s3:* for cross-account replication.',
319
144
  });
320
- test.addIngressRule(Peer.anyIpv4(), Port.allTraffic());
321
- NagSuppressions.addResourceSuppressions(test, [
322
- { id: 'CdkNagValidationFailure', reason: 'lorem ipsum' },
323
- ]);
324
- }
325
- }
326
- ```
327
-
328
- </details>
329
-
330
- <details>
331
- <summary>Example 2) Granular Suppression of Validation Failures</summary>
332
- Validation failures can be suppressed for individual rules by using `appliesTo` to list the desired rules
333
-
334
- ```typescript
335
- import { SecurityGroup, Vpc, Peer, Port } from 'aws-cdk-lib/aws-ec2';
336
- import { Stack, StackProps } from 'aws-cdk-lib';
337
- import { Construct } from 'constructs';
338
- import { NagSuppressions } from 'cdk-nag';
339
-
340
- export class CdkTestStack extends Stack {
341
- constructor(scope: Construct, id: string, props?: StackProps) {
342
- super(scope, id, props);
343
- const test = new SecurityGroup(this, 'test', {
344
- vpc: new Vpc(this, 'vpc'),
345
- });
346
- test.addIngressRule(Peer.anyIpv4(), Port.allTraffic());
347
- NagSuppressions.addResourceSuppressions(test, [
348
- {
349
- id: 'CdkNagValidationFailure',
350
- reason: 'lorem ipsum',
351
- appliesTo: ['AwsSolutions-L1'],
352
- },
353
- ]);
354
- }
355
- }
356
- ```
357
-
358
- </details>
359
-
360
- ## Suppressing `aws-cdk-lib/pipelines` Violations
361
-
362
- The [aws-cdk-lib/pipelines.CodePipeline](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.pipelines.CodePipeline.html) construct and its child constructs are not guaranteed to be "Visited" by `Aspects`, as they are not added during the "Construction" phase of the [cdk lifecycle](https://docs.aws.amazon.com/cdk/v2/guide/apps.html#lifecycle). Because of this behavior, you may experience problems such as rule violations not appearing or the inability to suppress violations on these constructs.
363
-
364
- You can remediate these rule violation and suppression problems by forcing the pipeline construct creation forward by calling `.buildPipeline()` on your `CodePipeline` object. Otherwise you may see errors such as:
365
-
366
- ```
367
- Error: Suppression path "/this/construct/path" did not match any resource. This can occur when a resource does not exist or if a suppression is applied before a resource is created.
368
- ```
369
-
370
- See [this issue](https://github.com/aws/aws-cdk/issues/18440) for more information.
371
-
372
- <details>
373
- <summary>Example) Suppressing Violations in Pipelines</summary>
374
-
375
- `example-app.ts`
376
-
377
- ```ts
378
- import { App, Aspects } from 'aws-cdk-lib';
379
- import { AwsSolutionsChecks } from 'cdk-nag';
380
- import { ExamplePipeline } from '../lib/example-pipeline';
381
-
382
- const app = new App();
383
- new ExamplePipeline(app, 'example-cdk-pipeline');
384
- Aspects.of(app).add(new AwsSolutionsChecks({ verbose: true }));
385
- app.synth();
386
- ```
387
-
388
- `example-pipeline.ts`
389
-
390
- ```ts
391
- import { Stack, StackProps } from 'aws-cdk-lib';
392
- import { Repository } from 'aws-cdk-lib/aws-codecommit';
393
- import {
394
- CodePipeline,
395
- CodePipelineSource,
396
- ShellStep,
397
- } from 'aws-cdk-lib/pipelines';
398
- import { NagSuppressions } from 'cdk-nag';
399
- import { Construct } from 'constructs';
400
-
401
- export class ExamplePipeline extends Stack {
402
- constructor(scope: Construct, id: string, props?: StackProps) {
403
- super(scope, id, props);
404
-
405
- const exampleSynth = new ShellStep('ExampleSynth', {
406
- commands: ['yarn build --frozen-lockfile'],
407
- input: CodePipelineSource.codeCommit(
408
- new Repository(this, 'ExampleRepo', { repositoryName: 'ExampleRepo' }),
409
- 'main'
410
- ),
411
- });
412
-
413
- const ExamplePipeline = new CodePipeline(this, 'ExamplePipeline', {
414
- synth: exampleSynth,
415
- });
416
-
417
- // Force the pipeline construct creation forward before applying suppressions.
418
- // @See https://github.com/aws/aws-cdk/issues/18440
419
- ExamplePipeline.buildPipeline();
420
-
421
- // The path suppression will error if you comment out "ExamplePipeline.buildPipeline();""
422
- NagSuppressions.addResourceSuppressionsByPath(
423
- this,
424
- '/example-cdk-pipeline/ExamplePipeline/Pipeline/ArtifactsBucket/Resource',
425
- [
426
- {
427
- id: 'AwsSolutions-S1',
428
- reason: 'Because I said so',
429
- },
430
- ]
431
- );
432
145
  }
433
146
  }
434
147
  ```
@@ -437,12 +150,12 @@ export class ExamplePipeline extends Stack {
437
150
 
438
151
  ## Rules and Property Overrides
439
152
 
440
- In some cases L2 Constructs do not have a native option to remediate an issue and must be fixed via [Raw Overrides](https://docs.aws.amazon.com/cdk/latest/guide/cfn_layer.html#cfn_layer_raw). Since raw overrides take place after template synthesis these fixes are not caught by cdk-nag. In this case you should remediate the issue and suppress the issue like in the following example.
153
+ In some cases L2 Constructs do not have a native option to remediate an issue and must be fixed via [Raw Overrides](https://docs.aws.amazon.com/cdk/latest/guide/cfn_layer.html#cfn_layer_raw). Since raw overrides take place after template synthesis these fixes are not caught by cdk-nag. In this case you should remediate the issue and acknowledge the rule.
441
154
 
442
155
  <details>
443
156
  <summary>Example) Property Overrides</summary>
444
157
 
445
- ```ts
158
+ ```ts nofixture
446
159
  import {
447
160
  Instance,
448
161
  InstanceType,
@@ -451,9 +164,8 @@ import {
451
164
  Vpc,
452
165
  CfnInstance,
453
166
  } from 'aws-cdk-lib/aws-ec2';
454
- import { Stack, StackProps } from 'aws-cdk-lib';
167
+ import { Stack, StackProps, Validations } from 'aws-cdk-lib';
455
168
  import { Construct } from 'constructs';
456
- import { NagSuppressions } from 'cdk-nag';
457
169
 
458
170
  export class CdkTestStack extends Stack {
459
171
  constructor(scope: Construct, id: string, props?: StackProps) {
@@ -465,233 +177,96 @@ export class CdkTestStack extends Stack {
465
177
  });
466
178
  const cfnIns = instance.node.defaultChild as CfnInstance;
467
179
  cfnIns.addPropertyOverride('DisableApiTermination', true);
468
- NagSuppressions.addResourceSuppressions(instance, [
469
- {
470
- id: 'AwsSolutions-EC29',
471
- reason: 'Remediated through property override.',
472
- },
473
- ]);
180
+ Validations.of(instance).acknowledge({
181
+ id: 'AwsSolutions-EC29',
182
+ reason: 'Remediated through property override.',
183
+ });
474
184
  }
475
185
  }
476
186
  ```
477
187
 
478
188
  </details>
479
189
 
480
- ## Conditionally Ignoring Suppressions
481
-
482
- You can optionally create a condition that prevents certain rules from being suppressed. You can create conditions for any variety of reasons. Examples include a condition that always ignores a suppression, a condition that ignores a suppression based on the date, a condition that ignores a suppression based on the reason. You can read [the developer docs](./docs/IgnoreSuppressionConditions.md) for more information on creating your own conditions.
483
-
484
- <details>
485
- <summary>Example) Using the pre-built `SuppressionIgnoreErrors` class to ignore suppressions on any `Error` level rules.</summary>
486
-
487
- ```ts
488
- import { App, Aspects } from 'aws-cdk-lib';
489
- import { CdkTestStack } from '../lib/cdk-test-stack';
490
- import { AwsSolutionsChecks, SuppressionIgnoreErrors } from 'cdk-nag';
491
-
492
- const app = new App();
493
- new CdkTestStack(app, 'CdkNagDemo');
494
- // Ignore Suppressions on any errors
495
- Aspects.of(app).add(
496
- new AwsSolutionsChecks({
497
- suppressionIgnoreCondition: new SuppressionIgnoreErrors(),
498
- })
499
- );
500
- ```
501
-
502
- </details>
503
-
504
- ## Customizing Logging
190
+ ## Audit Trail: CloudFormation Metadata
505
191
 
506
- `NagLogger`s give `NagPack` authors and users the ability to create their own custom reporting mechanisms. All pre-built `NagPacks`come with the `AnnotationsLogger`and the `NagReportLogger` (with CSV reports) enabled by default.
192
+ By default, cdk-nag writes violations to CDK's `policy-validation-report.json` in the cloud assembly. If you need the v2-compatible `cdk_nag` metadata block in your synthesized CloudFormation templates (for existing compliance tooling), enable `writeSuppressionsToCloudFormation`:
507
193
 
508
- See the [NagLogger](./docs/NagLogger.md) developer docs for more information.
509
-
510
- <details>
511
- <summary>Example) Adding the `ExtremelyHelpfulConsoleLogger` example from the NagLogger docs</summary>
512
-
513
- ```ts
514
- import { App, Aspects } from 'aws-cdk-lib';
515
- import { CdkTestStack } from '../lib/cdk-test-stack';
516
- import { ExtremelyHelpfulConsoleLogger } from './docs/NagLogger';
194
+ ```ts nofixture
195
+ import { App, Validations } from 'aws-cdk-lib';
517
196
  import { AwsSolutionsChecks } from 'cdk-nag';
518
197
 
519
198
  const app = new App();
520
- new CdkTestStack(app, 'CdkNagDemo');
521
- Aspects.of(app).add(
522
- new AwsSolutionsChecks({
523
- additionalLoggers: [new ExtremelyHelpfulConsoleLogger()],
524
- })
525
- );
199
+ // Writes acknowledged rules into CfnResource Metadata as cdk_nag: { rules_to_suppress: [...] }
200
+ Validations.of(app).addPlugins(new AwsSolutionsChecks(app, { writeSuppressionsToCloudFormation: true }));
526
201
  ```
527
202
 
528
- </details>
203
+ This registers a `WriteNagSuppressionsToCloudFormationAspect` that runs during synthesis and copies `Validations.of().acknowledge()` data into the CloudFormation template Metadata section, preserving the same format as cdk-nag v2.
529
204
 
530
205
  ## Using on CloudFormation templates
531
206
 
532
207
  You can use cdk-nag on existing CloudFormation templates by using the [cloudformation-include](https://docs.aws.amazon.com/cdk/latest/guide/use-cfn-template.html#use-cfn-template-import) module.
533
208
 
534
209
  <details>
535
- <summary>Example 1) CloudFormation template with suppression</summary>
536
-
537
- Sample CloudFormation template with suppression
538
-
539
- ```json
540
- {
541
- "Resources": {
542
- "rBucket": {
543
- "Type": "AWS::S3::Bucket",
544
- "Properties": {
545
- "BucketName": "some-bucket-name"
546
- },
547
- "Metadata": {
548
- "cdk_nag": {
549
- "rules_to_suppress": [
550
- {
551
- "id": "AwsSolutions-S1",
552
- "reason": "at least 10 characters"
553
- }
554
- ]
555
- }
556
- }
557
- }
558
- }
559
- }
560
- ```
210
+ <summary>Example) CloudFormation template</summary>
561
211
 
562
212
  Sample App
563
213
 
564
- ```typescript
565
- import { App, Aspects } from 'aws-cdk-lib';
566
- import { CdkTestStack } from '../lib/cdk-test-stack';
214
+ ```ts nofixture
215
+ import { App, Validations } from 'aws-cdk-lib';
567
216
  import { AwsSolutionsChecks } from 'cdk-nag';
568
217
 
218
+ declare const CdkTestStack: any;
219
+
569
220
  const app = new App();
570
221
  new CdkTestStack(app, 'CdkNagDemo');
571
- Aspects.of(app).add(new AwsSolutionsChecks());
222
+ Validations.of(app).addPlugins(new AwsSolutionsChecks(app));
572
223
  ```
573
224
 
574
225
  Sample Stack with imported template
575
226
 
576
- ```typescript
227
+ ```ts nofixture
577
228
  import { CfnInclude } from 'aws-cdk-lib/cloudformation-include';
578
- import { NagSuppressions } from 'cdk-nag';
579
- import { Stack, StackProps } from 'aws-cdk-lib';
229
+ import { Stack, StackProps, Validations } from 'aws-cdk-lib';
580
230
  import { Construct } from 'constructs';
581
231
 
582
232
  export class CdkTestStack extends Stack {
583
233
  constructor(scope: Construct, id: string, props?: StackProps) {
584
234
  super(scope, id, props);
585
- new CfnInclude(this, 'Template', {
235
+ const template = new CfnInclude(this, 'Template', {
586
236
  templateFile: 'my-template.json',
587
237
  });
588
- // Add any additional suppressions
589
- NagSuppressions.addResourceSuppressionsByPath(
590
- this,
591
- '/CdkNagDemo/Template/rBucket',
592
- [
593
- {
594
- id: 'AwsSolutions-S2',
595
- reason: 'at least 10 characters',
596
- },
597
- ]
598
- );
238
+ // Acknowledge rules on imported resources
239
+ const bucket = template.getResource('rBucket');
240
+ Validations.of(bucket).acknowledge({
241
+ id: 'AwsSolutions-S1',
242
+ reason: 'Logging not required for this bucket.',
243
+ });
599
244
  }
600
245
  }
601
246
  ```
602
247
 
603
248
  </details>
604
249
 
605
- <details>
606
- <summary>Example 2) CloudFormation template with granular suppressions</summary>
607
-
608
- Sample CloudFormation template with suppression
609
-
610
- ```json
611
- {
612
- "Resources": {
613
- "myPolicy": {
614
- "Type": "AWS::IAM::Policy",
615
- "Properties": {
616
- "PolicyDocument": {
617
- "Statement": [
618
- {
619
- "Action": [
620
- "kms:Decrypt",
621
- "kms:DescribeKey",
622
- "kms:Encrypt",
623
- "kms:ReEncrypt*",
624
- "kms:GenerateDataKey*"
625
- ],
626
- "Effect": "Allow",
627
- "Resource": ["some-key-arn"]
628
- }
629
- ],
630
- "Version": "2012-10-17"
631
- }
632
- },
633
- "Metadata": {
634
- "cdk_nag": {
635
- "rules_to_suppress": [
636
- {
637
- "id": "AwsSolutions-IAM5",
638
- "reason": "Allow key data access",
639
- "applies_to": [
640
- "Action::kms:ReEncrypt*",
641
- "Action::kms:GenerateDataKey*"
642
- ]
643
- }
644
- ]
645
- }
646
- }
647
- }
648
- }
649
- }
650
- ```
651
-
652
- Sample App
653
-
654
- ```typescript
655
- import { App, Aspects } from 'aws-cdk-lib';
656
- import { CdkTestStack } from '../lib/cdk-test-stack';
657
- import { AwsSolutionsChecks } from 'cdk-nag';
658
-
659
- const app = new App();
660
- new CdkTestStack(app, 'CdkNagDemo');
661
- Aspects.of(app).add(new AwsSolutionsChecks());
662
- ```
250
+ ## Migrating from v2
663
251
 
664
- Sample Stack with imported template
252
+ cdk-nag v3 replaces the custom `NagSuppressions` API with CDK's native `Validations.of().acknowledge()` mechanism.
665
253
 
666
- ```typescript
667
- import { CfnInclude } from 'aws-cdk-lib/cloudformation-include';
668
- import { NagSuppressions } from 'cdk-nag';
669
- import { Stack, StackProps } from 'aws-cdk-lib';
670
- import { Construct } from 'constructs';
254
+ | v2 | v3 |
255
+ |---|---|
256
+ | `NagSuppressions.addResourceSuppressions(construct, [{ id, reason }])` | `Validations.of(construct).acknowledge({ id, reason })` |
257
+ | `NagSuppressions.addStackSuppressions(stack, [{ id, reason }])` | `Validations.of(stack).acknowledge({ id, reason })` |
258
+ | `NagSuppressions.addResourceSuppressionsByPath(stack, path, [...])` | `Validations.of(construct).acknowledge({ id, reason })` |
259
+ | `appliesTo: ['Action::s3:*']` | `id: 'AwsSolutions-IAM5[Action::s3:*]'` |
260
+ | `{ id: 'CdkNagValidationFailure', reason: '...' }` | `Validations.of(construct).acknowledge({ id: 'RuleId', reason: '...' })` |
671
261
 
672
- export class CdkTestStack extends Stack {
673
- constructor(scope: Construct, id: string, props?: StackProps) {
674
- super(scope, id, props);
675
- new CfnInclude(this, 'Template', {
676
- templateFile: 'my-template.json',
677
- });
678
- // Add any additional suppressions
679
- NagSuppressions.addResourceSuppressionsByPath(
680
- this,
681
- '/CdkNagDemo/Template/myPolicy',
682
- [
683
- {
684
- id: 'AwsSolutions-IAM5',
685
- reason: 'Allow key data access',
686
- appliesTo: ['Action::kms:ReEncrypt*', 'Action::kms:GenerateDataKey*'],
687
- },
688
- ]
689
- );
690
- }
691
- }
692
- ```
262
+ **Note on bulk suppression:** In v2, suppressing a rule without `appliesTo` would suppress all findings for that rule on the construct. In v3, each finding must be acknowledged individually (e.g., `AwsSolutions-IAM5[Action::s3:*]` and `AwsSolutions-IAM5[Resource::*]` are separate acknowledgments). Prefix matching (acknowledging `AwsSolutions-IAM5` to suppress all findings) is not yet supported — tracked via [issue link].
693
263
 
694
- </details>
264
+ **Removed APIs:**
265
+ - `NagSuppressions` (use `Validations.of().acknowledge()`)
266
+ - `INagSuppressionIgnore` and all condition classes
267
+ - `NagPackSuppression` interface
268
+ - `CdkNagValidationFailure` concept
269
+ - `logIgnores` and `suppressionIgnoreCondition` props
695
270
 
696
271
  ## Contributing
697
272