Security News
Weekly Downloads Now Available in npm Package Search Results
Socket's package search now displays weekly downloads for npm packages, helping developers quickly assess popularity and make more informed decisions.
Check CDK v2 applications for best practices using a combination on available rule packs.
The cdk-nag package is a tool for AWS Cloud Development Kit (CDK) that helps enforce best practices and compliance by running checks against CDK constructs. It provides a set of rules that can be applied to CDK applications to ensure they adhere to security, cost, and operational excellence guidelines.
Applying Rule Packs
This feature allows you to apply predefined rule packs, such as AWS Solutions Checks, to your CDK stacks. The code sample demonstrates how to add AWS Solutions Checks to a stack using the Aspects API.
const { AwsSolutionsChecks } = require('cdk-nag');
const { Aspects } = require('aws-cdk-lib');
const app = new cdk.App();
const stack = new cdk.Stack(app, 'MyStack');
Aspects.of(stack).add(new AwsSolutionsChecks());
Custom Rule Creation
This feature allows you to create custom rules to enforce specific compliance requirements. The code sample shows how to define a custom rule by implementing the visit method to check compliance for a specific CDK construct.
const { NagRuleCompliance, NagRuleResult } = require('cdk-nag');
class MyCustomRule {
visit(node) {
if (node instanceof SomeCdkConstruct) {
// Custom logic to check compliance
return NagRuleResult.COMPLIANT;
}
return NagRuleResult.NOT_APPLICABLE;
}
}
Reporting
This feature provides the ability to generate compliance reports in various formats, such as JSON. The code sample demonstrates how to create a report for a stack and output it to a JSON file.
const { NagReportFormat, NagReport } = require('cdk-nag');
const report = new NagReport({
format: NagReportFormat.JSON,
outputPath: 'compliance-report.json'
});
report.generate(stack);
Check CDK applications or CloudFormation templates for best practices using a combination of available rule packs. Inspired by cfn_nag.
Check out this blog post for a guided overview!
See RULES for more information on all the available packs.
RULES also includes a collection of additional rules that are not currently included in any of the pre-built NagPacks, but are still available for inclusion in custom NagPacks.
Read the NagPack developer docs if you are interested in creating your own pack.
For a full list of options See NagPackProps
in the API.md
import { App, Aspects } from 'aws-cdk-lib';
import { CdkTestStack } from '../lib/cdk-test-stack';
import { AwsSolutionsChecks } from 'cdk-nag';
const app = new App();
new CdkTestStack(app, 'CdkNagDemo');
// Simple rule informational messages
Aspects.of(app).add(new AwsSolutionsChecks());
// Additional explanations on the purpose of triggered rules
// Aspects.of(stack).add(new AwsSolutionsChecks({ verbose: true }));
import { SecurityGroup, Vpc, Peer, Port } from 'aws-cdk-lib/aws-ec2';
import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { NagSuppressions } from 'cdk-nag';
export class CdkTestStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
const test = new SecurityGroup(this, 'test', {
vpc: new Vpc(this, 'vpc'),
});
test.addIngressRule(Peer.anyIpv4(), Port.allTraffic());
NagSuppressions.addResourceSuppressions(test, [
{ id: 'AwsSolutions-EC23', reason: 'lorem ipsum' },
]);
}
}
import { SecurityGroup, Vpc, Peer, Port } from 'aws-cdk-lib/aws-ec2';
import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { NagSuppressions } from 'cdk-nag';
export class CdkTestStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
const vpc = new Vpc(this, 'vpc');
const test1 = new SecurityGroup(this, 'test', { vpc });
test1.addIngressRule(Peer.anyIpv4(), Port.allTraffic());
const test2 = new SecurityGroup(this, 'test', { vpc });
test2.addIngressRule(Peer.anyIpv4(), Port.allTraffic());
NagSuppressions.addResourceSuppressions(
[test1, test2],
[{ id: 'AwsSolutions-EC23', reason: 'lorem ipsum' }]
);
}
}
import { User, PolicyStatement } from 'aws-cdk-lib/aws-iam';
import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { NagSuppressions } from 'cdk-nag';
export class CdkTestStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
const user = new User(this, 'rUser');
user.addToPolicy(
new PolicyStatement({
actions: ['s3:PutObject'],
resources: ['arn:aws:s3:::bucket_name/*'],
})
);
// Enable adding suppressions to child constructs
NagSuppressions.addResourceSuppressions(
user,
[
{
id: 'AwsSolutions-IAM5',
reason: 'lorem ipsum',
appliesTo: ['Resource::arn:aws:s3:::bucket_name/*'], // optional
},
],
true
);
}
}
import { App, Aspects } from 'aws-cdk-lib';
import { CdkTestStack } from '../lib/cdk-test-stack';
import { AwsSolutionsChecks, NagSuppressions } from 'cdk-nag';
const app = new App();
const stack = new CdkTestStack(app, 'CdkNagDemo');
Aspects.of(app).add(new AwsSolutionsChecks());
NagSuppressions.addStackSuppressions(stack, [
{ id: 'AwsSolutions-EC23', reason: 'lorem ipsum' },
]);
If you received the following error on synth/deploy
[Error at /StackName/Custom::CDKBucketDeployment8675309/ServiceRole/Resource] AwsSolutions-IAM4: The IAM user, role, or group uses AWS managed policies
import { Bucket } from 'aws-cdk-lib/aws-s3';
import { BucketDeployment } from 'aws-cdk-lib/aws-s3-deployment';
import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { NagSuppressions } from 'cdk-nag';
export class CdkTestStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
new BucketDeployment(this, 'rDeployment', {
sources: [],
destinationBucket: Bucket.fromBucketName(this, 'rBucket', 'foo'),
});
NagSuppressions.addResourceSuppressionsByPath(
this,
'/StackName/Custom::CDKBucketDeployment8675309/ServiceRole/Resource',
[{ id: 'AwsSolutions-IAM4', reason: 'at least 10 characters' }]
);
}
}
Certain rules support granular suppressions of findings
. If you received the following errors on synth/deploy
[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.
[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.
[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.
[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.
By applying the following suppressions
import { User } from 'aws-cdk-lib/aws-iam';
import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { NagSuppressions } from 'cdk-nag';
export class CdkTestStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
const firstUser = new User(this, 'rFirstUser');
firstUser.addToPolicy(
new PolicyStatement({
actions: ['s3:*'],
resources: ['*'],
})
);
const secondUser = new User(this, 'rSecondUser');
secondUser.addToPolicy(
new PolicyStatement({
actions: ['s3:*'],
resources: ['*'],
})
);
const thirdUser = new User(this, 'rSecondUser');
thirdUser.addToPolicy(
new PolicyStatement({
actions: ['sqs:CreateQueue'],
resources: [`arn:aws:sqs:${this.region}:${this.account}:*`],
})
);
NagSuppressions.addResourceSuppressions(
firstUser,
[
{
id: 'AwsSolutions-IAM5',
reason:
"Only suppress AwsSolutions-IAM5 's3:*' finding on First User.",
appliesTo: ['Action::s3:*'],
},
],
true
);
NagSuppressions.addResourceSuppressions(
secondUser,
[
{
id: 'AwsSolutions-IAM5',
reason: 'Suppress all AwsSolutions-IAM5 findings on Second User.',
},
],
true
);
NagSuppressions.addResourceSuppressions(
thirdUser,
[
{
id: 'AwsSolutions-IAM5',
reason: 'Suppress AwsSolutions-IAM5 on the SQS resource.',
appliesTo: [
{
regex: '/^Resource::arn:aws:sqs:(.*):\\*$/g',
},
],
},
],
true
);
}
}
You would see the following error on synth/deploy
[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.
aws-cdk-lib/pipelines
ViolationsThe aws-cdk-lib/pipelines.CodePipeline 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. Because of this behavior, you may experience problems such as rule violations not appearing or the inability to suppress violations on these constructs.
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:
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.
See this issue for more information.
example-app.ts
import { App, Aspects } from 'aws-cdk-lib';
import { AwsSolutionsChecks } from 'cdk-nag';
import { ExamplePipeline } from '../lib/example-pipeline';
const app = new App();
new ExamplePipeline(app, 'example-cdk-pipeline');
Aspects.of(app).add(new AwsSolutionsChecks({ verbose: true }));
app.synth();
example-pipeline.ts
import { Stack, StackProps } from 'aws-cdk-lib';
import { Repository } from 'aws-cdk-lib/aws-codecommit';
import {
CodePipeline,
CodePipelineSource,
ShellStep,
} from 'aws-cdk-lib/pipelines';
import { NagSuppressions } from 'cdk-nag';
import { Construct } from 'constructs';
export class ExamplePipeline extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
const exampleSynth = new ShellStep('ExampleSynth', {
commands: ['yarn build --frozen-lockfile'],
input: CodePipelineSource.codeCommit(
new Repository(this, 'ExampleRepo', { repositoryName: 'ExampleRepo' }),
'main'
),
});
const ExamplePipeline = new CodePipeline(this, 'ExamplePipeline', {
synth: exampleSynth,
});
// Force the pipeline construct creation forward before applying suppressions.
// @See https://github.com/aws/aws-cdk/issues/18440
ExamplePipeline.buildPipeline();
// The path suppression will error if you comment out "ExamplePipeline.buildPipeline();""
NagSuppressions.addResourceSuppressionsByPath(
this,
'/example-cdk-pipeline/ExamplePipeline/Pipeline/ArtifactsBucket/Resource',
[
{
id: 'AwsSolutions-S1',
reason: 'Because I said so',
},
]
);
}
}
In some cases L2 Constructs do not have a native option to remediate an issue and must be fixed via Raw Overrides. 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.
import {
Instance,
InstanceType,
InstanceClass,
MachineImage,
Vpc,
CfnInstance,
} from 'aws-cdk-lib/aws-ec2';
import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { NagSuppressions } from 'cdk-nag';
export class CdkTestStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
const instance = new Instance(this, 'rInstance', {
vpc: new Vpc(this, 'rVpc'),
instanceType: new InstanceType(InstanceClass.T3),
machineImage: MachineImage.latestAmazonLinux(),
});
const cfnIns = instance.node.defaultChild as CfnInstance;
cfnIns.addPropertyOverride('DisableApiTermination', true);
NagSuppressions.addResourceSuppressions(instance, [
{
id: 'AwsSolutions-EC29',
reason: 'Remediated through property override.',
},
]);
}
}
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 for more information on creating your own conditions.
import { App, Aspects } from 'aws-cdk-lib';
import { CdkTestStack } from '../lib/cdk-test-stack';
import { AwsSolutionsChecks, SuppressionIgnoreErrors } from 'cdk-nag';
const app = new App();
new CdkTestStack(app, 'CdkNagDemo');
// Ignore Suppressions on any errors
Aspects.of(app).add(
new AwsSolutionsChecks({
suppressionIgnoreCondition: new SuppressionIgnoreErrors(),
})
);
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.
See the NagLogger developer docs for more information.
import { App, Aspects } from 'aws-cdk-lib';
import { CdkTestStack } from '../lib/cdk-test-stack';
import { ExtremelyHelpfulConsoleLogger } from './docs/NagLogger';
import { AwsSolutionsChecks } from 'cdk-nag';
const app = new App();
new CdkTestStack(app, 'CdkNagDemo');
Aspects.of(app).add(
new AwsSolutionsChecks({
additionalLoggers: [new ExtremelyHelpfulConsoleLogger()],
})
);
You can use cdk-nag on existing CloudFormation templates by using the cloudformation-include module.
Sample CloudFormation template with suppression
{
"Resources": {
"rBucket": {
"Type": "AWS::S3::Bucket",
"Properties": {
"BucketName": "some-bucket-name"
},
"Metadata": {
"cdk_nag": {
"rules_to_suppress": [
{
"id": "AwsSolutions-S1",
"reason": "at least 10 characters"
}
]
}
}
}
}
}
Sample App
import { App, Aspects } from 'aws-cdk-lib';
import { CdkTestStack } from '../lib/cdk-test-stack';
import { AwsSolutionsChecks } from 'cdk-nag';
const app = new App();
new CdkTestStack(app, 'CdkNagDemo');
Aspects.of(app).add(new AwsSolutionsChecks());
Sample Stack with imported template
import { CfnInclude } from 'aws-cdk-lib/cloudformation-include';
import { NagSuppressions } from 'cdk-nag';
import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
export class CdkTestStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
new CfnInclude(this, 'Template', {
templateFile: 'my-template.json',
});
// Add any additional suppressions
NagSuppressions.addResourceSuppressionsByPath(
this,
'/CdkNagDemo/Template/rBucket',
[
{
id: 'AwsSolutions-S2',
reason: 'at least 10 characters',
},
]
);
}
}
Sample CloudFormation template with suppression
{
"Resources": {
"myPolicy": {
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyDocument": {
"Statement": [
{
"Action": [
"kms:Decrypt",
"kms:DescribeKey",
"kms:Encrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*"
],
"Effect": "Allow",
"Resource": ["some-key-arn"]
}
],
"Version": "2012-10-17"
}
},
"Metadata": {
"cdk_nag": {
"rules_to_suppress": [
{
"id": "AwsSolutions-IAM5",
"reason": "Allow key data access",
"applies_to": [
"Action::kms:ReEncrypt*",
"Action::kms:GenerateDataKey*"
]
}
]
}
}
}
}
}
Sample App
import { App, Aspects } from 'aws-cdk-lib';
import { CdkTestStack } from '../lib/cdk-test-stack';
import { AwsSolutionsChecks } from 'cdk-nag';
const app = new App();
new CdkTestStack(app, 'CdkNagDemo');
Aspects.of(app).add(new AwsSolutionsChecks());
Sample Stack with imported template
import { CfnInclude } from 'aws-cdk-lib/cloudformation-include';
import { NagSuppressions } from 'cdk-nag';
import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
export class CdkTestStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
new CfnInclude(this, 'Template', {
templateFile: 'my-template.json',
});
// Add any additional suppressions
NagSuppressions.addResourceSuppressionsByPath(
this,
'/CdkNagDemo/Template/myPolicy',
[
{
id: 'AwsSolutions-IAM5',
reason: 'Allow key data access',
appliesTo: ['Action::kms:ReEncrypt*', 'Action::kms:GenerateDataKey*'],
},
]
);
}
}
See CONTRIBUTING for more information.
This project is licensed under the Apache-2.0 License.
FAQs
Check CDK v2 applications for best practices using a combination on available rule packs.
The npm package cdk-nag receives a total of 103,163 weekly downloads. As such, cdk-nag popularity was classified as popular.
We found that cdk-nag demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 3 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Socket's package search now displays weekly downloads for npm packages, helping developers quickly assess popularity and make more informed decisions.
Security News
A Stanford study reveals 9.5% of engineers contribute almost nothing, costing tech $90B annually, with remote work fueling the rise of "ghost engineers."
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.