eslint-plugin-awscdk
TypeScript icon, indicating that this package has built-in type declarations

0.0.65 • Public • Published

ESLint Plugin for AWS CDK


cdk: Experimental

This module is experimental and under active development. Features are subject to non-backward compatible changes or removal in any future version. These are not subject to the Semantic Versioning model and breaking changes will be announced in the release notes. This means that while you may use them, you may need to update your source code when upgrading to a newer version of this package.


How to use

Install

npm install --save-dev eslint-plugin-awscdk

Configure

Using ESLint configs

Using projen

import { AwsCdkTypeScriptApp } from 'projen';
const project = new AwsCdkTypeScriptApp({
  ...

  devDeps: [
    'eslint-plugin-awscdk',
  ],

  ...
});
project.eslint?.config.plugins.push('awscdk');
project.eslint?.config.extends.push('plugin:awscdk/all'); // or 'awscdk/aws-foundational' etc

without projen update eslintrc.json

{
	"plugins": [
		"awscdk"
	],
	"extends": [
		"plugin:awscdk/all"
	]
}

Using ESLint rules

using projen

import { AwsCdkTypeScriptApp } from 'projen';
const project = new AwsCdkTypeScriptApp({
  ...

  devDeps: [
    'eslint-plugin-awscdk',
  ],

  ...
});
project.eslint?.config.plugins.push('awscdk');

// add individual rules
project.eslint?.addRules({
  'awscdk/require-bucket-encryption': [
    2, // can set severity (0 = info, 1 = warning, 2 = error)
  ],
});

without projen update eslintrc.json

{
	"plugins": [
		"awscdk"
	],
	"rules": {
		"awscdk/require-bucket-encryption": [
			2
		]
	}
}

Rules

AWS Foundational Security Best Practices

How to enable

new apigateway.Stage(this, 'Stage', {
  loggingLevel: apigateway.MethodLoggingLevel.ERROR, // or INFO
  ...
});

// or

new apigateway.RestApi(this, 'API', {
  deployOptions: {
    loggingLevel: apigateway.MethodLoggingLevel.ERROR, // or INFO
  },
  ...
})

How to enable

// if autoScalingGroupName is provided then it should use ELB health checks
new autoscaling.AutoScalingGroup(this, 'ASG', {
  autoScalingGroupName: 'my-asg',
  healthCheck: autoscaling.HealthCheck.elb(),
});

How to enable

new Distribution(this, 'Distribution', {
  defaultBehavior: {
    ...
  },
  defaultRootObject: 'index.html',
})

How to enable

new Distribution(this, 'Distribution', {
  defaultBehavior: {
    viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
    ...
  },
})

// or

new Distribution(this, 'Distribution', {
  defaultBehavior: {
    viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.HTTPS_ONLY,
    ...
  },
})

How to enable

new Distribution(this, 'Distribution', {
  defaultBehavior: {
    origin: new origins.OriginGroup(...),
  }
})

Currently no L2 construct

How to enable

// either set billingMode to PAY_PER_REQUEST

new Table(this, 'Table', {
  billingMode: BillingMode.PAY_PER_REQUEST,
});

new Table(this, 'Table', {
  replicationRegions: ['us-east-1', 'us-east-2'], // if replication regions is specified then billingMode is set to PAY_PER_REQUEST
})

// or if billingMode is PROVISIONED, then enable autoscaling

const table = new Table(this, 'Table', {
  billingMode: BillingMode.PROVISIONED, // this is also the default
});

table.autoScaleReadCapacity();
table.autoScaleWriteCapacity();

How to enable

new Table(this, 'Table', {
  pointInTimeRecovery: true,
})

Currently no L2 construct

How to enable

new ec2.Volume(this, 'Volume', {
  encrypted: true,
});
new efs.FileSystem(this, 'FileSystem', {
  encrypted: true,
});

How to enable

const alb = new ApplicationLoadBalancer(this, 'ALB', {
  ...
})

alb.setAttribute('routing.http.drop_invalid_header_fields.enabled', true);

How to enable

const alb = new ApplicationLoadBalancer(this, 'ALB', {
  ...
})

alb.logAccessLogs(bucket);

How to enable

new ApplicationLoadBalancer(this, 'ALB', {
  deletionProtection: true,
  ...
})

How to enable

const alb = new elbv2.ApplicationLoadBalancer(this, 'ALB');

alb.addListener('1', {
  protocol: elbv2.ApplicationProtocol.HTTP, // applies to listeners with HTTP protocol
  port: 80, //  if protocol is not provided then will default to http if port is 80 | 8080 | 8000 | 8008
  defaultAction: elbv2.ListenerAction.redirect({
    protocol: elbv2.ApplicationProtocol.HTTPS,
    ...
  }),
});

// or

new elbv2.ApplicationListener(this, 'Listener', {
  protocol: elbv2.ApplicationProtocol.HTTP, // applies to listeners with HTTP protocol
  port: 80, //  if protocol is not provided then will default to http if port is 80 | 8080 | 8000 | 8008
  defaultAction: elbv2.ListenerAction.redirect({
    protocol: elbv2.ApplicationProtocol.HTTPS,
    ...
  }),
})

Currently no L2 construct

How to enable

new elasticsearch.Domain(this, 'Domain', {
  encryptionAtRest: {
    enabled: true,
  }
});

How to enable

new elasticsearch.Domain(this, 'Domain', {
  vpc,
});

How to enable

new elasticsearch.Domain(this, 'Domain', {
  nodeToNodeEncryption: true,
});
new iam.PolicyStatement({
  effect: iam.Effect.ALLOW,
  resources: ['*'],
  actions: ['*'],
});

Will fail rule

const user = new iam.User(this, 'User', {
  managedPolicies: [...] // will fail if this is provided.
});

user.addManagedPolicy();
user.addToPolicy();
user.addToPrincipalPolicy();
user.attachInlinePolicy();

Will fail rule

import * as iam from '@aws-cdk/aws-iam';
new iam.PolicyStatement({
  effect: iam.Effect.ALLOW,
  resources: ['*'],
  actions: ['kms:Decrypt', 'kms:ReEncryptFrom'],
});

Will fail rule

import * as iam from '@aws-cdk/aws-iam';
new iam.PolicyStatement({
  effect: iam.Effect.ALLOW,
  resources: ['*'],
  actions: ['kms:Decrypt', 'kms:ReEncryptFrom'],
});

Will fail rule

new kms.Key(this, 'Key', {
  removalPolicy: cdk.RemovalPolicy.DESTROY,
});
// any policy with `iam.AnyPrincipal() can't be attached to a Lambda function
const policy = new iam.PolicyStatment({
  principals: [ 
    new iam.AnyPrincipal() // equals {"AWS": "*"}
  ],
  ...
});

// Lots of ways to attach this permission to a lambda function...

// used in `initialPolicy`
const handler = new lambda.Function(this, 'Handler', {
  initialPolicy: [policy],
  ...
});

// or
handler.grantPrincipal.addToPrincipalPolicy(policy);

//or
handler.role?.addToPrincipalPolicy(policy);

//or
handler.role?.attachInlinePolicy(policy);

//or
handler.role?.grant(new iam.AnyPrincipal(), [...]);

//or
handler.role?.grantPassRole(new iam.AnyPrincipal());

//or
handler.role?.grantPrincipal.addToPrincipalPolicy(policy);

//or
handler.addPermission('permission', {
  principal: new iam.AnyPrincipal(),
  ...
});

//or
handler.addToRolePolicy(policy);

//or
handler.grantInvoke(new iam.AnyPrincipal());

How to enable

new lambda.Function(this, 'Handler', {
  runtime: Runtime.PYTHON_3_8, 
});

Will check for these runtimes:

new rds.DatabaseInstance(this, 'Instance', {
  ...
  publiclyAccessible: true,
});

default value of 'publiclyAccessible' is determined by the subnet type

new rds.DatabaseInstance(this, 'Instance', {
  ...
  vpcSubnets: {
    subnetType: ec2.SubnetType.PUBLIC,
  }
});
new rds.DatabaseInstance(this, 'Instance', {
  ...
  storageEncrypted: false,
})

How to enable

new rds.DatabaseInstance(this, 'DB', {
  multiAz: true,
  ...
});

How to enable

new rds.DatabaseInstance(this, 'Instance', {
  ...
  enablePerformanceInsights: true,
});

// or
new rds.DatabaseInstance(this, 'Instance', {
  ...
  enablePerformanceInsights: true,
});

or

new rds.DatabaseInstance(this, 'Instance', {
  ...
  performanceInsightsEncryptionKey: key,
})

or

new rds.DatabaseInstance(this, 'Instance', {
  ...
  performanceInsightsRetention: rds.PerformanceInsightsRetention.DEFAULT,
})
new rds.DatabaseCluster(this, 'Instance', {
  ...
  deletionProtection: false,
})

How to enable

new rds.DatabaseCluster(this, 'Instance', {
  ...
  deletionProtection: true,
})

or

new rds.DatabaseCluster(this, 'Instance', {
  ...
  removalPolicy: cdk.RemovalPolicy.RETAIN,
})
new rds.DatabaseInstance(this, 'Instance', {
  ...
  deletionProtection: false,
})

How to enable

new rds.DatabaseInstance(this, 'Instance', {
  ...
  deletionProtection: true,
})

or

new rds.DatabaseInstance(this, 'Instance', {
  ...
  removalPolicy: cdk.RemovalPolicy.RETAIN,
})

How to enable also applies to XXFromSnapshot & XXReadReplica

// log exports depend on the engine used
new rds.DatabaseCluster(this, 'Cluster', {
  cloudwatchLogsExports: []
  ...
});

// for AURORA_POSTGRESQL
new rds.DatabaseCluster(this, 'Cluster', {
  engine: rds.DatabaseClusterEngine.AURORA_POSTGRESQL,
  cloudwatchLogsExports: ['Postgresql', 'Upgrade'],
  ...
});

// for AURORA_MYSQL & AURORA
new rds.DatabaseCluster(this, 'Cluster', {
  engine: rds.DatabaseClusterEngine.AURORA_MYSQL,
  cloudwatchLogsExports: ['Audit', 'Error', 'General', 'SlowQuery'],
  ...
});

// for MySQL
new rds.DatabaseInstance(this, 'RDS', {
  engine: rds.DatabaseInstanceEngine.MYSQL,
  cloudwatchLogsExports: ['Audit', 'Error', 'General', 'SlowQuery'],
});

// for POSTGRES
new rds.DatabaseInstance(this, 'RDS', {
  engine: rds.DatabaseInstanceEngine.POSTGRESQL,
  cloudwatchLogsExports: ['Postgresql', 'Upgrade'],
  ...
});

// for ORACLE_EE, ORACLE_SE, ORACLE_SE1, ORACLE_SE2
new rds.DatabaseInstance(this, 'RDS', {
  engine: rds.DatabaseInstanceEngine.ORACLE_XX,
  cloudwatchLogsExports: ['Alert', 'Audit', 'Trace', 'Listener'],
  ...
});

// for SQL_SERVER_EE, SQL_SERVER_EX, SQL_SERVER_SE, SQL_SERVER_SE, SQL_SERVER_WEB
new rds.DatabaseInstance(this, 'RDS', {
  engine: rds.DatabaseInstanceEngine.SQL_SERVER_XX,
  cloudwatchLogsExports: ['Error', 'Agent'],
  ...
});

// for MARIADB
new rds.DatabaseInstance(this, 'RDS', {
  engine: rds.DatabaseInstanceEngine.MARIADB,
  cloudwatchLogsExports: ['Audit', 'Error', 'General', 'SlowQuery'],
  ...
});

How to enable

new rds.DatabaseInstance(this, 'RDS', {
  iamAuthentication: true,
  ...
});

// or
new rds.DatabaseInstanceFromSnapshot(this, 'RDS', {
  iamAuthentication: true,
  ...
});

// or
new rds.DatabaseInstanceReadReplica(this, 'RDS', {
  iamAuthentication: true,
  ...
});

Will fail rule

new redshift.Cluster(this, 'Cluster', {
  publiclyAccessible:  true,
});

How to enable

const parameterGroup = new redshift.ClusterParameterGroup(this, 'ParamGroup', {
  parameters: {
    require_SSL: '1',
  },
});

new redshift.Cluster(this, 'Cluster', {
  parameterGroup,
});

Currently default to 1 without the ability to change. issue link

This is automatically enabled when using the Redshift Cluster L2 Construct

How to enable

new s3.Bucket(this, 'Bucket', {
  blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
});

// or

new s3.Bucket(this, 'Bucket', {
  blockPublicAccess: new s3.BlockPublicAccess({
    blockPublicAcls: true,
    blockPublicPolicy: true,
    ignorePublicAcls: true,
    restrictPublicBuckets: true,
  }),
});

Through Block Public Access settings How to enable

new s3.Bucket(this, 'Bucket', {
  blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
});

// or

new s3.Bucket(this, 'Bucket', {
  blockPublicAccess: new s3.BlockPublicAccess({
    blockPublicAcls: true,
    blockPublicPolicy: true,
    ignorePublicAcls: true,
    restrictPublicBuckets: true,
  }),
});

Through Bucket ACL Will fail rule

new s3.Bucket(this, 'Bucket', {
  accessControl: s3.BucketAccessControl.PUBLIC_READ,
});

// or

new s3.Bucket(this, 'Bucket', {
  accessControl: s3.BucketAccessControl.PUBLIC_READ_WRITE,
});

// or

const bucket = new s3.Bucket(this, 'Bucket', {
  accessControl: s3.BucketAccessControl.AUTHENTICATED_READ,
});

b.grantReadAccess()
bucket.grantReadAccess();

// or

new s3.Bucket(this, 'Bucket', {
  publicReadAccess: true,
});

Through Bucket policy

const bucket = new s3.Bucket(this, 'Bucket');

bucket.grantPublicAccess();

// or
bucket.addToResourcePolicy(new iam.PolicyStatement({
  ...,
  principals: ['*'],
}));

// or
const bucketPolicy = new s3.BucketPolicy(this, 'BucketPolicy', {
  bucket,
});

bucketPolicy.document.addStatements([
  new iam.PolicyStatement({
    ...,
    principals: ['*'],
  }),
]);

Through Block Public Access settings How to enable

new s3.Bucket(this, 'Bucket', {
  blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
});

// or

new s3.Bucket(this, 'Bucket', {
  blockPublicAccess: new s3.BlockPublicAccess({
    blockPublicAcls: true,
    blockPublicPolicy: true,
    ignorePublicAcls: true,
    restrictPublicBuckets: true,
  }),
});

Through Bucket ACL Will fail rule

new s3.Bucket(this, 'Bucket', {
  accessControl: s3.BucketAccessControl.PUBLIC_READ_WRITE,
});

Through Bucket policy Will fail rule

const bucket = new s3.Bucket(this, 'Bucket');

bucket.grantPublicAccess();

// or
bucket.addToResourcePolicy(new iam.PolicyStatement({
  ...,
  principals: ['*'],
}));

// or
const bucketPolicy = new s3.BucketPolicy(this, 'BucketPolicy', {
  bucket,
});

bucketPolicy.document.addStatements([
  new iam.PolicyStatement({
    ...,
    principals: ['*'],
  }),
]);

How to enable

new s3.Bucket(this, 'Bucket', {
  encryption: s3.BucketEncryption.XXX // any value
});

// or

new s3.Bucket(this, 'Bucket', {
  encryptionKey,
});

How to enable

new s3.Bucket(this, 'Bucket', {
  enforceSSL: true,
})

Will fail rule

const policy = new iam.PolicyStatement({
  actions: [ // these actions are not allowed
    's3:DeleteBucketPolicy',
    's3:PutBucketAcl',
    's3:PutBucketPolicy',
    's3:PutEncryptionConfiguration',
    's3:PutObjectAcl',
  ],
  principals: [
    new iam.AccountPrincipal('11111111111'), // If granting access to current account should use iam.AccountRootPrincipal()
    new iam.AnyPrincipal(), // allows access from any AWS account, i.e. { "AWS": "*" }
  ],
  ...
})

Currently no L2

How to enable

const secret = new secretsmanager.Secret(this, 'Secret');
secret.addRotationSchedule();

// or

const secret = new secretsmanager.Secret(this, 'Secret');
new secretsmanager.RotationSchedule(this, 'Rotation', {
  secret,
})

How to enable

new sns.Topic(this, 'Topic', {
  masterKey,
  ...
})

Readme

Keywords

none

Package Sidebar

Install

npm i eslint-plugin-awscdk

Weekly Downloads

364

Version

0.0.65

License

Apache-2.0

Unpacked Size

340 kB

Total Files

64

Last publish

Collaborators

  • corymhall