diff --git a/.projenrc.js b/.projenrc.js index 869e2627a8..eecfd53868 100644 --- a/.projenrc.js +++ b/.projenrc.js @@ -105,6 +105,7 @@ const project = new AwsCdkConstructLibrary({ }); project.package.addField('resolutions', { 'ansi-regex': '^5.0.1', + 'json-schema': '^0.4.0', }); project.package.addField('prettier', { singleQuote: true, diff --git a/API.md b/API.md index 468bdc6fbf..7c88300169 100644 --- a/API.md +++ b/API.md @@ -33,6 +33,7 @@ Name|Description Name|Description ----|----------- [NagMessageLevel](#cdk-nag-nagmessagelevel)|The level of the message that the rule applies. +[NagRuleCompliance](#cdk-nag-nagrulecompliance)|The compliance level of a resource in relation to a rule. @@ -54,6 +55,7 @@ new AwsSolutionsChecks(props?: NagPackProps) * **props** ([NagPackProps](#cdk-nag-nagpackprops)) *No description* * **logIgnores** (boolean) Whether or not to log triggered rules that have been suppressed as informational messages (default: false). __*Optional*__ + * **reports** (boolean) Whether or not to generate CSV compliance reports for applied Stacks in the App's output directory (default: true). __*Optional*__ * **verbose** (boolean) Whether or not to enable extended explanatory descriptions on warning, error, and logged ignore messages (default: false). __*Optional*__ @@ -95,6 +97,7 @@ new HIPAASecurityChecks(props?: NagPackProps) * **props** ([NagPackProps](#cdk-nag-nagpackprops)) *No description* * **logIgnores** (boolean) Whether or not to log triggered rules that have been suppressed as informational messages (default: false). __*Optional*__ + * **reports** (boolean) Whether or not to generate CSV compliance reports for applied Stacks in the App's output directory (default: true). __*Optional*__ * **verbose** (boolean) Whether or not to enable extended explanatory descriptions on warning, error, and logged ignore messages (default: false). __*Optional*__ @@ -136,6 +139,7 @@ new NIST80053R4Checks(props?: NagPackProps) * **props** ([NagPackProps](#cdk-nag-nagpackprops)) *No description* * **logIgnores** (boolean) Whether or not to log triggered rules that have been suppressed as informational messages (default: false). __*Optional*__ + * **reports** (boolean) Whether or not to generate CSV compliance reports for applied Stacks in the App's output directory (default: true). __*Optional*__ * **verbose** (boolean) Whether or not to enable extended explanatory descriptions on warning, error, and logged ignore messages (default: false). __*Optional*__ @@ -177,6 +181,7 @@ new NIST80053R5Checks(props?: NagPackProps) * **props** ([NagPackProps](#cdk-nag-nagpackprops)) *No description* * **logIgnores** (boolean) Whether or not to log triggered rules that have been suppressed as informational messages (default: false). __*Optional*__ + * **reports** (boolean) Whether or not to generate CSV compliance reports for applied Stacks in the App's output directory (default: true). __*Optional*__ * **verbose** (boolean) Whether or not to enable extended explanatory descriptions on warning, error, and logged ignore messages (default: false). __*Optional*__ @@ -216,6 +221,7 @@ new NagPack(props?: NagPackProps) * **props** ([NagPackProps](#cdk-nag-nagpackprops)) *No description* * **logIgnores** (boolean) Whether or not to log triggered rules that have been suppressed as informational messages (default: false). __*Optional*__ + * **reports** (boolean) Whether or not to generate CSV compliance reports for applied Stacks in the App's output directory (default: true). __*Optional*__ * **verbose** (boolean) Whether or not to enable extended explanatory descriptions on warning, error, and logged ignore messages (default: false). __*Optional*__ @@ -228,17 +234,33 @@ Name | Type | Description **logIgnores** | boolean | **packName** | string | **readPackName** | string | +**readReportStacks** | Array | +**reportStacks** | Array | +**reports** | boolean | **verbose** | boolean | ### Methods -#### applyRule(params) +#### visit(node) + +All aspects can visit an IConstruct. + +```ts +visit(node: IConstruct): void +``` + +* **node** ([IConstruct](#aws-cdk-core-iconstruct)) *No description* + + + + +#### protected applyRule(params) Create a rule to be used in the NagPack. ```ts -applyRule(params: IApplyRule): void +protected applyRule(params: IApplyRule): void ``` * **params** ([IApplyRule](#cdk-nag-iapplyrule)) The. @@ -246,15 +268,63 @@ applyRule(params: IApplyRule): void -#### visit(node) +#### protected createComplianceReportLine(params, ruleId, compliance, explanation?) -All aspects can visit an IConstruct. +Helper function to create a line for the compliance report. ```ts -visit(node: IConstruct): void +protected createComplianceReportLine(params: IApplyRule, ruleId: string, compliance: NagRuleCompliance | string, explanation?: string): string ``` -* **node** ([IConstruct](#aws-cdk-core-iconstruct)) *No description* +* **params** ([IApplyRule](#cdk-nag-iapplyrule)) The. +* **ruleId** (string) The id of the rule. +* **compliance** ([NagRuleCompliance](#cdk-nag-nagrulecompliance) | string) The compliance status of the rule. +* **explanation** (string) The explanation for suppressed rules. + +__Returns__: +* string + +#### protected createMessage(ruleId, info, explanation) + +The message to output to the console when a rule is triggered. + +```ts +protected createMessage(ruleId: string, info: string, explanation: string): string +``` + +* **ruleId** (string) The id of the rule. +* **info** (string) Why the rule was triggered. +* **explanation** (string) Why the rule exists. + +__Returns__: +* string + +#### protected ignoreRule(ignores, ruleId) + +Check whether a specific rule should be ignored. + +```ts +protected ignoreRule(ignores: Array, ruleId: string): string +``` + +* **ignores** (Array<[NagPackSuppression](#cdk-nag-nagpacksuppression)>) The ignores listed in cdk-nag metadata. +* **ruleId** (string) The id of the rule to ignore. + +__Returns__: +* string + +#### protected writeToStackComplianceReport(params, ruleId, compliance, explanation?) + +Write a line to the rule packs compliance report for the resource's Stack. + +```ts +protected writeToStackComplianceReport(params: IApplyRule, ruleId: string, compliance: NagRuleCompliance | string, explanation?: string): void +``` + +* **params** ([IApplyRule](#cdk-nag-iapplyrule)) The. +* **ruleId** (string) The id of the rule. +* **compliance** ([NagRuleCompliance](#cdk-nag-nagrulecompliance) | string) The compliance status of the rule. +* **explanation** (string) The explanation for suppressed rules. @@ -346,6 +416,7 @@ new PCIDSS321Checks(props?: NagPackProps) * **props** ([NagPackProps](#cdk-nag-nagpackprops)) *No description* * **logIgnores** (boolean) Whether or not to log triggered rules that have been suppressed as informational messages (default: false). __*Optional*__ + * **reports** (boolean) Whether or not to generate CSV compliance reports for applied Stacks in the App's output directory (default: true). __*Optional*__ * **verbose** (boolean) Whether or not to enable extended explanatory descriptions on warning, error, and logged ignore messages (default: false). __*Optional*__ @@ -391,13 +462,13 @@ Name | Type | Description The callback to the rule. ```ts -rule(node: CfnResource): boolean +rule(node: CfnResource): NagRuleCompliance ``` * **node** ([CfnResource](#aws-cdk-core-cfnresource)) The CfnResource to check. __Returns__: -* boolean +* [NagRuleCompliance](#cdk-nag-nagrulecompliance) @@ -411,6 +482,7 @@ Interface for creating a Nag rule pack. Name | Type | Description -----|------|------------- **logIgnores**? | boolean | Whether or not to log triggered rules that have been suppressed as informational messages (default: false).
__*Optional*__ +**reports**? | boolean | Whether or not to generate CSV compliance reports for applied Stacks in the App's output directory (default: true).
__*Optional*__ **verbose**? | boolean | Whether or not to enable extended explanatory descriptions on warning, error, and logged ignore messages (default: false).
__*Optional*__ @@ -439,3 +511,14 @@ Name | Description **ERROR** | +## enum NagRuleCompliance + +The compliance level of a resource in relation to a rule. + +Name | Description +-----|----- +**COMPLIANT** | +**NON_COMPLIANT** | +**NOT_APPLICABLE** | + + diff --git a/README.md b/README.md index 2a77b585de..9b9469d1cd 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,8 @@ See [RULES](./RULES.md) for more information on all the available packs. ## Usage +For a full list of options See `NagPackProps` in the [API.md](./API.md#struct-nagpackprops) +
cdk diff --git a/package.json b/package.json index b91486806a..b633c70b2c 100644 --- a/package.json +++ b/package.json @@ -240,7 +240,8 @@ } }, "resolutions": { - "ansi-regex": "^5.0.1" + "ansi-regex": "^5.0.1", + "json-schema": "^0.4.0" }, "prettier": { "singleQuote": true, diff --git a/src/nag-pack.ts b/src/nag-pack.ts index 9f527e7322..1046dffb69 100644 --- a/src/nag-pack.ts +++ b/src/nag-pack.ts @@ -2,12 +2,15 @@ Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ +import { appendFileSync, writeFileSync } from 'fs'; +import { join } from 'path'; import { IAspect, IConstruct, Annotations, CfnResource, Stack, + App, } from '@aws-cdk/core'; import { NagPackSuppression } from './nag-suppressions'; @@ -27,6 +30,11 @@ export interface NagPackProps { * Whether or not to log triggered rules that have been suppressed as informational messages (default: false). */ readonly logIgnores?: boolean; + + /** + * Whether or not to generate CSV compliance reports for applied Stacks in the App's output directory (default: true). + */ + readonly reports?: boolean; } /** @@ -57,15 +65,24 @@ export interface IApplyRule { * The callback to the rule. * @param node The CfnResource to check. */ - rule(node: CfnResource): boolean; + rule(node: CfnResource): NagRuleCompliance; } /** * The level of the message that the rule applies. */ export enum NagMessageLevel { - WARN, - ERROR, + WARN = 'Warning', + ERROR = 'Error', +} + +/** + * The compliance level of a resource in relation to a rule. + */ +export enum NagRuleCompliance { + COMPLIANT = 'Compliant', + NON_COMPLIANT = 'Non-Compliant', + NOT_APPLICABLE = 'N/A', } /** @@ -74,6 +91,8 @@ export enum NagMessageLevel { export abstract class NagPack implements IAspect { protected verbose: boolean; protected logIgnores: boolean; + protected reports: boolean; + protected reportStacks = new Array(); protected packName = ''; constructor(props?: NagPackProps) { @@ -83,11 +102,16 @@ export abstract class NagPack implements IAspect { props == undefined || props.logIgnores == undefined ? false : props.logIgnores; + this.reports = + props == undefined || props.reports == undefined ? true : props.reports; } public get readPackName(): string { return this.packName; } + public get readReportStacks(): string[] { + return this.reportStacks; + } /** * All aspects can visit an IConstruct. */ @@ -97,7 +121,7 @@ export abstract class NagPack implements IAspect { * Create a rule to be used in the NagPack. * @param params The @IApplyRule interface with rule details. */ - public applyRule(params: IApplyRule): void { + protected applyRule(params: IApplyRule): void { if (this.packName === '') { throw Error( 'The NagPack does not have a pack name, therefore the rule could not be applied. Set a packName in the NagPack constructor.' @@ -114,8 +138,22 @@ export abstract class NagPack implements IAspect { : params.rule.name; const ruleId = `${this.packName}-${ruleSuffix}`; try { - if (!params.rule(params.node)) { + const ruleCompliance = params.rule(params.node); + if ( + this.reports === true && + ruleCompliance === NagRuleCompliance.COMPLIANT + ) { + this.writeToStackComplianceReport(params, ruleId, ruleCompliance); + } else if (ruleCompliance === NagRuleCompliance.NON_COMPLIANT) { const reason = this.ignoreRule(allIgnores, ruleId); + if (this.reports === true) { + this.writeToStackComplianceReport( + params, + ruleId, + ruleCompliance, + reason + ); + } if (reason) { if (this.logIgnores === true) { const message = this.createMessage( @@ -140,6 +178,9 @@ export abstract class NagPack implements IAspect { } } catch (error) { const reason = this.ignoreRule(allIgnores, VALIDATION_FAILURE_ID); + if (this.reports === true) { + this.writeToStackComplianceReport(params, ruleId, 'UNKNOWN', reason); + } if (reason) { if (this.logIgnores === true) { const message = this.createMessage( @@ -167,7 +208,7 @@ export abstract class NagPack implements IAspect { * @param ruleId The id of the rule to ignore. * @returns The reason the rule was ignored, or an empty string. */ - private ignoreRule(ignores: NagPackSuppression[], ruleId: string): string { + protected ignoreRule(ignores: NagPackSuppression[], ruleId: string): string { for (let ignore of ignores) { if ( ignore.id && @@ -195,7 +236,7 @@ export abstract class NagPack implements IAspect { * @param explanation Why the rule exists. * @returns The formatted message string. */ - private createMessage( + protected createMessage( ruleId: string, info: string, explanation: string @@ -203,6 +244,77 @@ export abstract class NagPack implements IAspect { let message = `${ruleId}: ${info}`; return this.verbose ? `${message} ${explanation}\n` : `${message}\n`; } + + /** + * Write a line to the rule packs compliance report for the resource's Stack + * @param params The @IApplyRule interface with rule details. + * @param ruleId The id of the rule. + * @param compliance The compliance status of the rule. + * @param explanation The explanation for suppressed rules. + */ + protected writeToStackComplianceReport( + params: IApplyRule, + ruleId: string, + compliance: + | NagRuleCompliance.COMPLIANT + | NagRuleCompliance.NON_COMPLIANT + | 'UNKNOWN', + explanation: string = '' + ): void { + const line = this.createComplianceReportLine( + params, + ruleId, + compliance, + explanation + ); + let outDir = App.of(params.node)?.outdir; + const fileName = `${this.packName}-${params.node.stack.stackName}-NagReport.csv`; + const filePath = join(outDir ? outDir : '', fileName); + if (!this.reportStacks.includes(fileName)) { + this.reportStacks.push(fileName); + writeFileSync( + filePath, + 'Rule ID,Resource ID,Compliance,Exception Reason,Rule Level,Rule Info\n' + ); + } + appendFileSync(filePath, line); + } + + /** + * Helper function to create a line for the compliance report + * @param params The @IApplyRule interface with rule details. + * @param ruleId The id of the rule. + * @param compliance The compliance status of the rule. + * @param explanation The explanation for suppressed rules. + */ + protected createComplianceReportLine( + params: IApplyRule, + ruleId: string, + compliance: + | NagRuleCompliance.COMPLIANT + | NagRuleCompliance.NON_COMPLIANT + | 'UNKNOWN', + explanation: string = '' + ): string { + //| Rule ID | Resource ID | Compliance | Exception Reason | Rule Level | Rule Info + const line = Array(); + line.push(ruleId); + line.push(params.node.node.path); + if ( + (compliance === NagRuleCompliance.NON_COMPLIANT || + compliance === 'UNKNOWN') && + explanation !== '' + ) { + line.push('Suppressed'); + line.push(explanation); + } else { + line.push(compliance); + line.push('N/A'); + } + line.push(params.level); + line.push(params.info); + return line.map((i) => '"' + i.replace(/"/g, '""') + '"').join(',') + '\n'; + } } /** diff --git a/src/rules/apigw/APIGWAccessLogging.ts b/src/rules/apigw/APIGWAccessLogging.ts index 30fb18e65c..389d688b49 100644 --- a/src/rules/apigw/APIGWAccessLogging.ts +++ b/src/rules/apigw/APIGWAccessLogging.ts @@ -6,37 +6,40 @@ import { parse } from 'path'; import { CfnStage } from '@aws-cdk/aws-apigateway'; import { CfnStage as CfnV2Stage } from '@aws-cdk/aws-apigatewayv2'; import { CfnResource, Stack } from '@aws-cdk/core'; - +import { NagRuleCompliance } from '../../nag-pack'; /** * APIs have access logging enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnStage) { if (node.accessLogSetting == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const accessLogSetting = Stack.of(node).resolve(node.accessLogSetting); if ( accessLogSetting.destinationArn == undefined || accessLogSetting.format == undefined ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; } else if (node instanceof CfnV2Stage) { if (node.accessLogSettings == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const accessLogSetting = Stack.of(node).resolve(node.accessLogSettings); if ( accessLogSetting.destinationArn == undefined || accessLogSetting.format == undefined ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/apigw/APIGWAssociatedWithWAF.ts b/src/rules/apigw/APIGWAssociatedWithWAF.ts index d5ed3f9239..720b55add9 100644 --- a/src/rules/apigw/APIGWAssociatedWithWAF.ts +++ b/src/rules/apigw/APIGWAssociatedWithWAF.ts @@ -6,14 +6,17 @@ import { parse } from 'path'; import { CfnStage } from '@aws-cdk/aws-apigateway'; import { CfnWebACLAssociation } from '@aws-cdk/aws-wafv2'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveResourceFromInstrinsic } from '../../nag-pack'; +import { + resolveResourceFromInstrinsic, + NagRuleCompliance, +} from '../../nag-pack'; /** * Rest API stages are associated with AWS WAFv2 web ACLs * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnStage) { const stageLogicalId = resolveResourceFromInstrinsic(node, node.ref); const stageName = resolveResourceFromInstrinsic(node, node.stageName); @@ -35,10 +38,12 @@ export default Object.defineProperty( } } if (!found) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/apigw/APIGWAuthorization.ts b/src/rules/apigw/APIGWAuthorization.ts index df5d937e11..02f09b5fb6 100644 --- a/src/rules/apigw/APIGWAuthorization.ts +++ b/src/rules/apigw/APIGWAuthorization.ts @@ -6,14 +6,14 @@ import { parse } from 'path'; import { AuthorizationType, CfnMethod } from '@aws-cdk/aws-apigateway'; import { CfnRoute } from '@aws-cdk/aws-apigatewayv2'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * APIs implement authorization * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnMethod || node instanceof CfnRoute) { const authorizationType = resolveIfPrimitive( node, @@ -23,10 +23,12 @@ export default Object.defineProperty( authorizationType == undefined || authorizationType == AuthorizationType.NONE ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/apigw/APIGWCacheEnabledAndEncrypted.ts b/src/rules/apigw/APIGWCacheEnabledAndEncrypted.ts index 829192b39f..7a68bcb3bd 100644 --- a/src/rules/apigw/APIGWCacheEnabledAndEncrypted.ts +++ b/src/rules/apigw/APIGWCacheEnabledAndEncrypted.ts @@ -5,16 +5,17 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnStage } from '@aws-cdk/aws-apigateway'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * All methods in API Gateway stages have caching enabled and encrypted * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnStage) { if (node.methodSettings == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const methodSettings = Stack.of(node).resolve(node.methodSettings); let found = false; @@ -31,10 +32,12 @@ export default Object.defineProperty( } } if (!found) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/apigw/APIGWExecutionLoggingEnabled.ts b/src/rules/apigw/APIGWExecutionLoggingEnabled.ts index 831f76b6bf..37162cf77c 100644 --- a/src/rules/apigw/APIGWExecutionLoggingEnabled.ts +++ b/src/rules/apigw/APIGWExecutionLoggingEnabled.ts @@ -5,16 +5,17 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnStage, MethodLoggingLevel } from '@aws-cdk/aws-apigateway'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * API Gateway stages have logging enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnStage) { if (node.methodSettings == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const methodSettings = Stack.of(node).resolve(node.methodSettings); let found = false; @@ -31,10 +32,12 @@ export default Object.defineProperty( } } if (!found) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/apigw/APIGWRequestValidation.ts b/src/rules/apigw/APIGWRequestValidation.ts index 1609658bca..d0bb8fc24f 100644 --- a/src/rules/apigw/APIGWRequestValidation.ts +++ b/src/rules/apigw/APIGWRequestValidation.ts @@ -5,14 +5,17 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnRequestValidator, CfnRestApi } from '@aws-cdk/aws-apigateway'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveResourceFromInstrinsic } from '../../nag-pack'; +import { + resolveResourceFromInstrinsic, + NagRuleCompliance, +} from '../../nag-pack'; /** * Rest APIs have request validation enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnRestApi) { const apiLogicalId = resolveResourceFromInstrinsic(node, node.ref); let found = false; @@ -25,10 +28,12 @@ export default Object.defineProperty( } } if (!found) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/apigw/APIGWSSLEnabled.ts b/src/rules/apigw/APIGWSSLEnabled.ts index 8a13745cd5..323c7a489a 100644 --- a/src/rules/apigw/APIGWSSLEnabled.ts +++ b/src/rules/apigw/APIGWSSLEnabled.ts @@ -5,19 +5,22 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnStage } from '@aws-cdk/aws-apigateway'; import { CfnResource } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * API Gateway REST API stages are configured with SSL certificates * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnStage) { if (node.clientCertificateId == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/apigw/APIGWXrayEnabled.ts b/src/rules/apigw/APIGWXrayEnabled.ts index a4e823c883..4ee24fe338 100644 --- a/src/rules/apigw/APIGWXrayEnabled.ts +++ b/src/rules/apigw/APIGWXrayEnabled.ts @@ -5,20 +5,22 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnStage } from '@aws-cdk/aws-apigateway'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * API Gateway REST API stages have X-Ray tracing enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnStage) { const tracingEnabled = resolveIfPrimitive(node, node.tracingEnabled); if (tracingEnabled !== true) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/appsync/AppSyncGraphQLRequestLogging.ts b/src/rules/appsync/AppSyncGraphQLRequestLogging.ts index a61505c5ab..5e59a2355e 100644 --- a/src/rules/appsync/AppSyncGraphQLRequestLogging.ts +++ b/src/rules/appsync/AppSyncGraphQLRequestLogging.ts @@ -5,18 +5,18 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnGraphQLApi } from '@aws-cdk/aws-appsync'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * GraphQL APIs have request leveling logging enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnGraphQLApi) { const logConfig = Stack.of(node).resolve(node.logConfig); if (logConfig === undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const excludeVerboseContent = resolveIfPrimitive( node, @@ -26,10 +26,12 @@ export default Object.defineProperty( logConfig.cloudWatchLogsRoleArn === undefined || excludeVerboseContent === true ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/athena/AthenaWorkgroupEncryptedQueryResults.ts b/src/rules/athena/AthenaWorkgroupEncryptedQueryResults.ts index 4ef2e280c8..1afa54b4dd 100644 --- a/src/rules/athena/AthenaWorkgroupEncryptedQueryResults.ts +++ b/src/rules/athena/AthenaWorkgroupEncryptedQueryResults.ts @@ -5,14 +5,14 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnWorkGroup } from '@aws-cdk/aws-athena'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Athena workgroups encrypt query results * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnWorkGroup) { const workGroupConfiguration = Stack.of(node).resolve( node.workGroupConfiguration @@ -22,7 +22,7 @@ export default Object.defineProperty( node.workGroupConfigurationUpdates ); if (workGroupConfigurationUpdates == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const resultConfigurationUpdates = Stack.of(node).resolve( workGroupConfigurationUpdates.resultConfigurationUpdates @@ -43,12 +43,12 @@ export default Object.defineProperty( removeEncryptionConfiguration && encryptionConfiguration == undefined ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } else if ( encryptionConfiguration != undefined && !enforceWorkGroupConfiguration ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } } else { @@ -57,25 +57,27 @@ export default Object.defineProperty( workGroupConfiguration.enforceWorkGroupConfiguration ); if (!enforceWorkGroupConfiguration) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const resultConfiguration = Stack.of(node).resolve( workGroupConfiguration.resultConfiguration ); if (resultConfiguration == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const encryptionConfiguration = Stack.of(node).resolve( resultConfiguration.encryptionConfiguration ); if (encryptionConfiguration == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/autoscaling/AutoScalingGroupCooldownPeriod.ts b/src/rules/autoscaling/AutoScalingGroupCooldownPeriod.ts index b3af8c0d68..fe9f992258 100644 --- a/src/rules/autoscaling/AutoScalingGroupCooldownPeriod.ts +++ b/src/rules/autoscaling/AutoScalingGroupCooldownPeriod.ts @@ -5,21 +5,23 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnAutoScalingGroup } from '@aws-cdk/aws-autoscaling'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Auto Scaling Groups have configured cooldown periods * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnAutoScalingGroup) { const cooldown = resolveIfPrimitive(node, node.cooldown); if (cooldown != undefined && parseInt(cooldown) == 0) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/autoscaling/AutoScalingGroupELBHealthCheckRequired.ts b/src/rules/autoscaling/AutoScalingGroupELBHealthCheckRequired.ts index b41c7d3384..5574d2564c 100644 --- a/src/rules/autoscaling/AutoScalingGroupELBHealthCheckRequired.ts +++ b/src/rules/autoscaling/AutoScalingGroupELBHealthCheckRequired.ts @@ -5,13 +5,13 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnAutoScalingGroup } from '@aws-cdk/aws-autoscaling'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Auto Scaling groups which are associated with load balancers utilize ELB health checks * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnAutoScalingGroup) { const classicLBs = Stack.of(node).resolve(node.loadBalancerNames); const otherLBs = Stack.of(node).resolve(node.targetGroupArns); @@ -22,14 +22,16 @@ export default Object.defineProperty( const healthCheckType = resolveIfPrimitive(node, node.healthCheckType); if (healthCheckType != undefined) { if (healthCheckType != 'ELB') { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } else { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/autoscaling/AutoScalingGroupHealthCheck.ts b/src/rules/autoscaling/AutoScalingGroupHealthCheck.ts index cd35d57bb2..4bb080b768 100644 --- a/src/rules/autoscaling/AutoScalingGroupHealthCheck.ts +++ b/src/rules/autoscaling/AutoScalingGroupHealthCheck.ts @@ -5,14 +5,14 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnAutoScalingGroup } from '@aws-cdk/aws-autoscaling'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Auto Scaling Groups have properly configured health checks * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnAutoScalingGroup) { const healthCheckType = resolveIfPrimitive(node, node.healthCheckType); const healthCheckGracePeriod = resolveIfPrimitive( @@ -24,10 +24,12 @@ export default Object.defineProperty( healthCheckType == 'ELB' && healthCheckGracePeriod == undefined ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/autoscaling/AutoScalingGroupScalingNotifications.ts b/src/rules/autoscaling/AutoScalingGroupScalingNotifications.ts index 9203c38f23..c7936b1ec4 100644 --- a/src/rules/autoscaling/AutoScalingGroupScalingNotifications.ts +++ b/src/rules/autoscaling/AutoScalingGroupScalingNotifications.ts @@ -5,16 +5,17 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnAutoScalingGroup, ScalingEvent } from '@aws-cdk/aws-autoscaling'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * Auto Scaling Groups have notifications for all scaling events configured * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnAutoScalingGroup) { if (node.notificationConfigurations == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const notificationConfigurations = < CfnAutoScalingGroup.NotificationConfigurationProperty[] @@ -27,13 +28,18 @@ export default Object.defineProperty( ScalingEvent.INSTANCE_TERMINATE_ERROR, ]; - return requiredEvents.every((req) => { + const compliant = requiredEvents.every((req) => { return notificationConfigurations.some((config) => { return config.notificationTypes?.includes(req); }); }); + if (compliant !== true) { + return NagRuleCompliance.NON_COMPLIANT; + } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/autoscaling/AutoScalingLaunchConfigPublicIpDisabled.ts b/src/rules/autoscaling/AutoScalingLaunchConfigPublicIpDisabled.ts index 834f109df2..5e075269a6 100644 --- a/src/rules/autoscaling/AutoScalingLaunchConfigPublicIpDisabled.ts +++ b/src/rules/autoscaling/AutoScalingLaunchConfigPublicIpDisabled.ts @@ -5,23 +5,25 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnLaunchConfiguration } from '@aws-cdk/aws-autoscaling'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Auto Scaling launch configurations have public IP addresses disabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnLaunchConfiguration) { const associatePublicIpAddress = resolveIfPrimitive( node, node.associatePublicIpAddress ); if (associatePublicIpAddress !== false) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/cloud9/Cloud9InstanceNoIngressSystemsManager.ts b/src/rules/cloud9/Cloud9InstanceNoIngressSystemsManager.ts index 60193385b6..b2e6b52727 100644 --- a/src/rules/cloud9/Cloud9InstanceNoIngressSystemsManager.ts +++ b/src/rules/cloud9/Cloud9InstanceNoIngressSystemsManager.ts @@ -5,21 +5,23 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnEnvironmentEC2 } from '@aws-cdk/aws-cloud9'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Cloud9 instances use no-ingress EC2 instances with AWS Systems Manager * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnEnvironmentEC2) { const connectionType = resolveIfPrimitive(node, node.connectionType); if (connectionType == undefined || connectionType != 'CONNECT_SSM') { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/cloudfront/CloudFrontDistributionAccessLogging.ts b/src/rules/cloudfront/CloudFrontDistributionAccessLogging.ts index b4fc7e9bc8..6432263de3 100644 --- a/src/rules/cloudfront/CloudFrontDistributionAccessLogging.ts +++ b/src/rules/cloudfront/CloudFrontDistributionAccessLogging.ts @@ -8,35 +8,38 @@ import { CfnStreamingDistribution, } from '@aws-cdk/aws-cloudfront'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * CloudFront distributions have access logging enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnDistribution) { const distributionConfig = Stack.of(node).resolve( node.distributionConfig ); if (distributionConfig.logging == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; } else if (node instanceof CfnStreamingDistribution) { const distributionConfig = Stack.of(node).resolve( node.streamingDistributionConfig ); if (distributionConfig.logging == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const logging = Stack.of(node).resolve(distributionConfig.logging); const enabled = resolveIfPrimitive(node, logging.enabled); if (!enabled) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/cloudfront/CloudFrontDistributionGeoRestrictions.ts b/src/rules/cloudfront/CloudFrontDistributionGeoRestrictions.ts index c983d3ee43..2b83ff3374 100644 --- a/src/rules/cloudfront/CloudFrontDistributionGeoRestrictions.ts +++ b/src/rules/cloudfront/CloudFrontDistributionGeoRestrictions.ts @@ -5,20 +5,20 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnDistribution } from '@aws-cdk/aws-cloudfront'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * CloudFront distributions may require Geo restrictions * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnDistribution) { const distributionConfig = Stack.of(node).resolve( node.distributionConfig ); if (distributionConfig.restrictions == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } else { const restrictions = Stack.of(node).resolve( distributionConfig.restrictions @@ -31,11 +31,13 @@ export default Object.defineProperty( geoRestrictions.restrictionType ); if (restrictionType == 'none') { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/cloudfront/CloudFrontDistributionNoOutdatedSSL.ts b/src/rules/cloudfront/CloudFrontDistributionNoOutdatedSSL.ts index d08bf9ed1b..4285722dfc 100644 --- a/src/rules/cloudfront/CloudFrontDistributionNoOutdatedSSL.ts +++ b/src/rules/cloudfront/CloudFrontDistributionNoOutdatedSSL.ts @@ -9,14 +9,14 @@ import { SecurityPolicyProtocol, } from '@aws-cdk/aws-cloudfront'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * CloudFront distributions do not use SSLv3 or TLSv1 for communication to the origin * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnDistribution) { const distributionConfig = Stack.of(node).resolve( node.distributionConfig @@ -34,10 +34,10 @@ export default Object.defineProperty( customOriginConfig.originProtocolPolicy ); if (originProtocolPolicy != OriginProtocolPolicy.HTTPS_ONLY) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } if (customOriginConfig.originSslProtocols == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const outdatedValues = [ SecurityPolicyProtocol.SSL_V3, @@ -51,14 +51,16 @@ export default Object.defineProperty( }); for (const outdated of outdatedValues) { if (lowerCaseProtocols.includes(outdated.toLowerCase())) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } } } } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/cloudfront/CloudFrontDistributionS3OriginAccessIdentity.ts b/src/rules/cloudfront/CloudFrontDistributionS3OriginAccessIdentity.ts index fe95d38d04..d3caf598d9 100644 --- a/src/rules/cloudfront/CloudFrontDistributionS3OriginAccessIdentity.ts +++ b/src/rules/cloudfront/CloudFrontDistributionS3OriginAccessIdentity.ts @@ -8,13 +8,14 @@ import { CfnStreamingDistribution, } from '@aws-cdk/aws-cloudfront'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * CloudFront distributions use an origin access identity for S3 origins * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnDistribution) { const distributionConfig = Stack.of(node).resolve( node.distributionConfig @@ -30,7 +31,7 @@ export default Object.defineProperty( /^.+\.s3(?:-website)?(?:\..+)?(?:(?:\.amazonaws\.com(?:\.cn)?)|(?:\.c2s\.ic\.gov)|(?:\.sc2s\.sgov\.gov))$/; if (s3Regex.test(resolvedDomainName)) { if (resolvedOrigin.s3OriginConfig == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const resolvedConfig = Stack.of(node).resolve( resolvedOrigin.s3OriginConfig @@ -39,11 +40,12 @@ export default Object.defineProperty( resolvedConfig.originAccessIdentity == undefined || resolvedConfig.originAccessIdentity.replace(/\s/g, '').length == 0 ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } } } + return NagRuleCompliance.COMPLIANT; } else if (node instanceof CfnStreamingDistribution) { const distributionConfig = Stack.of(node).resolve( node.streamingDistributionConfig @@ -52,10 +54,12 @@ export default Object.defineProperty( distributionConfig.s3Origin ); if (resolvedOrigin.originAccessIdentity.replace(/\s/g, '').length == 0) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/cloudfront/CloudFrontDistributionWAFIntegration.ts b/src/rules/cloudfront/CloudFrontDistributionWAFIntegration.ts index be82ecbae1..b0f1bf69c6 100644 --- a/src/rules/cloudfront/CloudFrontDistributionWAFIntegration.ts +++ b/src/rules/cloudfront/CloudFrontDistributionWAFIntegration.ts @@ -5,22 +5,25 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnDistribution } from '@aws-cdk/aws-cloudfront'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * CloudFront distributions may require integration with AWS WAF * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnDistribution) { const distributionConfig = Stack.of(node).resolve( node.distributionConfig ); if (distributionConfig.webAclId == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/cloudtrail/CloudTrailCloudWatchLogsEnabled.ts b/src/rules/cloudtrail/CloudTrailCloudWatchLogsEnabled.ts index 0719a7a348..2e63fd3680 100644 --- a/src/rules/cloudtrail/CloudTrailCloudWatchLogsEnabled.ts +++ b/src/rules/cloudtrail/CloudTrailCloudWatchLogsEnabled.ts @@ -5,21 +5,24 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnTrail } from '@aws-cdk/aws-cloudtrail'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * CloudTrail trails have CloudWatch logs enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnTrail) { const cloudWatch = Stack.of(node).resolve(node.cloudWatchLogsLogGroupArn); if (cloudWatch == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/cloudtrail/CloudTrailEncryptionEnabled.ts b/src/rules/cloudtrail/CloudTrailEncryptionEnabled.ts index 9e3ca96ba3..313bfddeee 100644 --- a/src/rules/cloudtrail/CloudTrailEncryptionEnabled.ts +++ b/src/rules/cloudtrail/CloudTrailEncryptionEnabled.ts @@ -5,21 +5,24 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnTrail } from '@aws-cdk/aws-cloudtrail'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * CloudTrail trails have encryption enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnTrail) { const keyID = Stack.of(node).resolve(node.kmsKeyId); if (keyID == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/cloudtrail/CloudTrailLogFileValidationEnabled.ts b/src/rules/cloudtrail/CloudTrailLogFileValidationEnabled.ts index 007b87b206..ab2a1842e4 100644 --- a/src/rules/cloudtrail/CloudTrailLogFileValidationEnabled.ts +++ b/src/rules/cloudtrail/CloudTrailLogFileValidationEnabled.ts @@ -5,21 +5,23 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnTrail } from '@aws-cdk/aws-cloudtrail'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * CloudTrail trails have log file validation enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnTrail) { const enabled = resolveIfPrimitive(node, node.enableLogFileValidation); if (enabled != true) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/cloudwatch/CloudWatchAlarmAction.ts b/src/rules/cloudwatch/CloudWatchAlarmAction.ts index d14608da24..03d61b886b 100644 --- a/src/rules/cloudwatch/CloudWatchAlarmAction.ts +++ b/src/rules/cloudwatch/CloudWatchAlarmAction.ts @@ -5,17 +5,18 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnAlarm } from '@aws-cdk/aws-cloudwatch'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * CloudWatch alarms have at least one alarm action, one INSUFFICIENT_DATA action, or one OK action enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnAlarm) { const actionsEnabled = Stack.of(node).resolve(node.actionsEnabled); if (actionsEnabled === false) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } // Actions can be an array with a token that then resolves to an empty array or undefined const alarmActions = Stack.of(node).resolve(node.alarmActions); @@ -31,10 +32,12 @@ export default Object.defineProperty( const totalActions = totalAlarmActions + totalInsufficientDataActions + totalOkActions; if (totalActions == 0) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/cloudwatch/CloudWatchLogGroupEncrypted.ts b/src/rules/cloudwatch/CloudWatchLogGroupEncrypted.ts index 7048004f9c..a089caefaf 100644 --- a/src/rules/cloudwatch/CloudWatchLogGroupEncrypted.ts +++ b/src/rules/cloudwatch/CloudWatchLogGroupEncrypted.ts @@ -5,19 +5,22 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnLogGroup } from '@aws-cdk/aws-logs'; import { CfnResource } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * CloudWatch Log Groups are encrypted with customer managed keys * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnLogGroup) { if (node.kmsKeyId == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/cloudwatch/CloudWatchLogGroupRetentionPeriod.ts b/src/rules/cloudwatch/CloudWatchLogGroupRetentionPeriod.ts index 76c1b6e8f4..a72ca73a46 100644 --- a/src/rules/cloudwatch/CloudWatchLogGroupRetentionPeriod.ts +++ b/src/rules/cloudwatch/CloudWatchLogGroupRetentionPeriod.ts @@ -5,19 +5,22 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnLogGroup } from '@aws-cdk/aws-logs'; import { CfnResource } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * CloudWatch Log Groups have an explicit retention period configured * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnLogGroup) { if (node.retentionInDays == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/codebuild/CodeBuildProjectEnvVarAwsCred.ts b/src/rules/codebuild/CodeBuildProjectEnvVarAwsCred.ts index 904f932280..8c56f5f5dc 100644 --- a/src/rules/codebuild/CodeBuildProjectEnvVarAwsCred.ts +++ b/src/rules/codebuild/CodeBuildProjectEnvVarAwsCred.ts @@ -5,14 +5,14 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnProject } from '@aws-cdk/aws-codebuild'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * CodeBuild projects do not store AWS credentials as plaintext environment variables * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnProject) { //Check for the presence of OAUTH const environment = Stack.of(node).resolve(node.environment); @@ -28,13 +28,15 @@ export default Object.defineProperty( if (name == 'AWS_ACCESS_KEY_ID' || name == 'AWS_SECRET_ACCESS_KEY') { //is this credential being stored as plaintext? if (type == undefined || type == 'PLAINTEXT') { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } } } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/codebuild/CodeBuildProjectKMSEncryptedArtifacts.ts b/src/rules/codebuild/CodeBuildProjectKMSEncryptedArtifacts.ts index e68c8e89ba..ae54625bfe 100644 --- a/src/rules/codebuild/CodeBuildProjectKMSEncryptedArtifacts.ts +++ b/src/rules/codebuild/CodeBuildProjectKMSEncryptedArtifacts.ts @@ -5,21 +5,23 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnProject } from '@aws-cdk/aws-codebuild'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Codebuild projects use an AWS KMS key for encryption * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnProject) { const encryptionKey = resolveIfPrimitive(node, node.encryptionKey); if (encryptionKey == undefined || encryptionKey == 'alias/aws/s3') { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/codebuild/CodeBuildProjectManagedImages.ts b/src/rules/codebuild/CodeBuildProjectManagedImages.ts index e60b4d736b..5788255e25 100644 --- a/src/rules/codebuild/CodeBuildProjectManagedImages.ts +++ b/src/rules/codebuild/CodeBuildProjectManagedImages.ts @@ -5,22 +5,24 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnProject } from '@aws-cdk/aws-codebuild'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Codebuild projects use images provided by the CodeBuild service or have a cdk_nag suppression rule explaining the need for a custom image * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnProject) { const environment = Stack.of(node).resolve(node.environment); const image = resolveIfPrimitive(node, environment.image); if (!image.startsWith('aws/codebuild/')) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/codebuild/CodeBuildProjectPrivilegedModeDisabled.ts b/src/rules/codebuild/CodeBuildProjectPrivilegedModeDisabled.ts index b0cb5a0725..b7e0b3611a 100644 --- a/src/rules/codebuild/CodeBuildProjectPrivilegedModeDisabled.ts +++ b/src/rules/codebuild/CodeBuildProjectPrivilegedModeDisabled.ts @@ -5,14 +5,14 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnProject } from '@aws-cdk/aws-codebuild'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Codebuild projects have privileged mode disabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnProject) { const environment = Stack.of(node).resolve(node.environment); const privilegedMode = resolveIfPrimitive( @@ -20,10 +20,12 @@ export default Object.defineProperty( environment.privilegedMode ); if (privilegedMode != undefined && privilegedMode) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/codebuild/CodeBuildProjectSourceRepoUrl.ts b/src/rules/codebuild/CodeBuildProjectSourceRepoUrl.ts index 1bf889f794..71679be42a 100644 --- a/src/rules/codebuild/CodeBuildProjectSourceRepoUrl.ts +++ b/src/rules/codebuild/CodeBuildProjectSourceRepoUrl.ts @@ -5,28 +5,30 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnProject } from '@aws-cdk/aws-codebuild'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Codebuild projects with a GitHub or BitBucket source repository utilize OAUTH * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnProject) { //Check for the presence of OAUTH const projectSource = Stack.of(node).resolve(node.source); const projectAuth = Stack.of(node).resolve(projectSource.auth); if (projectAuth == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } else { const projectAuthType = resolveIfPrimitive(node, projectAuth.type); if (projectAuthType != 'OAUTH') { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/cognito/CognitoUserPoolAPIGWAuthorizer.ts b/src/rules/cognito/CognitoUserPoolAPIGWAuthorizer.ts index 39198cf218..2e0e5592ba 100644 --- a/src/rules/cognito/CognitoUserPoolAPIGWAuthorizer.ts +++ b/src/rules/cognito/CognitoUserPoolAPIGWAuthorizer.ts @@ -5,19 +5,22 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnMethod } from '@aws-cdk/aws-apigateway'; import { CfnResource } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * Rest API methods use Cognito User Pool Authorizers * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnMethod) { if (node.authorizationType !== 'COGNITO_USER_POOLS') { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/cognito/CognitoUserPoolAdvancedSecurityModeEnforced.ts b/src/rules/cognito/CognitoUserPoolAdvancedSecurityModeEnforced.ts index 724cb767ee..bdae5b1d6f 100644 --- a/src/rules/cognito/CognitoUserPoolAdvancedSecurityModeEnforced.ts +++ b/src/rules/cognito/CognitoUserPoolAdvancedSecurityModeEnforced.ts @@ -5,18 +5,18 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnUserPool } from '@aws-cdk/aws-cognito'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Cognito user pools have AdvancedSecurityMode set to ENFORCED * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnUserPool) { const userPoolAddOns = Stack.of(node).resolve(node.userPoolAddOns); if (userPoolAddOns == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const advancedSecurityMode = resolveIfPrimitive( node, @@ -26,10 +26,12 @@ export default Object.defineProperty( advancedSecurityMode == undefined || advancedSecurityMode != 'ENFORCED' ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/cognito/CognitoUserPoolMFA.ts b/src/rules/cognito/CognitoUserPoolMFA.ts index 4e3478b1fd..9e9e288da6 100644 --- a/src/rules/cognito/CognitoUserPoolMFA.ts +++ b/src/rules/cognito/CognitoUserPoolMFA.ts @@ -5,21 +5,23 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnUserPool, Mfa } from '@aws-cdk/aws-cognito'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Cognito user pools require MFA * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnUserPool) { const mfaConfiguration = resolveIfPrimitive(node, node.mfaConfiguration); if (mfaConfiguration == undefined || mfaConfiguration != Mfa.REQUIRED) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/cognito/CognitoUserPoolNoUnauthenticatedLogins.ts b/src/rules/cognito/CognitoUserPoolNoUnauthenticatedLogins.ts index c5ae935def..cab5f6c633 100644 --- a/src/rules/cognito/CognitoUserPoolNoUnauthenticatedLogins.ts +++ b/src/rules/cognito/CognitoUserPoolNoUnauthenticatedLogins.ts @@ -5,24 +5,26 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnIdentityPool } from '@aws-cdk/aws-cognito'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Cognito identity pools do not allow for unauthenticated logins without a valid reason * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnIdentityPool) { const allowUnauthenticatedIdentities = resolveIfPrimitive( node, node.allowUnauthenticatedIdentities ); if (allowUnauthenticatedIdentities) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/cognito/CognitoUserPoolStrongPasswordPolicy.ts b/src/rules/cognito/CognitoUserPoolStrongPasswordPolicy.ts index 9de6a8a2fb..8fd196f1ec 100644 --- a/src/rules/cognito/CognitoUserPoolStrongPasswordPolicy.ts +++ b/src/rules/cognito/CognitoUserPoolStrongPasswordPolicy.ts @@ -5,22 +5,22 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnUserPool } from '@aws-cdk/aws-cognito'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Cognito user pools have password policies that minimally specify a password length of at least 8 characters, as well as requiring uppercase, numeric, and special characters * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnUserPool) { const policies = Stack.of(node).resolve(node.policies); if (policies == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const passwordPolicy = Stack.of(node).resolve(policies.passwordPolicy); if (passwordPolicy == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const minimumLength = resolveIfPrimitive( @@ -28,7 +28,7 @@ export default Object.defineProperty( passwordPolicy.minimumLength ); if (minimumLength == undefined || minimumLength < 8) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const requireUppercase = resolveIfPrimitive( @@ -36,7 +36,7 @@ export default Object.defineProperty( passwordPolicy.requireUppercase ); if (requireUppercase !== true) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const requireNumbers = resolveIfPrimitive( @@ -44,7 +44,7 @@ export default Object.defineProperty( passwordPolicy.requireNumbers ); if (requireNumbers !== true) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const requireSymbols = resolveIfPrimitive( @@ -52,10 +52,12 @@ export default Object.defineProperty( passwordPolicy.requireSymbols ); if (requireSymbols !== true) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/dms/DMSReplicationNotPublic.ts b/src/rules/dms/DMSReplicationNotPublic.ts index 9e181a9ad6..12ded43f43 100644 --- a/src/rules/dms/DMSReplicationNotPublic.ts +++ b/src/rules/dms/DMSReplicationNotPublic.ts @@ -5,21 +5,23 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnReplicationInstance } from '@aws-cdk/aws-dms'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * DMS replication instances are not public * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnReplicationInstance) { const publicAccess = resolveIfPrimitive(node, node.publiclyAccessible); if (publicAccess !== false) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/documentdb/DocumentDBClusterBackupRetentionPeriod.ts b/src/rules/documentdb/DocumentDBClusterBackupRetentionPeriod.ts index f570094d28..8753238832 100644 --- a/src/rules/documentdb/DocumentDBClusterBackupRetentionPeriod.ts +++ b/src/rules/documentdb/DocumentDBClusterBackupRetentionPeriod.ts @@ -5,24 +5,26 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnDBCluster } from '@aws-cdk/aws-docdb'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Document DB clusters have a reasonable minimum backup retention period configured * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnDBCluster) { const backupRetentionPeriod = resolveIfPrimitive( node, node.backupRetentionPeriod ); if (backupRetentionPeriod == undefined || backupRetentionPeriod < 7) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/documentdb/DocumentDBClusterEncryptionAtRest.ts b/src/rules/documentdb/DocumentDBClusterEncryptionAtRest.ts index 219291abfc..895e1053c5 100644 --- a/src/rules/documentdb/DocumentDBClusterEncryptionAtRest.ts +++ b/src/rules/documentdb/DocumentDBClusterEncryptionAtRest.ts @@ -5,24 +5,26 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnDBCluster } from '@aws-cdk/aws-docdb'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Document DB clusters have encryption at rest enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnDBCluster) { if (node.storageEncrypted == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const storageEncrypted = resolveIfPrimitive(node, node.storageEncrypted); if (!storageEncrypted) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/documentdb/DocumentDBClusterLogExports.ts b/src/rules/documentdb/DocumentDBClusterLogExports.ts index 04e6eb3bed..41a33a97f2 100644 --- a/src/rules/documentdb/DocumentDBClusterLogExports.ts +++ b/src/rules/documentdb/DocumentDBClusterLogExports.ts @@ -5,22 +5,28 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnDBCluster } from '@aws-cdk/aws-docdb'; import { CfnResource } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * Document DB clusters have authenticate, createIndex, and dropCollection Log Exports enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnDBCluster) { if (node.enableCloudwatchLogsExports == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const needed = ['authenticate', 'createIndex', 'dropCollection']; const exports = node.enableCloudwatchLogsExports; - return needed.every((i) => exports.includes(i)); + const compliant = needed.every((i) => exports.includes(i)); + if (compliant !== true) { + return NagRuleCompliance.NON_COMPLIANT; + } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/documentdb/DocumentDBClusterNonDefaultPort.ts b/src/rules/documentdb/DocumentDBClusterNonDefaultPort.ts index 99bf1ac7bc..aee5e6e662 100644 --- a/src/rules/documentdb/DocumentDBClusterNonDefaultPort.ts +++ b/src/rules/documentdb/DocumentDBClusterNonDefaultPort.ts @@ -5,21 +5,23 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnDBCluster } from '@aws-cdk/aws-docdb'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Document DB clusters do not use the default endpoint port * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnDBCluster) { const port = resolveIfPrimitive(node, node.port); if (port == undefined || port == 27017) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/documentdb/DocumentDBCredentialsInSecretsManager.ts b/src/rules/documentdb/DocumentDBCredentialsInSecretsManager.ts index f6798f928c..47d4ffeeb1 100644 --- a/src/rules/documentdb/DocumentDBCredentialsInSecretsManager.ts +++ b/src/rules/documentdb/DocumentDBCredentialsInSecretsManager.ts @@ -5,14 +5,14 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnDBCluster } from '@aws-cdk/aws-docdb'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Document DB clusters have the username and password stored in Secrets Manager * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnDBCluster) { const masterUsername = resolveIfPrimitive(node, node.masterUsername); const masterUserPassword = resolveIfPrimitive( @@ -23,10 +23,12 @@ export default Object.defineProperty( masterUsername.includes('{{resolve:secretsmanager') == false || masterUserPassword.includes('{{resolve:secretsmanager') == false ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/dynamodb/DAXEncrypted.ts b/src/rules/dynamodb/DAXEncrypted.ts index 5406625873..adfd099940 100644 --- a/src/rules/dynamodb/DAXEncrypted.ts +++ b/src/rules/dynamodb/DAXEncrypted.ts @@ -5,25 +5,27 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnCluster } from '@aws-cdk/aws-dax'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * DAX clusters have server-side encryption enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnCluster) { if (node.sseSpecification == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const sseSpecification = Stack.of(node).resolve(node.sseSpecification); const enabled = resolveIfPrimitive(node, sseSpecification.sseEnabled); if (!enabled) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/dynamodb/DynamoDBAutoScalingEnabled.ts b/src/rules/dynamodb/DynamoDBAutoScalingEnabled.ts index e82a5dcb49..84acb70090 100644 --- a/src/rules/dynamodb/DynamoDBAutoScalingEnabled.ts +++ b/src/rules/dynamodb/DynamoDBAutoScalingEnabled.ts @@ -6,7 +6,10 @@ import { parse } from 'path'; import { CfnScalableTarget } from '@aws-cdk/aws-applicationautoscaling'; import { CfnTable, BillingMode } from '@aws-cdk/aws-dynamodb'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveResourceFromInstrinsic } from '../../nag-pack'; +import { + resolveResourceFromInstrinsic, + NagRuleCompliance, +} from '../../nag-pack'; /** * Provisioned capacity DynamoDB tables have auto-scaling enabled on their indexes @@ -14,7 +17,7 @@ import { resolveResourceFromInstrinsic } from '../../nag-pack'; */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnTable) { if ( resolveResourceFromInstrinsic(node, node.billingMode) !== @@ -60,11 +63,13 @@ export default Object.defineProperty( } } if (indexWriteMatches.length != 0 || indexReadMatches.length != 0) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/dynamodb/DynamoDBInBackupPlan.ts b/src/rules/dynamodb/DynamoDBInBackupPlan.ts index 7059ff6fb4..faee090911 100644 --- a/src/rules/dynamodb/DynamoDBInBackupPlan.ts +++ b/src/rules/dynamodb/DynamoDBInBackupPlan.ts @@ -6,7 +6,10 @@ import { parse } from 'path'; import { CfnBackupSelection } from '@aws-cdk/aws-backup'; import { CfnTable } from '@aws-cdk/aws-dynamodb'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveResourceFromInstrinsic } from '../../nag-pack'; +import { + resolveResourceFromInstrinsic, + NagRuleCompliance, +} from '../../nag-pack'; /** * DynamoDB tables are part of AWS Backup plan(s) @@ -14,7 +17,7 @@ import { resolveResourceFromInstrinsic } from '../../nag-pack'; */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnTable) { const tableLogicalId = resolveResourceFromInstrinsic(node, node.ref); const tableName = Stack.of(node).resolve(node.tableName); @@ -28,10 +31,12 @@ export default Object.defineProperty( } } if (!found) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/dynamodb/DynamoDBPITREnabled.ts b/src/rules/dynamodb/DynamoDBPITREnabled.ts index 2991d576e3..85cbc8bdfa 100644 --- a/src/rules/dynamodb/DynamoDBPITREnabled.ts +++ b/src/rules/dynamodb/DynamoDBPITREnabled.ts @@ -5,7 +5,7 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnTable } from '@aws-cdk/aws-dynamodb'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * DynamoDB tables have Point-in-time Recovery enabled @@ -13,20 +13,22 @@ import { resolveIfPrimitive } from '../../nag-pack'; */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnTable) { if (node.pointInTimeRecoverySpecification == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const pitr = Stack.of(node).resolve( node.pointInTimeRecoverySpecification ); const enabled = resolveIfPrimitive(node, pitr.pointInTimeRecoveryEnabled); if (!enabled) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/ec2/EC2EBSInBackupPlan.ts b/src/rules/ec2/EC2EBSInBackupPlan.ts index 08c77e67c6..1f62316b49 100644 --- a/src/rules/ec2/EC2EBSInBackupPlan.ts +++ b/src/rules/ec2/EC2EBSInBackupPlan.ts @@ -6,7 +6,10 @@ import { parse } from 'path'; import { CfnBackupSelection } from '@aws-cdk/aws-backup'; import { CfnVolume } from '@aws-cdk/aws-ec2'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveResourceFromInstrinsic } from '../../nag-pack'; +import { + resolveResourceFromInstrinsic, + NagRuleCompliance, +} from '../../nag-pack'; /** * EBS volumes are part of AWS Backup plan(s) @@ -14,7 +17,7 @@ import { resolveResourceFromInstrinsic } from '../../nag-pack'; */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnVolume) { const volumeLogicalId = resolveResourceFromInstrinsic(node, node.ref); let found = false; @@ -27,10 +30,12 @@ export default Object.defineProperty( } } if (!found) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/ec2/EC2EBSOptimizedInstance.ts b/src/rules/ec2/EC2EBSOptimizedInstance.ts index 16ec180802..174296c8e4 100644 --- a/src/rules/ec2/EC2EBSOptimizedInstance.ts +++ b/src/rules/ec2/EC2EBSOptimizedInstance.ts @@ -5,7 +5,7 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnInstance } from '@aws-cdk/aws-ec2'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; const EBS_OPTIMIZED_SUPPORTED = [ 'c1.xlarge', @@ -33,7 +33,7 @@ const DEFAULT_TYPE = 'm1.small'; * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnInstance) { const instanceType = node.instanceType ? resolveIfPrimitive(node, node.instanceType) @@ -43,10 +43,12 @@ export default Object.defineProperty( EBS_OPTIMIZED_SUPPORTED.includes(instanceType) && ebsOptimized !== true ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/ec2/EC2EBSVolumeEncrypted.ts b/src/rules/ec2/EC2EBSVolumeEncrypted.ts index 91037bab11..5f67e2dbdc 100644 --- a/src/rules/ec2/EC2EBSVolumeEncrypted.ts +++ b/src/rules/ec2/EC2EBSVolumeEncrypted.ts @@ -5,21 +5,23 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnVolume } from '@aws-cdk/aws-ec2'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * EBS volumes have encryption enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnVolume) { const encryption = resolveIfPrimitive(node, node.encrypted); if (encryption !== true) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/ec2/EC2InstanceDetailedMonitoringEnabled.ts b/src/rules/ec2/EC2InstanceDetailedMonitoringEnabled.ts index 15b05aed5c..77565b385f 100644 --- a/src/rules/ec2/EC2InstanceDetailedMonitoringEnabled.ts +++ b/src/rules/ec2/EC2InstanceDetailedMonitoringEnabled.ts @@ -6,26 +6,29 @@ import { parse } from 'path'; import { CfnLaunchConfiguration } from '@aws-cdk/aws-autoscaling'; import { CfnInstance } from '@aws-cdk/aws-ec2'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * EC2 instances have detailed monitoring enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnInstance) { const monitoring = resolveIfPrimitive(node, node.monitoring); if (monitoring == undefined || monitoring == false) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; } else if (node instanceof CfnLaunchConfiguration) { const monitoring = resolveIfPrimitive(node, node.instanceMonitoring); if (monitoring != undefined && monitoring == false) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/ec2/EC2InstanceNoPublicIp.ts b/src/rules/ec2/EC2InstanceNoPublicIp.ts index 3d6c63d38c..e754623487 100644 --- a/src/rules/ec2/EC2InstanceNoPublicIp.ts +++ b/src/rules/ec2/EC2InstanceNoPublicIp.ts @@ -5,14 +5,14 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnInstance } from '@aws-cdk/aws-ec2'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * EC2 instances do not have public IPs * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnInstance) { const networkInterfaces = Stack.of(node).resolve(node.networkInterfaces); if (networkInterfaces != undefined) { @@ -24,12 +24,14 @@ export default Object.defineProperty( resolvedInterface.associatePublicIpAddress ); if (associatePublicIpAddress === true) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/ec2/EC2InstanceProfileAttached.ts b/src/rules/ec2/EC2InstanceProfileAttached.ts index 3ed1522373..399de5878e 100644 --- a/src/rules/ec2/EC2InstanceProfileAttached.ts +++ b/src/rules/ec2/EC2InstanceProfileAttached.ts @@ -5,19 +5,22 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnInstance } from '@aws-cdk/aws-ec2'; import { CfnResource } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * EC2 instances have an instance profile attached * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnInstance) { if (node.iamInstanceProfile == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/ec2/EC2InstanceTerminationProtection.ts b/src/rules/ec2/EC2InstanceTerminationProtection.ts index d8f8a6053e..09795bde24 100644 --- a/src/rules/ec2/EC2InstanceTerminationProtection.ts +++ b/src/rules/ec2/EC2InstanceTerminationProtection.ts @@ -5,24 +5,26 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnInstance } from '@aws-cdk/aws-ec2'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * EC2 Instances outside of an ASG have Termination Protection enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnInstance) { const disableApiTermination = resolveIfPrimitive( node, node.disableApiTermination ); if (disableApiTermination !== true) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/ec2/EC2InstancesInVPC.ts b/src/rules/ec2/EC2InstancesInVPC.ts index 929ed33843..d7224885f2 100644 --- a/src/rules/ec2/EC2InstancesInVPC.ts +++ b/src/rules/ec2/EC2InstancesInVPC.ts @@ -5,21 +5,24 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnInstance } from '@aws-cdk/aws-ec2'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * EC2 instances are created within VPCs * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnInstance) { //If we are in a VPC, then we'll have a subnet const subnetId = Stack.of(node).resolve(node.subnetId); if (subnetId == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/ec2/EC2RestrictedCommonPorts.ts b/src/rules/ec2/EC2RestrictedCommonPorts.ts index b4d66b7a3a..6e19fd8683 100644 --- a/src/rules/ec2/EC2RestrictedCommonPorts.ts +++ b/src/rules/ec2/EC2RestrictedCommonPorts.ts @@ -5,7 +5,7 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnSecurityGroupIngress, CfnSecurityGroup } from '@aws-cdk/aws-ec2'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; const BLOCKED_PORTS = [20, 21, 3389, 3309, 3306, 4333]; @@ -15,7 +15,7 @@ const BLOCKED_PORTS = [20, 21, 3389, 3309, 3306, 4333]; * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnSecurityGroup) { const ingressRules = Stack.of(node).resolve(node.securityGroupIngress); if (ingressRules != undefined) { @@ -24,19 +24,22 @@ export default Object.defineProperty( const resolvedRule = Stack.of(node).resolve(rule); for (const portNum of BLOCKED_PORTS) { if (!testPort(node, resolvedRule, portNum)) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } } } + return NagRuleCompliance.COMPLIANT; } else if (node instanceof CfnSecurityGroupIngress) { for (const portNum of BLOCKED_PORTS) { if (!testPort(node, node, portNum)) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/ec2/EC2RestrictedInbound.ts b/src/rules/ec2/EC2RestrictedInbound.ts index 7e97a08330..0bf0fc2cff 100644 --- a/src/rules/ec2/EC2RestrictedInbound.ts +++ b/src/rules/ec2/EC2RestrictedInbound.ts @@ -5,14 +5,14 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnSecurityGroup, CfnSecurityGroupIngress } from '@aws-cdk/aws-ec2'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * EC2 security groups do not allow for 0.0.0.0/0 or ::/0 inbound access * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnSecurityGroup) { const ingressRules = Stack.of(node).resolve(node.securityGroupIngress); if (ingressRules != undefined) { @@ -24,27 +24,30 @@ export default Object.defineProperty( resolvedRule.cidrIpv6 ); if (resolvedcidrIp != undefined && resolvedcidrIp.includes('/0')) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } if ( resolvedcidrIpv6 != undefined && resolvedcidrIpv6.includes('/0') ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } } + return NagRuleCompliance.COMPLIANT; } else if (node instanceof CfnSecurityGroupIngress) { const resolvedcidrIp = resolveIfPrimitive(node, node.cidrIp); const resolvedcidrIpv6 = resolveIfPrimitive(node, node.cidrIpv6); if (resolvedcidrIp != undefined && resolvedcidrIp.includes('/0')) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } if (resolvedcidrIpv6 != undefined && resolvedcidrIpv6.includes('/0')) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/ec2/EC2RestrictedSSH.ts b/src/rules/ec2/EC2RestrictedSSH.ts index 78398f5862..f6872fb88f 100644 --- a/src/rules/ec2/EC2RestrictedSSH.ts +++ b/src/rules/ec2/EC2RestrictedSSH.ts @@ -5,14 +5,14 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnSecurityGroupIngress, CfnSecurityGroup } from '@aws-cdk/aws-ec2'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Security Groups do not allow for unrestricted SSH traffic * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnSecurityGroup) { const ingressRules = Stack.of(node).resolve(node.securityGroupIngress); if (ingressRules != undefined) { @@ -35,16 +35,17 @@ export default Object.defineProperty( toPort == -1 || ipProtocol == '-1' ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } else { if (fromPort == 22 || ipProtocol == '-1') { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } } } } + return NagRuleCompliance.COMPLIANT; } else if (node instanceof CfnSecurityGroupIngress) { const ipProtocol = resolveIfPrimitive(node, node.ipProtocol); const cidrIp = resolveIfPrimitive(node, node.cidrIp); @@ -63,16 +64,18 @@ export default Object.defineProperty( toPort == -1 || ipProtocol == '-1' ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } else { if (fromPort == 22 || ipProtocol == '-1') { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/ec2/EC2SecurityGroupDescription.ts b/src/rules/ec2/EC2SecurityGroupDescription.ts index bfe8455f2c..b16c345816 100644 --- a/src/rules/ec2/EC2SecurityGroupDescription.ts +++ b/src/rules/ec2/EC2SecurityGroupDescription.ts @@ -5,21 +5,23 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnSecurityGroup } from '@aws-cdk/aws-ec2'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Security Groups have descriptions * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnSecurityGroup) { const description = resolveIfPrimitive(node, node.groupDescription); if (description.length < 2) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/ecr/ECROpenAccess.ts b/src/rules/ecr/ECROpenAccess.ts index d1222ea933..d179773f03 100644 --- a/src/rules/ecr/ECROpenAccess.ts +++ b/src/rules/ecr/ECROpenAccess.ts @@ -5,21 +5,31 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnRegistryPolicy, CfnRepository } from '@aws-cdk/aws-ecr'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * ECR Repositories do not allow open access * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnRegistryPolicy) { const policyText = Stack.of(node).resolve(node.policyText); - return checkStatement(policyText); + const compliant = checkStatement(policyText); + if (compliant !== true) { + return NagRuleCompliance.NON_COMPLIANT; + } + return NagRuleCompliance.COMPLIANT; } else if (node instanceof CfnRepository) { const policyText = Stack.of(node).resolve(node.repositoryPolicyText); - return checkStatement(policyText); + const compliant = checkStatement(policyText); + if (compliant !== true) { + return NagRuleCompliance.NON_COMPLIANT; + } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/ecs/ECSClusterCloudWatchContainerInsights.ts b/src/rules/ecs/ECSClusterCloudWatchContainerInsights.ts index 501dfa09f8..6804be3a9c 100644 --- a/src/rules/ecs/ECSClusterCloudWatchContainerInsights.ts +++ b/src/rules/ecs/ECSClusterCloudWatchContainerInsights.ts @@ -5,18 +5,20 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnCluster } from '@aws-cdk/aws-ecs'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * ECS Cluster has CloudWatch Container Insights Enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnCluster) { if (node.clusterSettings == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const clusterSettings = Stack.of(node).resolve(node.clusterSettings); + let found = false; for (const setting of clusterSettings) { const resolvedSetting = Stack.of(node).resolve(setting); if ( @@ -25,13 +27,17 @@ export default Object.defineProperty( resolvedSetting.value && resolvedSetting.value == 'enabled' ) { - return true; + found = true; + break; } } - return false; + if (!found) { + return NagRuleCompliance.NON_COMPLIANT; + } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/ecs/ECSTaskDefinitionContainerLogging.ts b/src/rules/ecs/ECSTaskDefinitionContainerLogging.ts index 286b7b7870..59e66ff787 100644 --- a/src/rules/ecs/ECSTaskDefinitionContainerLogging.ts +++ b/src/rules/ecs/ECSTaskDefinitionContainerLogging.ts @@ -5,21 +5,21 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnCluster } from '@aws-cdk/aws-ecs'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * ECS Task Definition has awslogs logging enabled at the minimum * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnCluster) { if (node.configuration == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const configuration = Stack.of(node).resolve(node.configuration); if (configuration.executeCommandConfiguration == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const executeCommandConfiguration = resolveIfPrimitive( node, @@ -29,10 +29,12 @@ export default Object.defineProperty( executeCommandConfiguration.logging == undefined || executeCommandConfiguration.logging == 'NONE' ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/ecs/ECSTaskDefinitionUserForHostMode.ts b/src/rules/ecs/ECSTaskDefinitionUserForHostMode.ts index 1590087908..c78d3d4e4d 100644 --- a/src/rules/ecs/ECSTaskDefinitionUserForHostMode.ts +++ b/src/rules/ecs/ECSTaskDefinitionUserForHostMode.ts @@ -5,14 +5,14 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnTaskDefinition, NetworkMode } from '@aws-cdk/aws-ecs'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Containers in ECS task definitions configured for host networking have 'privileged' set to true and a non-empty non-root 'user' * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnTaskDefinition) { if (node.networkMode === NetworkMode.HOST) { const containerDefinitions = Stack.of(node).resolve( @@ -28,20 +28,22 @@ export default Object.defineProperty( ); const user = resolveIfPrimitive(node, resolvedDefinition.user); if (privileged !== true || user === undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const rootIdentifiers = ['root', '0']; const userParts = user.split(':'); for (const userPart of userParts) { if (rootIdentifiers.includes(userPart.toLowerCase())) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } } } } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/efs/EFSEncrypted.ts b/src/rules/efs/EFSEncrypted.ts index 1b95235b1d..b1b5213bb6 100644 --- a/src/rules/efs/EFSEncrypted.ts +++ b/src/rules/efs/EFSEncrypted.ts @@ -5,21 +5,23 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnFileSystem } from '@aws-cdk/aws-efs'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Elastic File Systems are configured for encryption at rest * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnFileSystem) { const encrypted = resolveIfPrimitive(node, node.encrypted); if (encrypted === false) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/efs/EFSInBackupPlan.ts b/src/rules/efs/EFSInBackupPlan.ts index 338012d2f4..94c7bb7cab 100644 --- a/src/rules/efs/EFSInBackupPlan.ts +++ b/src/rules/efs/EFSInBackupPlan.ts @@ -6,7 +6,10 @@ import { parse } from 'path'; import { CfnBackupSelection } from '@aws-cdk/aws-backup'; import { CfnFileSystem } from '@aws-cdk/aws-efs'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveResourceFromInstrinsic } from '../../nag-pack'; +import { + resolveResourceFromInstrinsic, + NagRuleCompliance, +} from '../../nag-pack'; /** * EFSs are part of AWS Backup plan(s) @@ -14,7 +17,7 @@ import { resolveResourceFromInstrinsic } from '../../nag-pack'; */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnFileSystem) { const fileSystemLogicalId = resolveResourceFromInstrinsic(node, node.ref); let found = false; @@ -27,10 +30,12 @@ export default Object.defineProperty( } } if (!found) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/elasticache/ElastiCacheClusterInVPC.ts b/src/rules/elasticache/ElastiCacheClusterInVPC.ts index 3425554f34..0d9b637026 100644 --- a/src/rules/elasticache/ElastiCacheClusterInVPC.ts +++ b/src/rules/elasticache/ElastiCacheClusterInVPC.ts @@ -5,13 +5,14 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnCacheCluster, CfnReplicationGroup } from '@aws-cdk/aws-elasticache'; import { CfnResource } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * ElastiCache clusters are provisioned in a VPC * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if ( node instanceof CfnCacheCluster || node instanceof CfnReplicationGroup @@ -20,10 +21,12 @@ export default Object.defineProperty( node.cacheSubnetGroupName == undefined || node.cacheSubnetGroupName.length == 0 ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/elasticache/ElastiCacheClusterNonDefaultPort.ts b/src/rules/elasticache/ElastiCacheClusterNonDefaultPort.ts index e2f2cb2a37..c896f848d6 100644 --- a/src/rules/elasticache/ElastiCacheClusterNonDefaultPort.ts +++ b/src/rules/elasticache/ElastiCacheClusterNonDefaultPort.ts @@ -5,39 +5,42 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnReplicationGroup, CfnCacheCluster } from '@aws-cdk/aws-elasticache'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * ElastiCache clusters do not use the default endpoint ports * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnCacheCluster) { const port = resolveIfPrimitive(node, node.port); if (port == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const engine = resolveIfPrimitive(node, node.engine); if (engine.toLowerCase() == 'redis' && port == 6379) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } else if (engine.toLowerCase() == 'memcached' && port == 11211) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; } else if (node instanceof CfnReplicationGroup) { const port = resolveIfPrimitive(node, node.port); if (port == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const engine = resolveIfPrimitive(node, node.engine); if ( (engine == undefined || engine.toLowerCase() == 'redis') && port == 6379 ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/elasticache/ElastiCacheRedisClusterAutomaticBackup.ts b/src/rules/elasticache/ElastiCacheRedisClusterAutomaticBackup.ts index 6fb094759a..cf4b225f71 100644 --- a/src/rules/elasticache/ElastiCacheRedisClusterAutomaticBackup.ts +++ b/src/rules/elasticache/ElastiCacheRedisClusterAutomaticBackup.ts @@ -5,27 +5,30 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnCacheCluster, CfnReplicationGroup } from '@aws-cdk/aws-elasticache'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * ElastiCache Redis clusters retain automatic backups for at least 15 days * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnCacheCluster) { const engine = resolveIfPrimitive(node, node.engine.toLowerCase()); const retention = resolveIfPrimitive(node, node.snapshotRetentionLimit); if (engine == 'redis' && (retention == undefined || retention < 15)) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; } else if (node instanceof CfnReplicationGroup) { const retention = resolveIfPrimitive(node, node.snapshotRetentionLimit); if (retention == undefined || retention < 15) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/elasticache/ElastiCacheRedisClusterEncryption.ts b/src/rules/elasticache/ElastiCacheRedisClusterEncryption.ts index 4329ab0440..6ebfb3a034 100644 --- a/src/rules/elasticache/ElastiCacheRedisClusterEncryption.ts +++ b/src/rules/elasticache/ElastiCacheRedisClusterEncryption.ts @@ -5,29 +5,30 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnReplicationGroup } from '@aws-cdk/aws-elasticache'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * ElastiCache Redis clusters have both encryption in transit and at rest enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnReplicationGroup) { if ( node.atRestEncryptionEnabled == undefined || node.transitEncryptionEnabled == undefined ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const rest = resolveIfPrimitive(node, node.atRestEncryptionEnabled); const transit = resolveIfPrimitive(node, node.transitEncryptionEnabled); if (rest == false || transit == false) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/elasticache/ElastiCacheRedisClusterMultiAZ.ts b/src/rules/elasticache/ElastiCacheRedisClusterMultiAZ.ts index a60ff2e92e..6f5acc09c2 100644 --- a/src/rules/elasticache/ElastiCacheRedisClusterMultiAZ.ts +++ b/src/rules/elasticache/ElastiCacheRedisClusterMultiAZ.ts @@ -5,24 +5,26 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnReplicationGroup } from '@aws-cdk/aws-elasticache'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * ElastiCache Redis clusters are deployed in a Multi-AZ configuration * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnReplicationGroup) { if (node.multiAzEnabled == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const multiAz = resolveIfPrimitive(node, node.multiAzEnabled); if (!multiAz) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/elasticache/ElastiCacheRedisClusterRedisAuth.ts b/src/rules/elasticache/ElastiCacheRedisClusterRedisAuth.ts index c1f33f2c1e..92abcf0951 100644 --- a/src/rules/elasticache/ElastiCacheRedisClusterRedisAuth.ts +++ b/src/rules/elasticache/ElastiCacheRedisClusterRedisAuth.ts @@ -5,19 +5,22 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnReplicationGroup } from '@aws-cdk/aws-elasticache'; import { CfnResource } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * ElastiCache Redis clusters use Redis AUTH for user authentication * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnReplicationGroup) { if (node.authToken == undefined || node.authToken.length == 0) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/elasticbeanstalk/ElasticBeanstalkEC2InstanceLogsToS3.ts b/src/rules/elasticbeanstalk/ElasticBeanstalkEC2InstanceLogsToS3.ts index 0c86589c38..b106ace6c9 100644 --- a/src/rules/elasticbeanstalk/ElasticBeanstalkEC2InstanceLogsToS3.ts +++ b/src/rules/elasticbeanstalk/ElasticBeanstalkEC2InstanceLogsToS3.ts @@ -5,6 +5,7 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnEnvironment } from '@aws-cdk/aws-elasticbeanstalk'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * EC2 instances in Elastic Beanstalk environments upload rotated logs to S3 @@ -12,11 +13,11 @@ import { CfnResource, Stack } from '@aws-cdk/core'; * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnEnvironment) { const optionSettings = Stack.of(node).resolve(node.optionSettings); if (optionSettings == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } let foundEnabled = false; for (const optionSetting of optionSettings) { @@ -34,10 +35,12 @@ export default Object.defineProperty( } } if (!foundEnabled) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/elasticbeanstalk/ElasticBeanstalkEnhancedHealthReportingEnabled.ts b/src/rules/elasticbeanstalk/ElasticBeanstalkEnhancedHealthReportingEnabled.ts index 3705836cd5..dd7fd9b41b 100644 --- a/src/rules/elasticbeanstalk/ElasticBeanstalkEnhancedHealthReportingEnabled.ts +++ b/src/rules/elasticbeanstalk/ElasticBeanstalkEnhancedHealthReportingEnabled.ts @@ -5,17 +5,18 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnEnvironment } from '@aws-cdk/aws-elasticbeanstalk'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * Elastic Beanstalk environments have enhanced health reporting enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnEnvironment) { const optionSettings = Stack.of(node).resolve(node.optionSettings); if (optionSettings == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } let found = false; for (const optionSetting of optionSettings) { @@ -33,10 +34,12 @@ export default Object.defineProperty( } } if (!found) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/elasticbeanstalk/ElasticBeanstalkManagedUpdatesEnabled.ts b/src/rules/elasticbeanstalk/ElasticBeanstalkManagedUpdatesEnabled.ts index edb26cff9f..0914b3a7bd 100644 --- a/src/rules/elasticbeanstalk/ElasticBeanstalkManagedUpdatesEnabled.ts +++ b/src/rules/elasticbeanstalk/ElasticBeanstalkManagedUpdatesEnabled.ts @@ -5,6 +5,7 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnEnvironment } from '@aws-cdk/aws-elasticbeanstalk'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * Elastic Beanstalk environments have managed updates enabled @@ -12,11 +13,11 @@ import { CfnResource, Stack } from '@aws-cdk/core'; * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnEnvironment) { const optionSettings = Stack.of(node).resolve(node.optionSettings); if (optionSettings == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } let foundEnabled = false; let foundLevel = false; @@ -46,10 +47,12 @@ export default Object.defineProperty( } } if (!foundEnabled || !foundLevel) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/elasticbeanstalk/ElasticBeanstalkVPCSpecified.ts b/src/rules/elasticbeanstalk/ElasticBeanstalkVPCSpecified.ts index 0d107a0452..239d8691c6 100644 --- a/src/rules/elasticbeanstalk/ElasticBeanstalkVPCSpecified.ts +++ b/src/rules/elasticbeanstalk/ElasticBeanstalkVPCSpecified.ts @@ -5,6 +5,7 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnEnvironment } from '@aws-cdk/aws-elasticbeanstalk'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * Elastic Beanstalk environments are configured to use a specific VPC @@ -12,11 +13,11 @@ import { CfnResource, Stack } from '@aws-cdk/core'; * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnEnvironment) { const optionSettings = Stack.of(node).resolve(node.optionSettings); if (optionSettings == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } let foundEnabled = false; for (const optionSetting of optionSettings) { @@ -34,10 +35,12 @@ export default Object.defineProperty( } } if (!foundEnabled) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/elb/ALBHttpDropInvalidHeaderEnabled.ts b/src/rules/elb/ALBHttpDropInvalidHeaderEnabled.ts index 53e82a3e11..088978d753 100644 --- a/src/rules/elb/ALBHttpDropInvalidHeaderEnabled.ts +++ b/src/rules/elb/ALBHttpDropInvalidHeaderEnabled.ts @@ -5,14 +5,14 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnLoadBalancer } from '@aws-cdk/aws-elasticloadbalancingv2'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Application Load Balancers are enabled to drop invalid headers * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnLoadBalancer) { const type = resolveIfPrimitive(node, node.type); if (type == undefined || type == 'application') { @@ -21,14 +21,16 @@ export default Object.defineProperty( const reg = /"routing\.http\.drop_invalid_header_fields\.enabled","value":"true"/gm; if (JSON.stringify(attributes).search(reg) == -1) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } else { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/elb/ALBHttpToHttpsRedirection.ts b/src/rules/elb/ALBHttpToHttpsRedirection.ts index 06dd7db272..5741e9f987 100644 --- a/src/rules/elb/ALBHttpToHttpsRedirection.ts +++ b/src/rules/elb/ALBHttpToHttpsRedirection.ts @@ -5,14 +5,14 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnListener } from '@aws-cdk/aws-elasticloadbalancingv2'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * ALB HTTP listeners are configured to redirect to HTTPS * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnListener) { let found = false; const protocol = resolveIfPrimitive(node, node.protocol); @@ -27,11 +27,12 @@ export default Object.defineProperty( found = true; } } - if (!found) return false; + if (!found) return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/elb/ALBWAFEnabled.ts b/src/rules/elb/ALBWAFEnabled.ts index 29819cf34f..2fcc063c49 100644 --- a/src/rules/elb/ALBWAFEnabled.ts +++ b/src/rules/elb/ALBWAFEnabled.ts @@ -9,6 +9,7 @@ import { CfnResource, Stack } from '@aws-cdk/core'; import { resolveIfPrimitive, resolveResourceFromInstrinsic, + NagRuleCompliance, } from '../../nag-pack'; /** @@ -17,7 +18,7 @@ import { */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnLoadBalancer) { const type = resolveIfPrimitive(node, node.type); if (type === undefined || type === 'application') { @@ -35,11 +36,13 @@ export default Object.defineProperty( } } if (!found) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/elb/CLBConnectionDraining.ts b/src/rules/elb/CLBConnectionDraining.ts index 1247f5a8f2..019fe561dd 100644 --- a/src/rules/elb/CLBConnectionDraining.ts +++ b/src/rules/elb/CLBConnectionDraining.ts @@ -5,26 +5,28 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnLoadBalancer } from '@aws-cdk/aws-elasticloadbalancing'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * CLBs have connection draining enabled. * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnLoadBalancer) { if (node.connectionDrainingPolicy == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const draining = Stack.of(node).resolve(node.connectionDrainingPolicy); const resolvedDraining = Stack.of(node).resolve(draining); const enabled = resolveIfPrimitive(node, resolvedDraining.enabled); if (enabled !== true) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/elb/CLBNoInboundHttpHttps.ts b/src/rules/elb/CLBNoInboundHttpHttps.ts index 2ddeb3597b..fef16aa9ac 100644 --- a/src/rules/elb/CLBNoInboundHttpHttps.ts +++ b/src/rules/elb/CLBNoInboundHttpHttps.ts @@ -5,14 +5,14 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnLoadBalancer } from '@aws-cdk/aws-elasticloadbalancing'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * CLBs are not used for incoming HTTP/HTTPS traffic. Use ALBs instead. * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnLoadBalancer) { const listeners = Stack.of(node).resolve(node.listeners); for (const listener of listeners) { @@ -22,11 +22,13 @@ export default Object.defineProperty( protocol.toLowerCase() == 'http' || protocol.toLowerCase() == 'https' ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/elb/ELBACMCertificateRequired.ts b/src/rules/elb/ELBACMCertificateRequired.ts index 02334f427e..d123acfaa2 100644 --- a/src/rules/elb/ELBACMCertificateRequired.ts +++ b/src/rules/elb/ELBACMCertificateRequired.ts @@ -5,14 +5,14 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnLoadBalancer } from '@aws-cdk/aws-elasticloadbalancing'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * CLBs use ACM-managed certificates * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnLoadBalancer) { //For each listener, ensure that it's utilizing an ACM SSL/HTTPS cert const listeners = Stack.of(node).resolve(node.listeners); @@ -26,17 +26,19 @@ export default Object.defineProperty( ); //Use the ARN to check if this is an ACM managed cert if (listenerARN == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } else { const acmRegex = /^arn:[^:]+:acm:.+$/; if (!acmRegex.test(listenerARN)) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } } } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/elb/ELBCrossZoneLoadBalancingEnabled.ts b/src/rules/elb/ELBCrossZoneLoadBalancingEnabled.ts index 73a2756b85..d030dc4232 100644 --- a/src/rules/elb/ELBCrossZoneLoadBalancingEnabled.ts +++ b/src/rules/elb/ELBCrossZoneLoadBalancingEnabled.ts @@ -5,34 +5,36 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnLoadBalancer } from '@aws-cdk/aws-elasticloadbalancing'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * CLBs use at least two AZs with the Cross-Zone Load Balancing feature enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnLoadBalancer) { if (node.crossZone == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } if (node.subnets == undefined) { if ( node.availabilityZones == undefined || node.availabilityZones.length < 2 ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } else if (node.subnets.length < 2) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const crossZone = resolveIfPrimitive(node, node.crossZone); if (crossZone != true) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/elb/ELBDeletionProtectionEnabled.ts b/src/rules/elb/ELBDeletionProtectionEnabled.ts index 31b8b9c13f..3a82791bc2 100644 --- a/src/rules/elb/ELBDeletionProtectionEnabled.ts +++ b/src/rules/elb/ELBDeletionProtectionEnabled.ts @@ -5,13 +5,14 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnLoadBalancer } from '@aws-cdk/aws-elasticloadbalancingv2'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * ALBs, NLBs, and GLBs have deletion protection enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnLoadBalancer) { const attributes = Stack.of(node).resolve(node.loadBalancerAttributes); if (attributes != undefined) { @@ -28,13 +29,15 @@ export default Object.defineProperty( } } if (!deletionProtectionEnabled) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } else { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/elb/ELBLoggingEnabled.ts b/src/rules/elb/ELBLoggingEnabled.ts index a9da071d40..65fe8032a7 100644 --- a/src/rules/elb/ELBLoggingEnabled.ts +++ b/src/rules/elb/ELBLoggingEnabled.ts @@ -6,17 +6,17 @@ import { parse } from 'path'; import { CfnLoadBalancer } from '@aws-cdk/aws-elasticloadbalancing'; import { CfnLoadBalancer as CfnLoadBalancerV2 } from '@aws-cdk/aws-elasticloadbalancingv2'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * ELBs have access logs enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnLoadBalancer) { if (node.accessLoggingPolicy == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const accessLoggingPolicy = Stack.of(node).resolve( node.accessLoggingPolicy @@ -24,17 +24,20 @@ export default Object.defineProperty( const enabled = resolveIfPrimitive(node, accessLoggingPolicy.enabled); if (enabled == false) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; } else if (node instanceof CfnLoadBalancerV2) { const attributes = Stack.of(node).resolve(node.loadBalancerAttributes); const reg = /"access_logs\.s3\.enabled","value":"true"/gm; if (JSON.stringify(attributes).search(reg) == -1) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/elb/ELBTlsHttpsListenersOnly.ts b/src/rules/elb/ELBTlsHttpsListenersOnly.ts index 2c2e4470dc..9df192ece6 100644 --- a/src/rules/elb/ELBTlsHttpsListenersOnly.ts +++ b/src/rules/elb/ELBTlsHttpsListenersOnly.ts @@ -5,14 +5,14 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnLoadBalancer } from '@aws-cdk/aws-elasticloadbalancing'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * CLB listeners are configured for secure (HTTPs or SSL) protocols for client communication * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnLoadBalancer) { const listeners = Stack.of(node).resolve(node.listeners); for (const listener of listeners) { @@ -29,7 +29,7 @@ export default Object.defineProperty( instanceProtocol.toLowerCase() == 'ssl' ) ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } else if (protocol.toLowerCase() == 'https') { if ( @@ -38,12 +38,14 @@ export default Object.defineProperty( instanceProtocol.toLowerCase() == 'https' ) ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/elb/ELBv2ACMCertificateRequired.ts b/src/rules/elb/ELBv2ACMCertificateRequired.ts index 483ca47565..16c03bc81a 100644 --- a/src/rules/elb/ELBv2ACMCertificateRequired.ts +++ b/src/rules/elb/ELBv2ACMCertificateRequired.ts @@ -5,17 +5,18 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnListener } from '@aws-cdk/aws-elasticloadbalancingv2'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * ALB, NLB, and GLB listeners use ACM-managed certificates * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnListener) { const certificates = Stack.of(node).resolve(node.certificates); if (certificates == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } let found = false; for (const certificate of certificates) { @@ -26,10 +27,12 @@ export default Object.defineProperty( } } if (!found) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/emr/EMRAuthEC2KeyPairOrKerberos.ts b/src/rules/emr/EMRAuthEC2KeyPairOrKerberos.ts index de0a97a25b..f03af36813 100644 --- a/src/rules/emr/EMRAuthEC2KeyPairOrKerberos.ts +++ b/src/rules/emr/EMRAuthEC2KeyPairOrKerberos.ts @@ -5,13 +5,14 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnCluster } from '@aws-cdk/aws-emr'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * EMR clusters implement authentication via an EC2 Key Pair or Kerberos * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnCluster) { const kerberosAttributes = Stack.of(node).resolve( node.kerberosAttributes @@ -19,11 +20,13 @@ export default Object.defineProperty( if (kerberosAttributes == undefined) { const instanceConfig = Stack.of(node).resolve(node.instances); if (instanceConfig.ec2KeyName == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/emr/EMRKerberosEnabled.ts b/src/rules/emr/EMRKerberosEnabled.ts index 7031c566bc..cd6460385b 100644 --- a/src/rules/emr/EMRKerberosEnabled.ts +++ b/src/rules/emr/EMRKerberosEnabled.ts @@ -5,22 +5,25 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnCluster } from '@aws-cdk/aws-emr'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * EMR clusters have Kerberos enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnCluster) { const kerberosAttributes = Stack.of(node).resolve( node.kerberosAttributes ); if (kerberosAttributes == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/emr/EMRS3AccessLogging.ts b/src/rules/emr/EMRS3AccessLogging.ts index d0bca18086..794f8ba4af 100644 --- a/src/rules/emr/EMRS3AccessLogging.ts +++ b/src/rules/emr/EMRS3AccessLogging.ts @@ -5,20 +5,23 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnCluster } from '@aws-cdk/aws-emr'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * EMR clusters have S3 logging enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnCluster) { const logUri = Stack.of(node).resolve(node.logUri); if (logUri == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/iam/IAMGroupHasUsers.ts b/src/rules/iam/IAMGroupHasUsers.ts index 2419177e55..ad712b5aa8 100644 --- a/src/rules/iam/IAMGroupHasUsers.ts +++ b/src/rules/iam/IAMGroupHasUsers.ts @@ -5,7 +5,10 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnGroup, CfnUser, CfnUserToGroupAddition } from '@aws-cdk/aws-iam'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveResourceFromInstrinsic } from '../../nag-pack'; +import { + resolveResourceFromInstrinsic, + NagRuleCompliance, +} from '../../nag-pack'; /** * IAM Groups have at least one IAM User @@ -13,7 +16,7 @@ import { resolveResourceFromInstrinsic } from '../../nag-pack'; */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnGroup) { const groupLogicalId = resolveResourceFromInstrinsic(node, node.ref); const groupName = Stack.of(node).resolve(node.groupName); @@ -32,10 +35,12 @@ export default Object.defineProperty( } } if (!found) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/iam/IAMNoInlinePolicy.ts b/src/rules/iam/IAMNoInlinePolicy.ts index 0dd6ac030a..72f63f19f1 100644 --- a/src/rules/iam/IAMNoInlinePolicy.ts +++ b/src/rules/iam/IAMNoInlinePolicy.ts @@ -5,13 +5,14 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnRole, CfnUser, CfnGroup, CfnPolicy } from '@aws-cdk/aws-iam'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * IAM Groups, Users, and Roles do not contain inline policies * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if ( node instanceof CfnGroup || node instanceof CfnUser || @@ -19,13 +20,14 @@ export default Object.defineProperty( ) { const inlinePolicies = Stack.of(node).resolve(node.policies); if (inlinePolicies != undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else if (node instanceof CfnPolicy) { + return NagRuleCompliance.NON_COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - if (node instanceof CfnPolicy) { - return false; - } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/iam/IAMNoManagedPolicies.ts b/src/rules/iam/IAMNoManagedPolicies.ts index 373d0b68a2..ff31c664d4 100644 --- a/src/rules/iam/IAMNoManagedPolicies.ts +++ b/src/rules/iam/IAMNoManagedPolicies.ts @@ -5,13 +5,14 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnRole, CfnUser, CfnGroup } from '@aws-cdk/aws-iam'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * IAM users, roles, and groups do not use AWS managed policies * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if ( node instanceof CfnGroup || node instanceof CfnUser || @@ -25,12 +26,14 @@ export default Object.defineProperty( if ( !(/\d{12}/.test(arnPrefix) || arnPrefix.includes('AWS::AccountId')) ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/iam/IAMNoWildcardPermissions.ts b/src/rules/iam/IAMNoWildcardPermissions.ts index f763dfdb56..e59491f0c8 100644 --- a/src/rules/iam/IAMNoWildcardPermissions.ts +++ b/src/rules/iam/IAMNoWildcardPermissions.ts @@ -11,13 +11,14 @@ import { CfnManagedPolicy, } from '@aws-cdk/aws-iam'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * IAM entities with wildcard permissions have a cdk_nag rule suppression with evidence for those permission * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if ( node instanceof CfnGroup || node instanceof CfnUser || @@ -31,17 +32,20 @@ export default Object.defineProperty( resolvedPolicy.policyDocument ); if (JSON.stringify(resolvedPolicyDocument).includes('*')) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } } + return NagRuleCompliance.COMPLIANT; } else if (node instanceof CfnPolicy || node instanceof CfnManagedPolicy) { const policyDocument = Stack.of(node).resolve(node.policyDocument); if (JSON.stringify(policyDocument).includes('*')) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/iam/IAMPolicyNoStatementsWithAdminAccess.ts b/src/rules/iam/IAMPolicyNoStatementsWithAdminAccess.ts index c06ee07855..f9029c902c 100644 --- a/src/rules/iam/IAMPolicyNoStatementsWithAdminAccess.ts +++ b/src/rules/iam/IAMPolicyNoStatementsWithAdminAccess.ts @@ -11,23 +11,27 @@ import { CfnRole, } from '@aws-cdk/aws-iam'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * IAM policies do not grant admin access * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnPolicy || node instanceof CfnManagedPolicy) { if (checkDocument(node, node.policyDocument)) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; } else if (node instanceof CfnGroup || node instanceof CfnRole) { if (node.policies != undefined && checkDocument(node, node.policies)) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/iam/IAMPolicyNoStatementsWithFullAccess.ts b/src/rules/iam/IAMPolicyNoStatementsWithFullAccess.ts index cc22ecee6e..2f453e91dc 100644 --- a/src/rules/iam/IAMPolicyNoStatementsWithFullAccess.ts +++ b/src/rules/iam/IAMPolicyNoStatementsWithFullAccess.ts @@ -11,23 +11,27 @@ import { CfnRole, } from '@aws-cdk/aws-iam'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * IAM policies do not grant full access * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnPolicy || node instanceof CfnManagedPolicy) { if (checkDocument(node, node.policyDocument)) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; } else if (node instanceof CfnGroup || node instanceof CfnRole) { if (node.policies != undefined && checkDocument(node, node.policies)) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/iam/IAMUserGroupMembership.ts b/src/rules/iam/IAMUserGroupMembership.ts index c10b262425..d8d7db715f 100644 --- a/src/rules/iam/IAMUserGroupMembership.ts +++ b/src/rules/iam/IAMUserGroupMembership.ts @@ -5,20 +5,23 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnUser } from '@aws-cdk/aws-iam'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * IAM users are assigned to at least one group * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnUser) { const userGroup = Stack.of(node).resolve(node.groups); if (userGroup == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/iam/IAMUserNoPolicies.ts b/src/rules/iam/IAMUserNoPolicies.ts index 6ddd2e23c9..7295490fa5 100644 --- a/src/rules/iam/IAMUserNoPolicies.ts +++ b/src/rules/iam/IAMUserNoPolicies.ts @@ -5,25 +5,29 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnPolicy, CfnManagedPolicy, CfnUser } from '@aws-cdk/aws-iam'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * IAM policies are not attached at the user level * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnPolicy || node instanceof CfnManagedPolicy) { const policyUsers = Stack.of(node).resolve(node.users); if (policyUsers != undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; } else if (node instanceof CfnUser) { const policies = Stack.of(node).resolve(node.policies); const managedPolicyArns = Stack.of(node).resolve(node.managedPolicyArns); if (policies != undefined || managedPolicyArns != undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/kinesis/KinesisDataAnalyticsFlinkCheckpointing.ts b/src/rules/kinesis/KinesisDataAnalyticsFlinkCheckpointing.ts index df18c3f582..bfc68034aa 100644 --- a/src/rules/kinesis/KinesisDataAnalyticsFlinkCheckpointing.ts +++ b/src/rules/kinesis/KinesisDataAnalyticsFlinkCheckpointing.ts @@ -5,33 +5,33 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnApplicationV2 } from '@aws-cdk/aws-kinesisanalytics'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Kinesis Data Analytics Flink Applications have checkpointing enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnApplicationV2) { if (node.runtimeEnvironment.toLowerCase().startsWith('flink')) { const applicationConfiguration = Stack.of(node).resolve( node.applicationConfiguration ); if (applicationConfiguration == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const flinkApplicationConfiguration = Stack.of(node).resolve( applicationConfiguration.flinkApplicationConfiguration ); if (flinkApplicationConfiguration == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const checkpointConfiguration = Stack.of(node).resolve( flinkApplicationConfiguration.checkpointConfiguration ); if (checkpointConfiguration == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } if ( resolveIfPrimitive(node, checkpointConfiguration.configurationType) == @@ -42,12 +42,14 @@ export default Object.defineProperty( checkpointConfiguration.checkpointingEnabled ); if (!enabled) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/kinesis/KinesisDataFirehoseSSE.ts b/src/rules/kinesis/KinesisDataFirehoseSSE.ts index 0b0c5dfec4..bbf92cf1fb 100644 --- a/src/rules/kinesis/KinesisDataFirehoseSSE.ts +++ b/src/rules/kinesis/KinesisDataFirehoseSSE.ts @@ -5,22 +5,25 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnDeliveryStream } from '@aws-cdk/aws-kinesisfirehose'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * Kinesis Data Firehose delivery stream have server-side encryption enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnDeliveryStream) { const deliveryStreamEncryptionConfigurationInput = Stack.of(node).resolve( node.deliveryStreamEncryptionConfigurationInput ); if (deliveryStreamEncryptionConfigurationInput == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/kinesis/KinesisDataStreamDefaultKeyWhenSSE.ts b/src/rules/kinesis/KinesisDataStreamDefaultKeyWhenSSE.ts index 092b9e64e4..63eef9e1d4 100644 --- a/src/rules/kinesis/KinesisDataStreamDefaultKeyWhenSSE.ts +++ b/src/rules/kinesis/KinesisDataStreamDefaultKeyWhenSSE.ts @@ -5,22 +5,25 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnStream } from '@aws-cdk/aws-kinesis'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * Kinesis Data Streams use the "aws/kinesis" key when server-sided encryption is enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnStream) { const streamEncryption = Stack.of(node).resolve(node.streamEncryption); if (streamEncryption !== undefined) { if (streamEncryption.keyId !== 'alias/aws/kinesis') { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/kinesis/KinesisDataStreamSSE.ts b/src/rules/kinesis/KinesisDataStreamSSE.ts index 7c180eb1c7..c86f01bbf7 100644 --- a/src/rules/kinesis/KinesisDataStreamSSE.ts +++ b/src/rules/kinesis/KinesisDataStreamSSE.ts @@ -5,20 +5,23 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnStream } from '@aws-cdk/aws-kinesis'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * Kinesis Data Streams have server-side encryption enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnStream) { const streamEncryption = Stack.of(node).resolve(node.streamEncryption); if (streamEncryption == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/kms/KMSBackingKeyRotationEnabled.ts b/src/rules/kms/KMSBackingKeyRotationEnabled.ts index bbd4ff5c26..aa53d48be3 100644 --- a/src/rules/kms/KMSBackingKeyRotationEnabled.ts +++ b/src/rules/kms/KMSBackingKeyRotationEnabled.ts @@ -5,14 +5,14 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnKey, KeySpec } from '@aws-cdk/aws-kms'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * KMS Symmetric keys have automatic key rotation enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnKey) { const keySpec = Stack.of(node).resolve(node.keySpec); if (keySpec == undefined || keySpec == KeySpec.SYMMETRIC_DEFAULT) { @@ -21,11 +21,13 @@ export default Object.defineProperty( node.enableKeyRotation ); if (enableKeyRotation !== true) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/lambda/LambdaConcurrency.ts b/src/rules/lambda/LambdaConcurrency.ts index bb9875385c..ed3c5791a7 100644 --- a/src/rules/lambda/LambdaConcurrency.ts +++ b/src/rules/lambda/LambdaConcurrency.ts @@ -5,14 +5,14 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnFunction } from '@aws-cdk/aws-lambda'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Lambda functions are configured with function-level concurrent execution limits * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnFunction) { const reservedConcurrentExecutions = resolveIfPrimitive( node, @@ -22,10 +22,12 @@ export default Object.defineProperty( reservedConcurrentExecutions == undefined || reservedConcurrentExecutions === 0 ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/lambda/LambdaDLQ.ts b/src/rules/lambda/LambdaDLQ.ts index 86c8ec251b..943bd6cbe3 100644 --- a/src/rules/lambda/LambdaDLQ.ts +++ b/src/rules/lambda/LambdaDLQ.ts @@ -5,23 +5,26 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnFunction } from '@aws-cdk/aws-lambda'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * Lambda functions are configured with a dead-letter configuration * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnFunction) { const deadLetterConfig = Stack.of(node).resolve(node.deadLetterConfig); if ( deadLetterConfig == undefined || deadLetterConfig.targetArn == undefined ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/lambda/LambdaInsideVPC.ts b/src/rules/lambda/LambdaInsideVPC.ts index 97fcc554fa..bf3915c49e 100644 --- a/src/rules/lambda/LambdaInsideVPC.ts +++ b/src/rules/lambda/LambdaInsideVPC.ts @@ -5,28 +5,31 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnFunction } from '@aws-cdk/aws-lambda'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * Lambda functions are VPC enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnFunction) { const vpcConfig = Stack.of(node).resolve(node.vpcConfig); if (vpcConfig == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } else { const secgroups = Stack.of(node).resolve(vpcConfig.securityGroupIds); const subnets = Stack.of(node).resolve(vpcConfig.subnetIds); if (secgroups == undefined || secgroups.length == 0) { if (subnets == undefined || subnets.length == 0) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/mediastore/MediaStoreCloudWatchMetricPolicy.ts b/src/rules/mediastore/MediaStoreCloudWatchMetricPolicy.ts index c688eb4fd9..96f7556e59 100644 --- a/src/rules/mediastore/MediaStoreCloudWatchMetricPolicy.ts +++ b/src/rules/mediastore/MediaStoreCloudWatchMetricPolicy.ts @@ -5,28 +5,30 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnContainer } from '@aws-cdk/aws-mediastore'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Media Store containers define metric policies to send metrics to CloudWatch * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnContainer) { const metricPolicy = Stack.of(node).resolve(node.metricPolicy); if (metricPolicy == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const containerLevelMetrics = resolveIfPrimitive( node, metricPolicy.containerLevelMetrics ); if (containerLevelMetrics != 'ENABLED') { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/mediastore/MediaStoreContainerAccessLogging.ts b/src/rules/mediastore/MediaStoreContainerAccessLogging.ts index 911f5a4176..ae182d8cd5 100644 --- a/src/rules/mediastore/MediaStoreContainerAccessLogging.ts +++ b/src/rules/mediastore/MediaStoreContainerAccessLogging.ts @@ -5,24 +5,26 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnContainer } from '@aws-cdk/aws-mediastore'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Media Store containers have container access logging enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnContainer) { const accessLoggingEnabled = resolveIfPrimitive( node, node.accessLoggingEnabled ); if (accessLoggingEnabled !== true) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/mediastore/MediaStoreContainerCORSPolicy.ts b/src/rules/mediastore/MediaStoreContainerCORSPolicy.ts index aad8fc78f5..60796c68ef 100644 --- a/src/rules/mediastore/MediaStoreContainerCORSPolicy.ts +++ b/src/rules/mediastore/MediaStoreContainerCORSPolicy.ts @@ -5,20 +5,23 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnContainer } from '@aws-cdk/aws-mediastore'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * Media Store containers define CORS policies * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnContainer) { const corsPolicy = Stack.of(node).resolve(node.corsPolicy); if (corsPolicy == undefined || corsPolicy.length == 0) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/mediastore/MediaStoreContainerHasContainerPolicy.ts b/src/rules/mediastore/MediaStoreContainerHasContainerPolicy.ts index 80dfcea291..4b0955a540 100644 --- a/src/rules/mediastore/MediaStoreContainerHasContainerPolicy.ts +++ b/src/rules/mediastore/MediaStoreContainerHasContainerPolicy.ts @@ -5,20 +5,23 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnContainer } from '@aws-cdk/aws-mediastore'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * Media Store containers define container policies * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnContainer) { const policy = Stack.of(node).resolve(node.policy); if (policy == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/mediastore/MediaStoreContainerLifecyclePolicy.ts b/src/rules/mediastore/MediaStoreContainerLifecyclePolicy.ts index 4f1bb8583c..6543bcee13 100644 --- a/src/rules/mediastore/MediaStoreContainerLifecyclePolicy.ts +++ b/src/rules/mediastore/MediaStoreContainerLifecyclePolicy.ts @@ -5,20 +5,23 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnContainer } from '@aws-cdk/aws-mediastore'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * Media Store containers define lifecycle policies * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnContainer) { const lifecyclePolicy = Stack.of(node).resolve(node.lifecyclePolicy); if (lifecyclePolicy == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/msk/MSKBrokerLogging.ts b/src/rules/msk/MSKBrokerLogging.ts index 1cb3834d8b..8ce2ecac05 100644 --- a/src/rules/msk/MSKBrokerLogging.ts +++ b/src/rules/msk/MSKBrokerLogging.ts @@ -5,18 +5,18 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnCluster } from '@aws-cdk/aws-msk'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * MSK clusters send broker logs to a supported destination * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnCluster) { const loggingInfo = Stack.of(node).resolve(node.loggingInfo); if (loggingInfo == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const resolvedBrokerLogs = Stack.of(node).resolve(loggingInfo.brokerLogs); let enabled = false; @@ -47,10 +47,12 @@ export default Object.defineProperty( } } if (!enabled) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/msk/MSKBrokerToBrokerTLS.ts b/src/rules/msk/MSKBrokerToBrokerTLS.ts index 1708ab2495..24d6958c00 100644 --- a/src/rules/msk/MSKBrokerToBrokerTLS.ts +++ b/src/rules/msk/MSKBrokerToBrokerTLS.ts @@ -5,14 +5,14 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnCluster } from '@aws-cdk/aws-msk'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * MSK clusters use TLS communication between brokers * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnCluster) { const encryptionInfo = Stack.of(node).resolve(node.encryptionInfo); if (encryptionInfo != undefined) { @@ -25,12 +25,14 @@ export default Object.defineProperty( encryptionInTransit.inCluster ); if (inCluster === false) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/msk/MSKClientToBrokerTLS.ts b/src/rules/msk/MSKClientToBrokerTLS.ts index e3b5816e0b..493fbaa7aa 100644 --- a/src/rules/msk/MSKClientToBrokerTLS.ts +++ b/src/rules/msk/MSKClientToBrokerTLS.ts @@ -3,16 +3,16 @@ Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ import { parse } from 'path'; -import { CfnCluster, ClientBrokerEncryption } from '@aws-cdk/aws-msk'; +import { CfnCluster } from '@aws-cdk/aws-msk'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * MSK clusters only uses TLS communication between clients and brokers * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnCluster) { const encryptionInfo = Stack.of(node).resolve(node.encryptionInfo); if (encryptionInfo != undefined) { @@ -24,16 +24,15 @@ export default Object.defineProperty( node, encryptionInTransit.clientBroker ); - if ( - clientBroker != undefined && - clientBroker != ClientBrokerEncryption.TLS - ) { - return false; + if (clientBroker != undefined && clientBroker != 'TLS') { + return NagRuleCompliance.NON_COMPLIANT; } } } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/neptune/NeptuneClusterAutomaticMinorVersionUpgrade.ts b/src/rules/neptune/NeptuneClusterAutomaticMinorVersionUpgrade.ts index 093861e71a..6406125e1a 100644 --- a/src/rules/neptune/NeptuneClusterAutomaticMinorVersionUpgrade.ts +++ b/src/rules/neptune/NeptuneClusterAutomaticMinorVersionUpgrade.ts @@ -5,27 +5,29 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnDBInstance } from '@aws-cdk/aws-neptune'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Neptune DB instances have Auto Minor Version Upgrade enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnDBInstance) { if (node.autoMinorVersionUpgrade == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const autoMinorVersionUpgrade = resolveIfPrimitive( node, node.autoMinorVersionUpgrade ); if (!autoMinorVersionUpgrade) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/neptune/NeptuneClusterBackupRetentionPeriod.ts b/src/rules/neptune/NeptuneClusterBackupRetentionPeriod.ts index d4f512d7ca..5299894116 100644 --- a/src/rules/neptune/NeptuneClusterBackupRetentionPeriod.ts +++ b/src/rules/neptune/NeptuneClusterBackupRetentionPeriod.ts @@ -5,24 +5,26 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnDBCluster } from '@aws-cdk/aws-neptune'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Neptune DB clusters have a reasonable minimum backup retention period configured * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnDBCluster) { const backupRetentionPeriod = resolveIfPrimitive( node, node.backupRetentionPeriod ); if (backupRetentionPeriod == undefined || backupRetentionPeriod < 7) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/neptune/NeptuneClusterEncryptionAtRest.ts b/src/rules/neptune/NeptuneClusterEncryptionAtRest.ts index da8ce840b3..29a57b0482 100644 --- a/src/rules/neptune/NeptuneClusterEncryptionAtRest.ts +++ b/src/rules/neptune/NeptuneClusterEncryptionAtRest.ts @@ -5,24 +5,26 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnDBCluster } from '@aws-cdk/aws-neptune'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Neptune DB clusters have encryption at rest enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnDBCluster) { if (node.storageEncrypted == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const storageEncrypted = resolveIfPrimitive(node, node.storageEncrypted); if (!storageEncrypted) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/neptune/NeptuneClusterIAMAuth.ts b/src/rules/neptune/NeptuneClusterIAMAuth.ts index 168cf9d01d..9d5a0cd5bd 100644 --- a/src/rules/neptune/NeptuneClusterIAMAuth.ts +++ b/src/rules/neptune/NeptuneClusterIAMAuth.ts @@ -5,24 +5,26 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnDBCluster } from '@aws-cdk/aws-neptune'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Neptune DB clusters have IAM Database Authentication enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnDBCluster) { if (node.iamAuthEnabled == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const iamAuthEnabled = resolveIfPrimitive(node, node.iamAuthEnabled); if (!iamAuthEnabled) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/neptune/NeptuneClusterMultiAZ.ts b/src/rules/neptune/NeptuneClusterMultiAZ.ts index 5cd5c37285..5089e33293 100644 --- a/src/rules/neptune/NeptuneClusterMultiAZ.ts +++ b/src/rules/neptune/NeptuneClusterMultiAZ.ts @@ -5,25 +5,28 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnDBCluster } from '@aws-cdk/aws-neptune'; import { CfnResource } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * Neptune DB clusters are deployed in a Multi-AZ configuration * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnDBCluster) { if (node.dbSubnetGroupName == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } if ( node.availabilityZones != undefined && node.availabilityZones.length < 2 ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/opensearch/OpenSearchAllowlistedIPs.ts b/src/rules/opensearch/OpenSearchAllowlistedIPs.ts index 7ec55a22f6..c0fa86d78c 100644 --- a/src/rules/opensearch/OpenSearchAllowlistedIPs.ts +++ b/src/rules/opensearch/OpenSearchAllowlistedIPs.ts @@ -6,32 +6,35 @@ import { parse } from 'path'; import { CfnDomain as LegacyCfnDomain } from '@aws-cdk/aws-elasticsearch'; import { CfnDomain } from '@aws-cdk/aws-opensearchservice'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * OpenSearch Service domains only grant access via allowlisted IP addresses * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof LegacyCfnDomain || node instanceof CfnDomain) { const accessPolicies = Stack.of(node).resolve(node.accessPolicies); if (accessPolicies === undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const statements = accessPolicies?.Statement; if (statements === undefined || statements.length === 0) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } for (const statement of statements) { if (statement.Effect === 'Allow') { const allowList = statement?.Condition?.IpAddress?.['aws:sourceIp']; if (allowList === undefined || allowList.length === 0) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/opensearch/OpenSearchDedicatedMasterNode.ts b/src/rules/opensearch/OpenSearchDedicatedMasterNode.ts index c92ca1373c..f369e735c5 100644 --- a/src/rules/opensearch/OpenSearchDedicatedMasterNode.ts +++ b/src/rules/opensearch/OpenSearchDedicatedMasterNode.ts @@ -6,42 +6,45 @@ import { parse } from 'path'; import { CfnDomain as LegacyCfnDomain } from '@aws-cdk/aws-elasticsearch'; import { CfnDomain } from '@aws-cdk/aws-opensearchservice'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * OpenSearch Service domains use dedicated master nodes * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof LegacyCfnDomain) { const elasticsearchClusterConfig = Stack.of(node).resolve( node.elasticsearchClusterConfig ); if (elasticsearchClusterConfig == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const dedicatedMasterEnabled = resolveIfPrimitive( node, elasticsearchClusterConfig.dedicatedMasterEnabled ); if (!dedicatedMasterEnabled) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; } else if (node instanceof CfnDomain) { const clusterConfig = Stack.of(node).resolve(node.clusterConfig); if (clusterConfig == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const dedicatedMasterEnabled = resolveIfPrimitive( node, clusterConfig.dedicatedMasterEnabled ); if (!dedicatedMasterEnabled) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/opensearch/OpenSearchEncryptedAtRest.ts b/src/rules/opensearch/OpenSearchEncryptedAtRest.ts index adaacbf1de..89e1ba6e86 100644 --- a/src/rules/opensearch/OpenSearchEncryptedAtRest.ts +++ b/src/rules/opensearch/OpenSearchEncryptedAtRest.ts @@ -6,14 +6,14 @@ import { parse } from 'path'; import { CfnDomain as LegacyCfnDomain } from '@aws-cdk/aws-elasticsearch'; import { CfnDomain } from '@aws-cdk/aws-opensearchservice'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * OpenSearch Service domains have encryption at rest enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof LegacyCfnDomain || node instanceof CfnDomain) { const encryptionAtRestOptions = Stack.of(node).resolve( node.encryptionAtRestOptions @@ -24,13 +24,15 @@ export default Object.defineProperty( encryptionAtRestOptions.enabled ); if (enabled !== true) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } else { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/opensearch/OpenSearchErrorLogsToCloudWatch.ts b/src/rules/opensearch/OpenSearchErrorLogsToCloudWatch.ts index 657f803a53..212197a816 100644 --- a/src/rules/opensearch/OpenSearchErrorLogsToCloudWatch.ts +++ b/src/rules/opensearch/OpenSearchErrorLogsToCloudWatch.ts @@ -6,28 +6,31 @@ import { parse } from 'path'; import { CfnDomain as LegacyCfnDomain } from '@aws-cdk/aws-elasticsearch'; import { CfnDomain } from '@aws-cdk/aws-opensearchservice'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * OpenSearch Service domains stream error logs to CloudWatch Logs * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof LegacyCfnDomain || node instanceof CfnDomain) { const logPublishingOptions = Stack.of(node).resolve( node.logPublishingOptions ); if (logPublishingOptions === undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const resolvedLog = Stack.of(node).resolve( logPublishingOptions?.ES_APPLICATION_LOGS ); if (resolvedLog === undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/opensearch/OpenSearchInVPCOnly.ts b/src/rules/opensearch/OpenSearchInVPCOnly.ts index 4c754eb0a3..cbc01efd47 100644 --- a/src/rules/opensearch/OpenSearchInVPCOnly.ts +++ b/src/rules/opensearch/OpenSearchInVPCOnly.ts @@ -6,24 +6,27 @@ import { parse } from 'path'; import { CfnDomain as LegacyCfnDomain } from '@aws-cdk/aws-elasticsearch'; import { CfnDomain } from '@aws-cdk/aws-opensearchservice'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * OpenSearch Service domains are within VPCs * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof LegacyCfnDomain || node instanceof CfnDomain) { const vpcOptions = Stack.of(node).resolve(node.vpcOptions); if (vpcOptions === undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const subnetIds = Stack.of(node).resolve(vpcOptions.subnetIds); if (subnetIds === undefined || subnetIds.length === 0) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/opensearch/OpenSearchNoUnsignedOrAnonymousAccess.ts b/src/rules/opensearch/OpenSearchNoUnsignedOrAnonymousAccess.ts index c8f332ea00..827f5a6f70 100644 --- a/src/rules/opensearch/OpenSearchNoUnsignedOrAnonymousAccess.ts +++ b/src/rules/opensearch/OpenSearchNoUnsignedOrAnonymousAccess.ts @@ -6,21 +6,22 @@ import { parse } from 'path'; import { CfnDomain as LegacyCfnDomain } from '@aws-cdk/aws-elasticsearch'; import { CfnDomain } from '@aws-cdk/aws-opensearchservice'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * OpenSearch Service domains do not allow for unsigned requests or anonymous access * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof LegacyCfnDomain || node instanceof CfnDomain) { const accessPolicies = Stack.of(node).resolve(node.accessPolicies); if (accessPolicies == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const statements = accessPolicies?.Statement; if (statements == undefined || statements.length == 0) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } for (const statement of statements) { if (statement.Effect == 'Allow') { @@ -28,12 +29,14 @@ export default Object.defineProperty( ? JSON.stringify(statement.Principal) : ''; if (awsString.includes('*')) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/opensearch/OpenSearchNodeToNodeEncryption.ts b/src/rules/opensearch/OpenSearchNodeToNodeEncryption.ts index 0e763f8eae..4895ef2776 100644 --- a/src/rules/opensearch/OpenSearchNodeToNodeEncryption.ts +++ b/src/rules/opensearch/OpenSearchNodeToNodeEncryption.ts @@ -6,14 +6,14 @@ import { parse } from 'path'; import { CfnDomain as LegacyCfnDomain } from '@aws-cdk/aws-elasticsearch'; import { CfnDomain } from '@aws-cdk/aws-opensearchservice'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * OpenSearch Service domains are node-to-node encrypted * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof LegacyCfnDomain || node instanceof CfnDomain) { const encryptedNodeToNode = Stack.of(node).resolve( node.nodeToNodeEncryptionOptions @@ -21,13 +21,15 @@ export default Object.defineProperty( if (encryptedNodeToNode != undefined) { const enabled = resolveIfPrimitive(node, encryptedNodeToNode.enabled); if (enabled !== true) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } else { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/opensearch/OpenSearchSlowLogsToCloudWatch.ts b/src/rules/opensearch/OpenSearchSlowLogsToCloudWatch.ts index 3acd59799a..6911d00426 100644 --- a/src/rules/opensearch/OpenSearchSlowLogsToCloudWatch.ts +++ b/src/rules/opensearch/OpenSearchSlowLogsToCloudWatch.ts @@ -6,20 +6,20 @@ import { parse } from 'path'; import { CfnDomain as LegacyCfnDomain } from '@aws-cdk/aws-elasticsearch'; import { CfnDomain } from '@aws-cdk/aws-opensearchservice'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * OpenSearch Service domains minimally publish SEARCH_SLOW_LOGS and INDEX_SLOW_LOGS to CloudWatch Logs * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof LegacyCfnDomain || node instanceof CfnDomain) { const logPublishingOptions = Stack.of(node).resolve( node.logPublishingOptions ); if (logPublishingOptions == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const requiredSlowLogs = [ logPublishingOptions?.SEARCH_SLOW_LOGS, @@ -28,15 +28,17 @@ export default Object.defineProperty( for (const log of requiredSlowLogs) { const resolvedLog = Stack.of(node).resolve(log); if (resolvedLog == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const enabled = resolveIfPrimitive(node, resolvedLog.enabled); if (!enabled) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/opensearch/OpenSearchZoneAwareness.ts b/src/rules/opensearch/OpenSearchZoneAwareness.ts index 59dd7f0852..93f83e21f0 100644 --- a/src/rules/opensearch/OpenSearchZoneAwareness.ts +++ b/src/rules/opensearch/OpenSearchZoneAwareness.ts @@ -6,42 +6,45 @@ import { parse } from 'path'; import { CfnDomain as LegacyCfnDomain } from '@aws-cdk/aws-elasticsearch'; import { CfnDomain } from '@aws-cdk/aws-opensearchservice'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * OpenSearch Service domains have Zone Awareness enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof LegacyCfnDomain) { const elasticsearchClusterConfig = Stack.of(node).resolve( node.elasticsearchClusterConfig ); if (elasticsearchClusterConfig == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const zoneAwarenessEnabled = resolveIfPrimitive( node, elasticsearchClusterConfig.zoneAwarenessEnabled ); if (!zoneAwarenessEnabled) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; } else if (node instanceof CfnDomain) { const clusterConfig = Stack.of(node).resolve(node.clusterConfig); if (clusterConfig == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const zoneAwarenessEnabled = resolveIfPrimitive( node, clusterConfig.zoneAwarenessEnabled ); if (!zoneAwarenessEnabled) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/quicksight/QuicksightSSLConnections.ts b/src/rules/quicksight/QuicksightSSLConnections.ts index e09a8b4fee..bf4e7a8b7c 100644 --- a/src/rules/quicksight/QuicksightSSLConnections.ts +++ b/src/rules/quicksight/QuicksightSSLConnections.ts @@ -5,24 +5,26 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnDataSource } from '@aws-cdk/aws-quicksight'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Quicksight uses SSL when connecting to a data source * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnDataSource) { const sslProperties = Stack.of(node).resolve(node.sslProperties); if (sslProperties != undefined) { const disableSsl = resolveIfPrimitive(node, sslProperties.disableSsl); if (disableSsl === true) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/rds/AuroraMySQLBacktrack.ts b/src/rules/rds/AuroraMySQLBacktrack.ts index 1c6c144481..fcd1b2f378 100644 --- a/src/rules/rds/AuroraMySQLBacktrack.ts +++ b/src/rules/rds/AuroraMySQLBacktrack.ts @@ -5,24 +5,26 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnDBCluster } from '@aws-cdk/aws-rds'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * RDS Aurora serverless clusters have Log Exports enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnDBCluster) { const engine = resolveIfPrimitive(node, node.engine).toLowerCase(); const backtrackWindow = resolveIfPrimitive(node, node.backtrackWindow); if (engine == 'aurora' || engine == 'aurora-mysql') { if (backtrackWindow == undefined || backtrackWindow == 0) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/rds/AuroraMySQLLogging.ts b/src/rules/rds/AuroraMySQLLogging.ts index ea918aa0a5..b9188085d8 100644 --- a/src/rules/rds/AuroraMySQLLogging.ts +++ b/src/rules/rds/AuroraMySQLLogging.ts @@ -5,14 +5,14 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnDBCluster } from '@aws-cdk/aws-rds'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * RDS Aurora MySQL serverless clusters have audit, error, general, and slowquery Log Exports enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnDBCluster) { const engine = resolveIfPrimitive(node, node.engine).toLowerCase(); const engineMode = resolveIfPrimitive( @@ -26,16 +26,21 @@ export default Object.defineProperty( engine.toLowerCase() == 'aurora-mysql') ) { if (node.enableCloudwatchLogsExports == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const needed = ['audit', 'error', 'general', 'slowquery']; const exports = node.enableCloudwatchLogsExports.map((i) => { return i.toLowerCase(); }); - return needed.every((i) => exports.includes(i)); + const compliant = needed.every((i) => exports.includes(i)); + if (compliant !== true) { + return NagRuleCompliance.NON_COMPLIANT; + } } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/rds/AuroraMySQLPostgresIAMAuth.ts b/src/rules/rds/AuroraMySQLPostgresIAMAuth.ts index 7625ba1296..887c42aeda 100644 --- a/src/rules/rds/AuroraMySQLPostgresIAMAuth.ts +++ b/src/rules/rds/AuroraMySQLPostgresIAMAuth.ts @@ -5,30 +5,31 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnDBCluster } from '@aws-cdk/aws-rds'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * RDS Aurora MySQL/PostgresSQL clusters have IAM Database Authentication enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnDBCluster) { if (node.engine.toLowerCase().includes('aurora')) { if (node.enableIamDatabaseAuthentication == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const iamAuth = resolveIfPrimitive( node, node.enableIamDatabaseAuthentication ); if (iamAuth == false) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/rds/RDSAutomaticMinorVersionUpgradeEnabled.ts b/src/rules/rds/RDSAutomaticMinorVersionUpgradeEnabled.ts index f12d377fc3..d5821a81db 100644 --- a/src/rules/rds/RDSAutomaticMinorVersionUpgradeEnabled.ts +++ b/src/rules/rds/RDSAutomaticMinorVersionUpgradeEnabled.ts @@ -5,24 +5,26 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnDBInstance } from '@aws-cdk/aws-rds'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * RDS DB instances have automatic minor version upgrades enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnDBInstance) { const autoMinorVersionUpgrade = resolveIfPrimitive( node, node.autoMinorVersionUpgrade ); if (autoMinorVersionUpgrade === false) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/rds/RDSEnhancedMonitoringEnabled.ts b/src/rules/rds/RDSEnhancedMonitoringEnabled.ts index 935c608fe3..a9296f65d9 100644 --- a/src/rules/rds/RDSEnhancedMonitoringEnabled.ts +++ b/src/rules/rds/RDSEnhancedMonitoringEnabled.ts @@ -5,24 +5,26 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnDBInstance } from '@aws-cdk/aws-rds'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * RDS DB instances have enhanced monitoring enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnDBInstance) { const enhancedMonitoring = resolveIfPrimitive( node, node.monitoringInterval ); if (enhancedMonitoring == undefined || enhancedMonitoring <= 0) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/rds/RDSInBackupPlan.ts b/src/rules/rds/RDSInBackupPlan.ts index 2b556c0d71..5503dc6384 100644 --- a/src/rules/rds/RDSInBackupPlan.ts +++ b/src/rules/rds/RDSInBackupPlan.ts @@ -6,7 +6,10 @@ import { parse } from 'path'; import { CfnBackupSelection } from '@aws-cdk/aws-backup'; import { CfnDBInstance } from '@aws-cdk/aws-rds'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveResourceFromInstrinsic } from '../../nag-pack'; +import { + resolveResourceFromInstrinsic, + NagRuleCompliance, +} from '../../nag-pack'; /** * RDS DB Instances are part of AWS Backup plan(s) @@ -14,7 +17,7 @@ import { resolveResourceFromInstrinsic } from '../../nag-pack'; */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnDBInstance) { const dbLogicalId = resolveResourceFromInstrinsic(node, node.ref); let found = false; @@ -27,10 +30,12 @@ export default Object.defineProperty( } } if (!found) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/rds/RDSInstanceBackupEnabled.ts b/src/rules/rds/RDSInstanceBackupEnabled.ts index 511bf0c504..1d69588ff3 100644 --- a/src/rules/rds/RDSInstanceBackupEnabled.ts +++ b/src/rules/rds/RDSInstanceBackupEnabled.ts @@ -5,21 +5,23 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnDBInstance } from '@aws-cdk/aws-rds'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * RDS DB instances have backup enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnDBInstance) { const backup = resolveIfPrimitive(node, node.backupRetentionPeriod); if (backup !== undefined && backup <= 0) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/rds/RDSInstanceDeletionProtectionEnabled.ts b/src/rules/rds/RDSInstanceDeletionProtectionEnabled.ts index b90ecb2f79..63fda244be 100644 --- a/src/rules/rds/RDSInstanceDeletionProtectionEnabled.ts +++ b/src/rules/rds/RDSInstanceDeletionProtectionEnabled.ts @@ -5,26 +5,26 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnDBCluster, CfnDBInstance } from '@aws-cdk/aws-rds'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * RDS DB instances and Aurora DB clusters have Deletion Protection enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnDBCluster) { if (node.deletionProtection == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const deletionProtection = resolveIfPrimitive( node, node.deletionProtection ); if (deletionProtection == false) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } - return true; + return NagRuleCompliance.COMPLIANT; } else if (node instanceof CfnDBInstance) { const deletionProtection = resolveIfPrimitive( node, @@ -35,11 +35,12 @@ export default Object.defineProperty( (deletionProtection == false || deletionProtection == undefined) && (engine == undefined || !engine.toLowerCase().includes('aurora')) ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } - return true; + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/rds/RDSInstancePublicAccess.ts b/src/rules/rds/RDSInstancePublicAccess.ts index a0c8764ee1..f66bbceccf 100644 --- a/src/rules/rds/RDSInstancePublicAccess.ts +++ b/src/rules/rds/RDSInstancePublicAccess.ts @@ -5,22 +5,23 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnDBInstance } from '@aws-cdk/aws-rds'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * RDS DB instances are not publicly accessible * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnDBInstance) { const publicAccess = resolveIfPrimitive(node, node.publiclyAccessible); if (publicAccess !== false) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } - return true; + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/rds/RDSLoggingEnabled.ts b/src/rules/rds/RDSLoggingEnabled.ts index 14d6a14d46..d216762290 100644 --- a/src/rules/rds/RDSLoggingEnabled.ts +++ b/src/rules/rds/RDSLoggingEnabled.ts @@ -5,21 +5,21 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnDBInstance } from '@aws-cdk/aws-rds'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * RDS DB instances are configured to export all possible log types to CloudWatch * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnDBInstance) { const dbType = JSON.stringify(resolveIfPrimitive(node, node.engine)); const dbLogs = JSON.stringify( Stack.of(node).resolve(node.enableCloudwatchLogsExports) ); if (dbLogs == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } if (dbType.includes('mariadb') || dbType.includes('mysql')) { @@ -31,12 +31,12 @@ export default Object.defineProperty( dbLogs.includes('slowquery') ) ) - return false; + return NagRuleCompliance.NON_COMPLIANT; } if (dbType.includes('postgres')) { if (!(dbLogs.includes('postgresql') && dbLogs.includes('upgrade'))) - return false; + return NagRuleCompliance.NON_COMPLIANT; } if (dbType.includes('oracle')) { @@ -49,15 +49,17 @@ export default Object.defineProperty( dbLogs.includes('trace') ) ) - return false; + return NagRuleCompliance.NON_COMPLIANT; } if (dbType.includes('sqlserver')) { if (!(dbLogs.includes('agent') && dbLogs.includes('error'))) - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/rds/RDSMultiAZSupport.ts b/src/rules/rds/RDSMultiAZSupport.ts index a4e5c0ad0d..9cbe1783e5 100644 --- a/src/rules/rds/RDSMultiAZSupport.ts +++ b/src/rules/rds/RDSMultiAZSupport.ts @@ -5,14 +5,14 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnDBInstance } from '@aws-cdk/aws-rds'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Non-Aurora RDS DB instances have multi-AZ support enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnDBInstance) { const multiAz = resolveIfPrimitive(node, node.multiAz); const engine = resolveIfPrimitive(node, node.engine); @@ -20,10 +20,12 @@ export default Object.defineProperty( !multiAz && (engine == undefined || !engine.toLowerCase().includes('aurora')) ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/rds/RDSNonDefaultPort.ts b/src/rules/rds/RDSNonDefaultPort.ts index 909f86e044..9e71140f40 100644 --- a/src/rules/rds/RDSNonDefaultPort.ts +++ b/src/rules/rds/RDSNonDefaultPort.ts @@ -5,17 +5,17 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnDBCluster, CfnDBInstance } from '@aws-cdk/aws-rds'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * RDS DB instances and Aurora DB clusters do not use the default endpoint ports * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnDBCluster) { if (node.port == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const port = resolveIfPrimitive(node, node.port); const engine = resolveIfPrimitive(node, node.engine).toLowerCase(); @@ -25,41 +25,42 @@ export default Object.defineProperty( engineMode.toLowerCase() == 'provisioned' ) { if (engine.includes('aurora') && port == 3306) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } else if ( (engine == 'aurora' || engine == 'aurora-mysql') && port == 3306 ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } else if (engine == 'aurora-postgresql' && port == 5432) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } - return true; + return NagRuleCompliance.COMPLIANT; } else if (node instanceof CfnDBInstance) { if (node.engine == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const port = resolveIfPrimitive(node, node.port); const engine = resolveIfPrimitive(node, node.engine).toLowerCase(); if (port == undefined) { if (!engine.includes('aurora')) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } else { if ((engine == 'mariadb' || engine == 'mysql') && port == 3306) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } else if (engine == 'postgres' && port == 5432) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } else if (engine.includes('oracle') && port == 1521) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } else if (engine.includes('sqlserver') && port == 1433) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } - return true; + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/rds/RDSStorageEncrypted.ts b/src/rules/rds/RDSStorageEncrypted.ts index 5f44653267..6088dd2da5 100644 --- a/src/rules/rds/RDSStorageEncrypted.ts +++ b/src/rules/rds/RDSStorageEncrypted.ts @@ -5,22 +5,23 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnDBCluster, CfnDBInstance } from '@aws-cdk/aws-rds'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * RDS DB instances and Aurora DB clusters have storage encryption enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnDBCluster) { if (node.storageEncrypted == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const encrypted = resolveIfPrimitive(node, node.storageEncrypted); if (encrypted == false) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; } else if (node instanceof CfnDBInstance) { const encrypted = resolveIfPrimitive(node, node.storageEncrypted); if ( @@ -28,10 +29,12 @@ export default Object.defineProperty( (node.engine == undefined || !node.engine.toLowerCase().includes('aurora')) ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/redshift/RedshiftBackupEnabled.ts b/src/rules/redshift/RedshiftBackupEnabled.ts index 797260cc97..bfd71294f1 100644 --- a/src/rules/redshift/RedshiftBackupEnabled.ts +++ b/src/rules/redshift/RedshiftBackupEnabled.ts @@ -5,14 +5,14 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnCluster } from '@aws-cdk/aws-redshift'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Redshift clusters have automated snapshots enabled and the retention period is between 1 and 35 days * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnCluster) { const automatedSnapshotRetentionPeriod = resolveIfPrimitive( node, @@ -22,10 +22,12 @@ export default Object.defineProperty( automatedSnapshotRetentionPeriod != undefined && automatedSnapshotRetentionPeriod == 0 ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/redshift/RedshiftClusterAuditLogging.ts b/src/rules/redshift/RedshiftClusterAuditLogging.ts index 6fae297e75..a76d015c8b 100644 --- a/src/rules/redshift/RedshiftClusterAuditLogging.ts +++ b/src/rules/redshift/RedshiftClusterAuditLogging.ts @@ -5,20 +5,23 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnCluster } from '@aws-cdk/aws-redshift'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * Redshift clusters have audit logging enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnCluster) { const loggingProperties = Stack.of(node).resolve(node.loggingProperties); if (loggingProperties == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/redshift/RedshiftClusterConfiguration.ts b/src/rules/redshift/RedshiftClusterConfiguration.ts index 71a0bfdf39..807cff4ca9 100644 --- a/src/rules/redshift/RedshiftClusterConfiguration.ts +++ b/src/rules/redshift/RedshiftClusterConfiguration.ts @@ -5,22 +5,24 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnCluster } from '@aws-cdk/aws-redshift'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Redshift clusters have encryption and audit logging enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnCluster) { const encrypted = resolveIfPrimitive(node, node.encrypted); const loggingProperties = Stack.of(node).resolve(node.loggingProperties); if (!encrypted || loggingProperties == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/redshift/RedshiftClusterEncryptionAtRest.ts b/src/rules/redshift/RedshiftClusterEncryptionAtRest.ts index 5afa18429d..28fe7275a2 100644 --- a/src/rules/redshift/RedshiftClusterEncryptionAtRest.ts +++ b/src/rules/redshift/RedshiftClusterEncryptionAtRest.ts @@ -5,24 +5,26 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnCluster } from '@aws-cdk/aws-redshift'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Redshift clusters have encryption at rest enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnCluster) { if (node.encrypted == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const encrypted = resolveIfPrimitive(node, node.encrypted); if (!encrypted) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/redshift/RedshiftClusterInVPC.ts b/src/rules/redshift/RedshiftClusterInVPC.ts index 3ef4712698..ae91770449 100644 --- a/src/rules/redshift/RedshiftClusterInVPC.ts +++ b/src/rules/redshift/RedshiftClusterInVPC.ts @@ -5,22 +5,25 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnCluster } from '@aws-cdk/aws-redshift'; import { CfnResource } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * Redshift clusters are provisioned in a VPC * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnCluster) { if ( node.clusterSubnetGroupName == undefined || node.clusterSubnetGroupName.length == 0 ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/redshift/RedshiftClusterMaintenanceSettings.ts b/src/rules/redshift/RedshiftClusterMaintenanceSettings.ts index e7daeb5a3b..e6b8689543 100644 --- a/src/rules/redshift/RedshiftClusterMaintenanceSettings.ts +++ b/src/rules/redshift/RedshiftClusterMaintenanceSettings.ts @@ -5,14 +5,14 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnCluster } from '@aws-cdk/aws-redshift'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Redshift clusters have version upgrades enabled, automated snapshot retention periods enabled, and explicit maintenance windows configured * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnCluster) { const allowVersionUpgrade = resolveIfPrimitive( node, @@ -28,10 +28,12 @@ export default Object.defineProperty( node.preferredMaintenanceWindow == undefined || allowVersionUpgrade === false ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/redshift/RedshiftClusterNonDefaultPort.ts b/src/rules/redshift/RedshiftClusterNonDefaultPort.ts index 33389b2975..1d7a584215 100644 --- a/src/rules/redshift/RedshiftClusterNonDefaultPort.ts +++ b/src/rules/redshift/RedshiftClusterNonDefaultPort.ts @@ -5,21 +5,23 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnCluster } from '@aws-cdk/aws-redshift'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Redshift clusters do not use the default endpoint port * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnCluster) { const port = resolveIfPrimitive(node, node.port); if (port == undefined || port == 5439) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/redshift/RedshiftClusterNonDefaultUsername.ts b/src/rules/redshift/RedshiftClusterNonDefaultUsername.ts index 0f09832e44..ed5b6e8eb7 100644 --- a/src/rules/redshift/RedshiftClusterNonDefaultUsername.ts +++ b/src/rules/redshift/RedshiftClusterNonDefaultUsername.ts @@ -5,21 +5,23 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnCluster } from '@aws-cdk/aws-redshift'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Redshift clusters use custom user names vice the default (awsuser) * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnCluster) { const masterUsername = resolveIfPrimitive(node, node.masterUsername); if (masterUsername == 'awsuser') { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/redshift/RedshiftClusterPublicAccess.ts b/src/rules/redshift/RedshiftClusterPublicAccess.ts index 92301899d4..705a5eac37 100644 --- a/src/rules/redshift/RedshiftClusterPublicAccess.ts +++ b/src/rules/redshift/RedshiftClusterPublicAccess.ts @@ -5,21 +5,23 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnCluster } from '@aws-cdk/aws-redshift'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Redshift clusters do not allow public access * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnCluster) { const publicAccess = resolveIfPrimitive(node, node.publiclyAccessible); if (publicAccess === true) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/redshift/RedshiftClusterUserActivityLogging.ts b/src/rules/redshift/RedshiftClusterUserActivityLogging.ts index 5c66ef604c..5b3da7c93d 100644 --- a/src/rules/redshift/RedshiftClusterUserActivityLogging.ts +++ b/src/rules/redshift/RedshiftClusterUserActivityLogging.ts @@ -5,21 +5,24 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnCluster, CfnClusterParameterGroup } from '@aws-cdk/aws-redshift'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveResourceFromInstrinsic } from '../../nag-pack'; +import { + resolveResourceFromInstrinsic, + NagRuleCompliance, +} from '../../nag-pack'; /** * Redshift clusters have user user activity logging enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnCluster) { const clusterParameterGroupName = resolveResourceFromInstrinsic( node, node.clusterParameterGroupName ); if (clusterParameterGroupName === undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } let found = false; for (const child of Stack.of(node).node.findAll()) { @@ -35,10 +38,12 @@ export default Object.defineProperty( } } if (!found) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/redshift/RedshiftClusterVersionUpgrade.ts b/src/rules/redshift/RedshiftClusterVersionUpgrade.ts index 4166e6033e..99377b0188 100644 --- a/src/rules/redshift/RedshiftClusterVersionUpgrade.ts +++ b/src/rules/redshift/RedshiftClusterVersionUpgrade.ts @@ -5,24 +5,26 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnCluster } from '@aws-cdk/aws-redshift'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Redshift clusters have version upgrade enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnCluster) { const allowVersionUpgrade = resolveIfPrimitive( node, node.allowVersionUpgrade ); if (allowVersionUpgrade === false) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/redshift/RedshiftEnhancedVPCRoutingEnabled.ts b/src/rules/redshift/RedshiftEnhancedVPCRoutingEnabled.ts index 0852932cfc..6613310eb0 100644 --- a/src/rules/redshift/RedshiftEnhancedVPCRoutingEnabled.ts +++ b/src/rules/redshift/RedshiftEnhancedVPCRoutingEnabled.ts @@ -5,24 +5,26 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnCluster } from '@aws-cdk/aws-redshift'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Redshift clusters have enhanced VPC routing enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnCluster) { const enhancedVpcRouting = resolveIfPrimitive( node, node.enhancedVpcRouting ); if (enhancedVpcRouting !== true) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/redshift/RedshiftRequireTlsSSL.ts b/src/rules/redshift/RedshiftRequireTlsSSL.ts index d54fb33081..a8bcbbb164 100644 --- a/src/rules/redshift/RedshiftRequireTlsSSL.ts +++ b/src/rules/redshift/RedshiftRequireTlsSSL.ts @@ -5,7 +5,10 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnCluster, CfnClusterParameterGroup } from '@aws-cdk/aws-redshift'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveResourceFromInstrinsic } from '../../nag-pack'; +import { + resolveResourceFromInstrinsic, + NagRuleCompliance, +} from '../../nag-pack'; /** * Redshift clusters require TLS/SSL encryption @@ -13,14 +16,14 @@ import { resolveResourceFromInstrinsic } from '../../nag-pack'; */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnCluster) { const clusterParameterGroupName = resolveResourceFromInstrinsic( node, node.clusterParameterGroupName ); if (clusterParameterGroupName === undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } let found = false; for (const child of Stack.of(node).node.findAll()) { @@ -32,10 +35,12 @@ export default Object.defineProperty( } } if (!found) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/s3/S3BucketDefaultLockEnabled.ts b/src/rules/s3/S3BucketDefaultLockEnabled.ts index dbb5e4316b..a59f0dc6ec 100644 --- a/src/rules/s3/S3BucketDefaultLockEnabled.ts +++ b/src/rules/s3/S3BucketDefaultLockEnabled.ts @@ -5,14 +5,14 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnBucket } from '@aws-cdk/aws-s3'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * S3 Buckets have object lock enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnBucket) { const objectLockEnabled = resolveIfPrimitive( node, @@ -27,10 +27,12 @@ export default Object.defineProperty( resolveIfPrimitive(node, objectLockConfiguration.objectLockEnabled) !== 'Enabled' ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/s3/S3BucketLevelPublicAccessProhibited.ts b/src/rules/s3/S3BucketLevelPublicAccessProhibited.ts index c6c20ff1d2..215aafb31b 100644 --- a/src/rules/s3/S3BucketLevelPublicAccessProhibited.ts +++ b/src/rules/s3/S3BucketLevelPublicAccessProhibited.ts @@ -5,17 +5,17 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnBucket } from '@aws-cdk/aws-s3'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * S3 Buckets prohibit public access through bucket level settings * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnBucket) { if (node.publicAccessBlockConfiguration == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const publicAccess = Stack.of(node).resolve( node.publicAccessBlockConfiguration @@ -42,10 +42,12 @@ export default Object.defineProperty( ignorePublicAcls !== true || restrictPublicBuckets !== true ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/s3/S3BucketLoggingEnabled.ts b/src/rules/s3/S3BucketLoggingEnabled.ts index c65191910c..7d4e4faa15 100644 --- a/src/rules/s3/S3BucketLoggingEnabled.ts +++ b/src/rules/s3/S3BucketLoggingEnabled.ts @@ -5,13 +5,14 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnBucket } from '@aws-cdk/aws-s3'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * S3 Buckets have server access logs enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnBucket) { const logging = Stack.of(node).resolve(node.loggingConfiguration); if ( @@ -19,10 +20,12 @@ export default Object.defineProperty( (logging.destinationBucketName == undefined && logging.logFilePrefix == undefined) ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/s3/S3BucketPublicReadProhibited.ts b/src/rules/s3/S3BucketPublicReadProhibited.ts index ab93ad417e..d540c7ca00 100644 --- a/src/rules/s3/S3BucketPublicReadProhibited.ts +++ b/src/rules/s3/S3BucketPublicReadProhibited.ts @@ -5,14 +5,14 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnBucket } from '@aws-cdk/aws-s3'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * S3 Buckets prohibit public read access through their Block Public Access configurations and bucket ACLs * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnBucket) { const publicAccessBlockConfiguration = Stack.of(node).resolve( node.publicAccessBlockConfiguration @@ -24,7 +24,7 @@ export default Object.defineProperty( publicAccessBlockConfiguration.blockPublicPolicy ) !== true ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const accessControl = resolveIfPrimitive(node, node.accessControl); const blockPublicAcls = resolveIfPrimitive( @@ -36,10 +36,12 @@ export default Object.defineProperty( accessControl === 'PublicReadWrite') && blockPublicAcls !== true ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/s3/S3BucketPublicWriteProhibited.ts b/src/rules/s3/S3BucketPublicWriteProhibited.ts index efdd54bcff..9a569a9fdd 100644 --- a/src/rules/s3/S3BucketPublicWriteProhibited.ts +++ b/src/rules/s3/S3BucketPublicWriteProhibited.ts @@ -5,14 +5,14 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnBucket } from '@aws-cdk/aws-s3'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * S3 Buckets prohibit public write access through their Block Public Access configurations and bucket ACLs * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnBucket) { const publicAccessBlockConfiguration = Stack.of(node).resolve( node.publicAccessBlockConfiguration @@ -24,7 +24,7 @@ export default Object.defineProperty( publicAccessBlockConfiguration.blockPublicPolicy ) !== true ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const accessControl = resolveIfPrimitive(node, node.accessControl); const blockPublicAcls = resolveIfPrimitive( @@ -32,10 +32,12 @@ export default Object.defineProperty( publicAccessBlockConfiguration.blockPublicAcls ); if (accessControl === 'PublicReadWrite' && blockPublicAcls !== true) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/s3/S3BucketReplicationEnabled.ts b/src/rules/s3/S3BucketReplicationEnabled.ts index 8cd4dd2219..87b43ae7bb 100644 --- a/src/rules/s3/S3BucketReplicationEnabled.ts +++ b/src/rules/s3/S3BucketReplicationEnabled.ts @@ -5,17 +5,18 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnBucket } from '@aws-cdk/aws-s3'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * S3 Buckets have replication enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnBucket) { const replication = Stack.of(node).resolve(node.replicationConfiguration); if (replication === undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const rules = Stack.of(node).resolve(replication.rules); let found = false; @@ -27,10 +28,12 @@ export default Object.defineProperty( } } if (!found) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/s3/S3BucketServerSideEncryptionEnabled.ts b/src/rules/s3/S3BucketServerSideEncryptionEnabled.ts index aaa5665708..455bb1bebc 100644 --- a/src/rules/s3/S3BucketServerSideEncryptionEnabled.ts +++ b/src/rules/s3/S3BucketServerSideEncryptionEnabled.ts @@ -5,21 +5,21 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnBucket } from '@aws-cdk/aws-s3'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * S3 Buckets have default server-side encryption enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnBucket) { if (node.bucketEncryption == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const encryption = Stack.of(node).resolve(node.bucketEncryption); if (encryption.serverSideEncryptionConfiguration == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const sse = Stack.of(node).resolve( encryption.serverSideEncryptionConfiguration @@ -29,7 +29,7 @@ export default Object.defineProperty( rule.serverSideEncryptionByDefault ); if (defaultEncryption == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const sseAlgorithm = resolveIfPrimitive( node, @@ -39,11 +39,13 @@ export default Object.defineProperty( sseAlgorithm.toLowerCase() != 'aes256' && sseAlgorithm.toLowerCase() != 'aws:kms' ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/s3/S3BucketVersioningEnabled.ts b/src/rules/s3/S3BucketVersioningEnabled.ts index ef5d977adb..d16b328826 100644 --- a/src/rules/s3/S3BucketVersioningEnabled.ts +++ b/src/rules/s3/S3BucketVersioningEnabled.ts @@ -5,14 +5,14 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnBucket } from '@aws-cdk/aws-s3'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * S3 Buckets have versioningConfiguration enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnBucket) { const versioningConfiguration = Stack.of(node).resolve( node.versioningConfiguration @@ -21,10 +21,12 @@ export default Object.defineProperty( versioningConfiguration === undefined || resolveIfPrimitive(node, versioningConfiguration.status) === 'Suspended' ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/s3/S3DefaultEncryptionKMS.ts b/src/rules/s3/S3DefaultEncryptionKMS.ts index c5fb114f8c..69e40ff356 100644 --- a/src/rules/s3/S3DefaultEncryptionKMS.ts +++ b/src/rules/s3/S3DefaultEncryptionKMS.ts @@ -5,21 +5,21 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnBucket } from '@aws-cdk/aws-s3'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * S3 Buckets are encrypted with a KMS Key by default * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnBucket) { if (node.bucketEncryption == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const encryption = Stack.of(node).resolve(node.bucketEncryption); if (encryption.serverSideEncryptionConfiguration == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const sse = Stack.of(node).resolve( encryption.serverSideEncryptionConfiguration @@ -29,18 +29,20 @@ export default Object.defineProperty( rule.serverSideEncryptionByDefault ); if (defaultEncryption == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const sseAlgorithm = resolveIfPrimitive( node, defaultEncryption.sseAlgorithm ); if (sseAlgorithm.toLowerCase() != 'aws:kms') { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/sagemaker/SageMakerEndpointConfigurationKMSKeyConfigured.ts b/src/rules/sagemaker/SageMakerEndpointConfigurationKMSKeyConfigured.ts index 96792ca5be..64fa89235f 100644 --- a/src/rules/sagemaker/SageMakerEndpointConfigurationKMSKeyConfigured.ts +++ b/src/rules/sagemaker/SageMakerEndpointConfigurationKMSKeyConfigured.ts @@ -5,21 +5,23 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnEndpointConfig } from '@aws-cdk/aws-sagemaker'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * SageMaker endpoints utilize a KMS key * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnEndpointConfig) { - //Does this endpoint have a KMS key ID? const kmsKey = Stack.of(node).resolve(node.kmsKeyId); if (kmsKey == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/sagemaker/SageMakerNotebookInVPC.ts b/src/rules/sagemaker/SageMakerNotebookInVPC.ts index 77c068d2a6..1d74929248 100644 --- a/src/rules/sagemaker/SageMakerNotebookInVPC.ts +++ b/src/rules/sagemaker/SageMakerNotebookInVPC.ts @@ -5,20 +5,23 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnNotebookInstance } from '@aws-cdk/aws-sagemaker'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * SageMaker notebook instances are provisioned inside a VPC * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnNotebookInstance) { const subnetId = Stack.of(node).resolve(node.subnetId); if (subnetId == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/sagemaker/SageMakerNotebookInstanceKMSKeyConfigured.ts b/src/rules/sagemaker/SageMakerNotebookInstanceKMSKeyConfigured.ts index c9dc85e300..5bc2608881 100644 --- a/src/rules/sagemaker/SageMakerNotebookInstanceKMSKeyConfigured.ts +++ b/src/rules/sagemaker/SageMakerNotebookInstanceKMSKeyConfigured.ts @@ -5,20 +5,23 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnNotebookInstance } from '@aws-cdk/aws-sagemaker'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * SageMaker notebook instances utilize KMS keys for encryption at rest * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnNotebookInstance) { const kmsKey = Stack.of(node).resolve(node.kmsKeyId); if (kmsKey == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/sagemaker/SageMakerNotebookNoDirectInternetAccess.ts b/src/rules/sagemaker/SageMakerNotebookNoDirectInternetAccess.ts index 2a0dcab355..99b2e8655e 100644 --- a/src/rules/sagemaker/SageMakerNotebookNoDirectInternetAccess.ts +++ b/src/rules/sagemaker/SageMakerNotebookNoDirectInternetAccess.ts @@ -5,14 +5,14 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnNotebookInstance } from '@aws-cdk/aws-sagemaker'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * SageMaker notebook instances have direct internet access disabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnNotebookInstance) { const directInternetAccess = resolveIfPrimitive( node, @@ -22,10 +22,12 @@ export default Object.defineProperty( directInternetAccess == undefined || directInternetAccess != 'Disabled' ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/secretsmanager/SecretsManagerRotationEnabled.ts b/src/rules/secretsmanager/SecretsManagerRotationEnabled.ts index 7456b8fcce..de335dfcc6 100644 --- a/src/rules/secretsmanager/SecretsManagerRotationEnabled.ts +++ b/src/rules/secretsmanager/SecretsManagerRotationEnabled.ts @@ -9,14 +9,17 @@ import { CfnSecretTargetAttachment, } from '@aws-cdk/aws-secretsmanager'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveResourceFromInstrinsic } from '../../nag-pack'; +import { + resolveResourceFromInstrinsic, + NagRuleCompliance, +} from '../../nag-pack'; /** * Secrets have automatic rotation scheduled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnSecret) { const secretLogicalId = resolveResourceFromInstrinsic(node, node.ref); const secretTargetAttachmentLogicalIds = Array(); @@ -30,7 +33,7 @@ export default Object.defineProperty( } } if (cfnRotationSchedules.length === 0) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } let found = false; for (const child of cfnSecretTargetAttachments) { @@ -55,10 +58,12 @@ export default Object.defineProperty( } } if (!found) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/secretsmanager/SecretsManagerUsingKMSKey.ts b/src/rules/secretsmanager/SecretsManagerUsingKMSKey.ts index 2fdc6f8208..8ea2bd7395 100644 --- a/src/rules/secretsmanager/SecretsManagerUsingKMSKey.ts +++ b/src/rules/secretsmanager/SecretsManagerUsingKMSKey.ts @@ -5,21 +5,23 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnSecret } from '@aws-cdk/aws-secretsmanager'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Secrets are encrypted with KMS Customer managed keys * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnSecret) { const kmsKeyId = resolveIfPrimitive(node, node.kmsKeyId); if (kmsKeyId === undefined || kmsKeyId === 'aws/secretsmanager') { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/sns/SNSEncryptedKMS.ts b/src/rules/sns/SNSEncryptedKMS.ts index c2c04f9e0f..0f5fd2e726 100644 --- a/src/rules/sns/SNSEncryptedKMS.ts +++ b/src/rules/sns/SNSEncryptedKMS.ts @@ -5,20 +5,23 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnTopic } from '@aws-cdk/aws-sns'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * SNS topics are encrypted via KMS * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnTopic) { const topicKey = Stack.of(node).resolve(node.kmsMasterKeyId); if (topicKey == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/sqs/SQSQueueDLQ.ts b/src/rules/sqs/SQSQueueDLQ.ts index 06b7ab91c9..a64785c15d 100644 --- a/src/rules/sqs/SQSQueueDLQ.ts +++ b/src/rules/sqs/SQSQueueDLQ.ts @@ -5,20 +5,23 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnQueue } from '@aws-cdk/aws-sqs'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * SQS queues have a dead-letter queue enabled or have a cdk_nag rule suppression indicating they are a dead-letter queue. * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnQueue) { const redrivePolicy = Stack.of(node).resolve(node.redrivePolicy); if (redrivePolicy == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/sqs/SQSQueueSSE.ts b/src/rules/sqs/SQSQueueSSE.ts index eee74ef301..295cd1fbd3 100644 --- a/src/rules/sqs/SQSQueueSSE.ts +++ b/src/rules/sqs/SQSQueueSSE.ts @@ -5,20 +5,23 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnQueue } from '@aws-cdk/aws-sqs'; import { CfnResource, Stack } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * SQS queues have server-side encryption enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnQueue) { const kmsMasterKeyId = Stack.of(node).resolve(node.kmsMasterKeyId); if (kmsMasterKeyId == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/stepfunctions/StepFunctionStateMachineAllLogsToCloudWatch.ts b/src/rules/stepfunctions/StepFunctionStateMachineAllLogsToCloudWatch.ts index 0548156f7c..c45291e1a2 100644 --- a/src/rules/stepfunctions/StepFunctionStateMachineAllLogsToCloudWatch.ts +++ b/src/rules/stepfunctions/StepFunctionStateMachineAllLogsToCloudWatch.ts @@ -5,27 +5,29 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnStateMachine, LogLevel } from '@aws-cdk/aws-stepfunctions'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Step Function log "ALL" events to CloudWatch Logs * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnStateMachine) { const loggingConfiguration = Stack.of(node).resolve( node.loggingConfiguration ); if (loggingConfiguration == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const level = resolveIfPrimitive(node, loggingConfiguration.level); if (level == undefined || level != LogLevel.ALL) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/stepfunctions/StepFunctionStateMachineXray.ts b/src/rules/stepfunctions/StepFunctionStateMachineXray.ts index 97ce9904e6..3f14610511 100644 --- a/src/rules/stepfunctions/StepFunctionStateMachineXray.ts +++ b/src/rules/stepfunctions/StepFunctionStateMachineXray.ts @@ -5,27 +5,29 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnStateMachine } from '@aws-cdk/aws-stepfunctions'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Step Function have X-Ray tracing enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnStateMachine) { const tracingConfiguration = Stack.of(node).resolve( node.tracingConfiguration ); if (tracingConfiguration == undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } const enabled = resolveIfPrimitive(node, tracingConfiguration.enabled); if (!enabled) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/timestream/TimestreamDatabaseCustomerManagedKey.ts b/src/rules/timestream/TimestreamDatabaseCustomerManagedKey.ts index 03a169b867..2b09503fe7 100644 --- a/src/rules/timestream/TimestreamDatabaseCustomerManagedKey.ts +++ b/src/rules/timestream/TimestreamDatabaseCustomerManagedKey.ts @@ -5,19 +5,22 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnDatabase } from '@aws-cdk/aws-timestream'; import { CfnResource } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * Timestream databases use Customer Managed KMS Keys * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnDatabase) { if (node.kmsKeyId === undefined) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/vpc/VPCDefaultSecurityGroupClosed.ts b/src/rules/vpc/VPCDefaultSecurityGroupClosed.ts index ba5543f8bd..7d41bd8be6 100644 --- a/src/rules/vpc/VPCDefaultSecurityGroupClosed.ts +++ b/src/rules/vpc/VPCDefaultSecurityGroupClosed.ts @@ -5,6 +5,7 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnVPC } from '@aws-cdk/aws-ec2'; import { CfnResource } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * VPCs have their default security group closed @@ -13,11 +14,12 @@ import { CfnResource } from '@aws-cdk/core'; * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnVPC) { - return false; + return NagRuleCompliance.NON_COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/vpc/VPCFlowLogsEnabled.ts b/src/rules/vpc/VPCFlowLogsEnabled.ts index 7718afa4aa..e674661e71 100644 --- a/src/rules/vpc/VPCFlowLogsEnabled.ts +++ b/src/rules/vpc/VPCFlowLogsEnabled.ts @@ -5,14 +5,17 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnVPC, CfnFlowLog } from '@aws-cdk/aws-ec2'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveResourceFromInstrinsic } from '../../nag-pack'; +import { + resolveResourceFromInstrinsic, + NagRuleCompliance, +} from '../../nag-pack'; /** * VPCs have Flow Logs enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnVPC) { const vpcLogicalId = resolveResourceFromInstrinsic(node, node.ref); let found = false; @@ -25,10 +28,12 @@ export default Object.defineProperty( } } if (!found) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/vpc/VPCNoNACLs.ts b/src/rules/vpc/VPCNoNACLs.ts index d46de96e61..4ab22c793e 100644 --- a/src/rules/vpc/VPCNoNACLs.ts +++ b/src/rules/vpc/VPCNoNACLs.ts @@ -5,17 +5,19 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnNetworkAcl, CfnNetworkAclEntry } from '@aws-cdk/aws-ec2'; import { CfnResource } from '@aws-cdk/core'; +import { NagRuleCompliance } from '../../nag-pack'; /** * VPCs do not implement network ACLs * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnNetworkAcl || node instanceof CfnNetworkAclEntry) { - return false; + return NagRuleCompliance.NON_COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/vpc/VPCNoUnrestrictedRouteToIGW.ts b/src/rules/vpc/VPCNoUnrestrictedRouteToIGW.ts index 9b0ba3fe03..6d58b5423e 100644 --- a/src/rules/vpc/VPCNoUnrestrictedRouteToIGW.ts +++ b/src/rules/vpc/VPCNoUnrestrictedRouteToIGW.ts @@ -5,14 +5,14 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnRoute } from '@aws-cdk/aws-ec2'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Route tables do not have unrestricted routes ('0.0.0.0/0' or '::/0') to IGWs * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnRoute) { if (node.gatewayId != undefined) { const destinationCidrBlock = resolveIfPrimitive( @@ -27,17 +27,19 @@ export default Object.defineProperty( destinationCidrBlock != undefined && destinationCidrBlock.includes('/0') ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } if ( destinationIpv6CidrBlock != undefined && destinationIpv6CidrBlock.includes('/0') ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/vpc/VPCSubnetAutoAssignPublicIpDisabled.ts b/src/rules/vpc/VPCSubnetAutoAssignPublicIpDisabled.ts index a72cf7cbb9..1589116146 100644 --- a/src/rules/vpc/VPCSubnetAutoAssignPublicIpDisabled.ts +++ b/src/rules/vpc/VPCSubnetAutoAssignPublicIpDisabled.ts @@ -5,14 +5,14 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnSubnet } from '@aws-cdk/aws-ec2'; import { CfnResource } from '@aws-cdk/core'; -import { resolveIfPrimitive } from '../../nag-pack'; +import { resolveIfPrimitive, NagRuleCompliance } from '../../nag-pack'; /** * Subnets do not auto-assign public IP addresses * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnSubnet) { const mapPublicIpOnLaunch = resolveIfPrimitive( node, @@ -26,10 +26,12 @@ export default Object.defineProperty( mapPublicIpOnLaunch === true || assignIpv6AddressOnCreation === true ) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/src/rules/waf/WAFv2LoggingEnabled.ts b/src/rules/waf/WAFv2LoggingEnabled.ts index 53dc7b0ca5..f879ede52a 100644 --- a/src/rules/waf/WAFv2LoggingEnabled.ts +++ b/src/rules/waf/WAFv2LoggingEnabled.ts @@ -5,14 +5,17 @@ SPDX-License-Identifier: Apache-2.0 import { parse } from 'path'; import { CfnWebACL, CfnLoggingConfiguration } from '@aws-cdk/aws-wafv2'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { resolveResourceFromInstrinsic } from '../../nag-pack'; +import { + resolveResourceFromInstrinsic, + NagRuleCompliance, +} from '../../nag-pack'; /** * WAFv2 web ACLs have logging enabled * @param node the CfnResource to check */ export default Object.defineProperty( - (node: CfnResource): boolean => { + (node: CfnResource): NagRuleCompliance => { if (node instanceof CfnWebACL) { const webAclLogicalId = resolveResourceFromInstrinsic(node, node.ref); const webAclName = Stack.of(node).resolve(node.name); @@ -28,10 +31,12 @@ export default Object.defineProperty( } } if (!found) { - return false; + return NagRuleCompliance.NON_COMPLIANT; } + return NagRuleCompliance.COMPLIANT; + } else { + return NagRuleCompliance.NOT_APPLICABLE; } - return true; }, 'name', { value: parse(__filename).name } diff --git a/test/Engine.test.ts b/test/Engine.test.ts index 224cdca98c..317ad49555 100644 --- a/test/Engine.test.ts +++ b/test/Engine.test.ts @@ -13,8 +13,9 @@ import { Vpc, } from '@aws-cdk/aws-ec2'; import { PolicyStatement, User } from '@aws-cdk/aws-iam'; -import { CfnBucket } from '@aws-cdk/aws-s3'; +import { Bucket, CfnBucket } from '@aws-cdk/aws-s3'; import { + App, Aspects, CfnParameter, CfnResource, @@ -29,9 +30,11 @@ import { NagPack, resolveIfPrimitive, NagPackProps, + NagRuleCompliance, + IApplyRule, } from '../src'; -describe('Testing rule suppression system', () => { +describe('Rule suppression system', () => { test('Test single rule suppression', () => { const stack = new Stack(); Aspects.of(stack).add(new AwsSolutionsChecks()); @@ -468,7 +471,7 @@ describe('Testing rule suppression system', () => { }); }); -describe('Testing rule explanations', () => { +describe('Rule explanations', () => { test('Test no explicit explanation', () => { const stack = new Stack(); Aspects.of(stack).add(new AwsSolutionsChecks()); @@ -519,7 +522,7 @@ describe('Testing rule explanations', () => { }); }); -describe('Testing rule exception handling', () => { +describe('Rule exception handling', () => { const ERROR_MESSAGE = 'oops!'; class BadPack extends NagPack { constructor(props?: NagPackProps) { @@ -533,11 +536,11 @@ describe('Testing rule exception handling', () => { info: 'This is a imporperly made rule.', explanation: 'This will throw an error', level: NagMessageLevel.ERROR, - rule: function (node2: CfnResource): boolean { + rule: function (node2: CfnResource): NagRuleCompliance { if (node2) { throw Error(ERROR_MESSAGE); } - return false; + return NagRuleCompliance.NON_COMPLIANT; }, node: node, }); @@ -623,3 +626,135 @@ describe('Testing rule exception handling', () => { }).not.toThrowError(); }); }); + +describe('Report system', () => { + class TestPack extends NagPack { + lines = new Array(); + constructor(props?: NagPackProps) { + super(props); + this.packName = 'Test'; + } + public visit(node: IConstruct): void { + if (node instanceof CfnResource) { + const compliances = [ + NagRuleCompliance.NON_COMPLIANT, + NagRuleCompliance.COMPLIANT, + NagRuleCompliance.NOT_APPLICABLE, + ]; + compliances.forEach((compliance) => { + this.applyRule({ + ruleSuffixOverride: compliance, + info: 'foo.', + explanation: 'bar.', + level: NagMessageLevel.ERROR, + rule: function (node2: CfnResource): NagRuleCompliance { + if (node2.cfnResourceType !== 'Error') { + return compliance; + } + throw Error('foobar'); + }, + node: node, + }); + }); + } + } + + protected writeToStackComplianceReport( + params: IApplyRule, + ruleId: string, + compliance: NagRuleCompliance.COMPLIANT | NagRuleCompliance.NON_COMPLIANT, + explanation: string = '' + ): void { + this.lines.push( + this.createComplianceReportLine(params, ruleId, compliance, explanation) + ); + const fileName = `${this.packName}-${params.node.stack.stackName}-NagReport.csv`; + if (!this.reportStacks.includes(fileName)) { + this.reportStacks.push(fileName); + } + } + } + + test('Reports are generated for all stacks by default', () => { + const app = new App(); + const stack = new Stack(app, 'Stack1'); + const stack2 = new Stack(app, 'Stack2'); + const pack = new TestPack(); + Aspects.of(app).add(pack); + new SecurityGroup(stack, 'rSg', { + vpc: new Vpc(stack, 'rVpc'), + }); + new Bucket(stack2, 'rBucket'); + app.synth(); + expect(pack.readReportStacks.length).toEqual(2); + }); + test('Compliant and Non-Compliant values are written properly', () => { + const app = new App(); + const stack = new Stack(app, 'Stack1'); + const pack = new TestPack(); + Aspects.of(app).add(pack); + new CfnResource(stack, 'rResource', { type: 'foo' }); + app.synth(); + const expectedOuput = [ + '"Test-Compliant","Stack1/rResource","Compliant","N/A","Error","foo."\n', + '"Test-Non-Compliant","Stack1/rResource","Non-Compliant","N/A","Error","foo."\n', + ]; + expect(pack.lines.sort()).toEqual(expectedOuput.sort()); + }); + test('Suppression values are written properly', () => { + const app = new App(); + const stack = new Stack(app, 'Stack1'); + const pack = new TestPack(); + Aspects.of(app).add(pack); + const resource = new CfnResource(stack, 'rResource', { type: 'foo' }); + NagSuppressions.addResourceSuppressions(resource, [ + { + id: `${pack.readPackName}-${NagRuleCompliance.NON_COMPLIANT}`, + reason: 'lorem ipsum', + }, + ]); + app.synth(); + const expectedOuput = [ + '"Test-Compliant","Stack1/rResource","Compliant","N/A","Error","foo."\n', + '"Test-Non-Compliant","Stack1/rResource","Suppressed","lorem ipsum","Error","foo."\n', + ]; + expect(pack.lines.sort()).toEqual(expectedOuput.sort()); + }); + test('Error values are written properly', () => { + const app = new App(); + const stack = new Stack(app, 'Stack1'); + const pack = new TestPack(); + Aspects.of(app).add(pack); + const resource = new CfnResource(stack, 'rResource', { type: 'Error' }); + NagSuppressions.addResourceSuppressions(resource, [ + { + id: `${pack.readPackName}-${NagRuleCompliance.NON_COMPLIANT}`, + reason: 'lorem ipsum', + }, + ]); + app.synth(); + const expectedOuput = [ + '"Test-Non-Compliant","Stack1/rResource","UNKNOWN","N/A","Error","foo."\n', + '"Test-Compliant","Stack1/rResource","UNKNOWN","N/A","Error","foo."\n', + '"Test-N/A","Stack1/rResource","UNKNOWN","N/A","Error","foo."\n', + ]; + expect(pack.lines.sort()).toEqual(expectedOuput.sort()); + }); + test('Suppressed error values are escaped and written properly', () => { + const app = new App(); + const stack = new Stack(app, 'Stack1'); + const pack = new TestPack(); + Aspects.of(app).add(pack); + const resource = new CfnResource(stack, 'rResource', { type: 'Error' }); + NagSuppressions.addResourceSuppressions(resource, [ + { id: 'CdkNagValidationFailure', reason: '"quoted "lorem" ipsum"' }, + ]); + app.synth(); + const expectedOuput = [ + '"Test-Compliant","Stack1/rResource","Suppressed","""quoted ""lorem"" ipsum""","Error","foo."\n', + '"Test-N/A","Stack1/rResource","Suppressed","""quoted ""lorem"" ipsum""","Error","foo."\n', + '"Test-Non-Compliant","Stack1/rResource","Suppressed","""quoted ""lorem"" ipsum""","Error","foo."\n', + ]; + expect(pack.lines.sort()).toEqual(expectedOuput.sort()); + }); +}); diff --git a/test/Packs.test.ts b/test/Packs.test.ts index ed6d11dadc..bfe7fcaf6d 100644 --- a/test/Packs.test.ts +++ b/test/Packs.test.ts @@ -17,7 +17,22 @@ import { describe('Check NagPack Details', () => { describe('AwsSolutions', () => { - const pack = new AwsSolutionsChecks(); + class AwsSolutionsChecksExtended extends AwsSolutionsChecks { + actualWarnings = new Array(); + actualErrors = new Array(); + applyRule(params: IApplyRule): void { + const ruleSuffix = params.ruleSuffixOverride + ? params.ruleSuffixOverride + : params.rule.name; + const ruleId = `${pack.readPackName}-${ruleSuffix}`; + if (params.level === NagMessageLevel.WARN) { + this.actualWarnings.push(ruleId); + } else { + this.actualErrors.push(ruleId); + } + } + } + const pack = new AwsSolutionsChecksExtended(); test('Pack Name is correct', () => { expect(pack.readPackName).toStrictEqual('AwsSolutions'); }); @@ -143,30 +158,32 @@ describe('Check NagPack Details', () => { 'AwsSolutions-SQS3', 'AwsSolutions-VPC7', ]; - const actualWarnings = new Array(); - const actualErrors = new Array(); - jest.spyOn(pack, 'applyRule').mockImplementation((params: IApplyRule) => { - const ruleSuffix = params.ruleSuffixOverride - ? params.ruleSuffixOverride - : params.rule.name; - const ruleId = `${pack.readPackName}-${ruleSuffix}`; - if (params.level === NagMessageLevel.WARN) { - actualWarnings.push(ruleId); - } else { - actualErrors.push(ruleId); - } - return ruleId; - }); + jest.spyOn(pack, 'applyRule'); const stack = new Stack(); Aspects.of(stack).add(pack); new CfnResource(stack, 'rTestResource', { type: 'foo' }); SynthUtils.synthesize(stack).messages; - expect(actualWarnings.sort()).toEqual(expectedWarnings.sort()); - expect(actualErrors.sort()).toEqual(expectedErrors.sort()); + expect(pack.actualWarnings.sort()).toEqual(expectedWarnings.sort()); + expect(pack.actualErrors.sort()).toEqual(expectedErrors.sort()); }); }); describe('HIPAA-Security', () => { - const pack = new HIPAASecurityChecks(); + class HIPAASecurityChecksExtended extends HIPAASecurityChecks { + actualWarnings = new Array(); + actualErrors = new Array(); + applyRule(params: IApplyRule): void { + const ruleSuffix = params.ruleSuffixOverride + ? params.ruleSuffixOverride + : params.rule.name; + const ruleId = `${pack.readPackName}-${ruleSuffix}`; + if (params.level === NagMessageLevel.WARN) { + this.actualWarnings.push(ruleId); + } else { + this.actualErrors.push(ruleId); + } + } + } + const pack = new HIPAASecurityChecksExtended(); test('Pack Name is correct', () => { expect(pack.readPackName).toStrictEqual('HIPAA.Security'); }); @@ -261,30 +278,32 @@ describe('Check NagPack Details', () => { 'HIPAA.Security-VPCSubnetAutoAssignPublicIpDisabled', 'HIPAA.Security-WAFv2LoggingEnabled', ]; - const actualWarnings = new Array(); - const actualErrors = new Array(); - jest.spyOn(pack, 'applyRule').mockImplementation((params: IApplyRule) => { - const ruleSuffix = params.ruleSuffixOverride - ? params.ruleSuffixOverride - : params.rule.name; - const ruleId = `${pack.readPackName}-${ruleSuffix}`; - if (params.level === NagMessageLevel.WARN) { - actualWarnings.push(ruleId); - } else { - actualErrors.push(ruleId); - } - return ruleId; - }); + jest.spyOn(pack, 'applyRule'); const stack = new Stack(); Aspects.of(stack).add(pack); new CfnResource(stack, 'rTestResource', { type: 'foo' }); SynthUtils.synthesize(stack).messages; - expect(actualWarnings.sort()).toEqual(expectedWarnings.sort()); - expect(actualErrors.sort()).toEqual(expectedErrors.sort()); + expect(pack.actualWarnings.sort()).toEqual(expectedWarnings.sort()); + expect(pack.actualErrors.sort()).toEqual(expectedErrors.sort()); }); }); describe('NIST-800-53-R4', () => { - const pack = new NIST80053R4Checks(); + class NIST80053R4ChecksExtended extends NIST80053R4Checks { + actualWarnings = new Array(); + actualErrors = new Array(); + applyRule(params: IApplyRule): void { + const ruleSuffix = params.ruleSuffixOverride + ? params.ruleSuffixOverride + : params.rule.name; + const ruleId = `${pack.readPackName}-${ruleSuffix}`; + if (params.level === NagMessageLevel.WARN) { + this.actualWarnings.push(ruleId); + } else { + this.actualErrors.push(ruleId); + } + } + } + const pack = new NIST80053R4ChecksExtended(); test('Pack Name is correct', () => { expect(pack.readPackName).toStrictEqual('NIST.800.53.R4'); }); @@ -359,30 +378,32 @@ describe('Check NagPack Details', () => { 'NIST.800.53.R4-VPCFlowLogsEnabled', 'NIST.800.53.R4-WAFv2LoggingEnabled', ]; - const actualWarnings = new Array(); - const actualErrors = new Array(); - jest.spyOn(pack, 'applyRule').mockImplementation((params: IApplyRule) => { - const ruleSuffix = params.ruleSuffixOverride - ? params.ruleSuffixOverride - : params.rule.name; - const ruleId = `${pack.readPackName}-${ruleSuffix}`; - if (params.level === NagMessageLevel.WARN) { - actualWarnings.push(ruleId); - } else { - actualErrors.push(ruleId); - } - return ruleId; - }); + jest.spyOn(pack, 'applyRule'); const stack = new Stack(); Aspects.of(stack).add(pack); new CfnResource(stack, 'rTestResource', { type: 'foo' }); SynthUtils.synthesize(stack).messages; - expect(actualWarnings.sort()).toEqual(expectedWarnings.sort()); - expect(actualErrors.sort()).toEqual(expectedErrors.sort()); + expect(pack.actualWarnings.sort()).toEqual(expectedWarnings.sort()); + expect(pack.actualErrors.sort()).toEqual(expectedErrors.sort()); }); }); describe('NIST-800-53-R5', () => { - const pack = new NIST80053R5Checks(); + class NIST80053R5ChecksExtended extends NIST80053R5Checks { + actualWarnings = new Array(); + actualErrors = new Array(); + applyRule(params: IApplyRule): void { + const ruleSuffix = params.ruleSuffixOverride + ? params.ruleSuffixOverride + : params.rule.name; + const ruleId = `${pack.readPackName}-${ruleSuffix}`; + if (params.level === NagMessageLevel.WARN) { + this.actualWarnings.push(ruleId); + } else { + this.actualErrors.push(ruleId); + } + } + } + const pack = new NIST80053R5ChecksExtended(); test('Pack Name is correct', () => { expect(pack.readPackName).toStrictEqual('NIST.800.53.R5'); }); @@ -472,30 +493,32 @@ describe('Check NagPack Details', () => { 'NIST.800.53.R5-VPCSubnetAutoAssignPublicIpDisabled', 'NIST.800.53.R5-WAFv2LoggingEnabled', ]; - const actualWarnings = new Array(); - const actualErrors = new Array(); - jest.spyOn(pack, 'applyRule').mockImplementation((params: IApplyRule) => { - const ruleSuffix = params.ruleSuffixOverride - ? params.ruleSuffixOverride - : params.rule.name; - const ruleId = `${pack.readPackName}-${ruleSuffix}`; - if (params.level === NagMessageLevel.WARN) { - actualWarnings.push(ruleId); - } else { - actualErrors.push(ruleId); - } - return ruleId; - }); + jest.spyOn(pack, 'applyRule'); const stack = new Stack(); Aspects.of(stack).add(pack); new CfnResource(stack, 'rTestResource', { type: 'foo' }); SynthUtils.synthesize(stack).messages; - expect(actualWarnings.sort()).toEqual(expectedWarnings.sort()); - expect(actualErrors.sort()).toEqual(expectedErrors.sort()); + expect(pack.actualWarnings.sort()).toEqual(expectedWarnings.sort()); + expect(pack.actualErrors.sort()).toEqual(expectedErrors.sort()); }); }); describe('PCI-DSS-3.2.1', () => { - const pack = new PCIDSS321Checks(); + class PCIDSS321ChecksExtended extends PCIDSS321Checks { + actualWarnings = new Array(); + actualErrors = new Array(); + applyRule(params: IApplyRule): void { + const ruleSuffix = params.ruleSuffixOverride + ? params.ruleSuffixOverride + : params.rule.name; + const ruleId = `${pack.readPackName}-${ruleSuffix}`; + if (params.level === NagMessageLevel.WARN) { + this.actualWarnings.push(ruleId); + } else { + this.actualErrors.push(ruleId); + } + } + } + const pack = new PCIDSS321ChecksExtended(); test('Pack Name is correct', () => { expect(pack.readPackName).toStrictEqual('PCI.DSS.321'); }); @@ -570,26 +593,13 @@ describe('Check NagPack Details', () => { 'PCI.DSS.321-VPCSubnetAutoAssignPublicIpDisabled', 'PCI.DSS.321-WAFv2LoggingEnabled', ]; - const actualWarnings = new Array(); - const actualErrors = new Array(); - jest.spyOn(pack, 'applyRule').mockImplementation((params: IApplyRule) => { - const ruleSuffix = params.ruleSuffixOverride - ? params.ruleSuffixOverride - : params.rule.name; - const ruleId = `${pack.readPackName}-${ruleSuffix}`; - if (params.level === NagMessageLevel.WARN) { - actualWarnings.push(ruleId); - } else { - actualErrors.push(ruleId); - } - return ruleId; - }); + jest.spyOn(pack, 'applyRule'); const stack = new Stack(); Aspects.of(stack).add(pack); new CfnResource(stack, 'rTestResource', { type: 'foo' }); SynthUtils.synthesize(stack).messages; - expect(actualWarnings.sort()).toEqual(expectedWarnings.sort()); - expect(actualErrors.sort()).toEqual(expectedErrors.sort()); + expect(pack.actualWarnings.sort()).toEqual(expectedWarnings.sort()); + expect(pack.actualErrors.sort()).toEqual(expectedErrors.sort()); }); }); }); diff --git a/yarn.lock b/yarn.lock index 40d73a7d05..77ea040dff 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5070,12 +5070,7 @@ json-schema-traverse@^1.0.0: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= - -json-schema@^0.4.0: +json-schema@0.2.3, json-schema@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==