From d3c556c60f9a09fb0e9ebb427b6d19abd331a68e Mon Sep 17 00:00:00 2001 From: Arun Donti Date: Thu, 17 Mar 2022 14:18:08 -0400 Subject: [PATCH] fix(Nag Reports): compliance reports not generated for stacks with no relevant resources (#723) --- src/nag-pack.ts | 32 +++++++++++++++++++++++--------- test/Engine.test.ts | 16 +++++++++++++++- 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/src/nag-pack.ts b/src/nag-pack.ts index de65bc451e..f409a5da5e 100644 --- a/src/nag-pack.ts +++ b/src/nag-pack.ts @@ -127,12 +127,13 @@ export abstract class NagPack implements IAspect { const ruleId = `${this.packName}-${ruleSuffix}`; try { const ruleCompliance = params.rule(params.node); - if ( - this.reports === true && - ruleCompliance === NagRuleCompliance.COMPLIANT - ) { - this.writeToStackComplianceReport(params, ruleId, ruleCompliance); - } else if (this.isNonCompliant(ruleCompliance)) { + if (this.reports === true) { + this.initializeStackReport(params); + if (ruleCompliance === NagRuleCompliance.COMPLIANT) { + this.writeToStackComplianceReport(params, ruleId, ruleCompliance); + } + } + if (this.isNonCompliant(ruleCompliance)) { const findings = this.asFindings(ruleCompliance); for (const findingId of findings) { const suppressionReason = this.ignoreRule( @@ -249,7 +250,7 @@ export abstract class NagPack implements IAspect { } /** - * Write a line to the rule packs compliance report for the resource's Stack + * Write a line to the rule pack's 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. @@ -270,20 +271,33 @@ export abstract class NagPack implements IAspect { compliance, explanation ); - let outDir = App.of(params.node)?.outdir; + const outDir = App.of(params.node)?.outdir; const stackName = params.node.stack.nested ? Names.uniqueId(params.node.stack) : params.node.stack.stackName; const fileName = `${this.packName}-${stackName}-NagReport.csv`; const filePath = join(outDir ? outDir : '', fileName); + appendFileSync(filePath, line); + } + + /** + * Initialize the report for the rule pack's compliance report for the resource's Stack if it doesn't exist + * @param params + */ + protected initializeStackReport(params: IApplyRule): void { + const stackName = params.node.stack.nested + ? Names.uniqueId(params.node.stack) + : params.node.stack.stackName; + const fileName = `${this.packName}-${stackName}-NagReport.csv`; if (!this.reportStacks.includes(fileName)) { + const outDir = App.of(params.node)?.outdir; + const filePath = join(outDir ? outDir : '', fileName); this.reportStacks.push(fileName); writeFileSync( filePath, 'Rule ID,Resource ID,Compliance,Exception Reason,Rule Level,Rule Info\n' ); } - appendFileSync(filePath, line); } /** diff --git a/test/Engine.test.ts b/test/Engine.test.ts index 01a62be9ac..decee313ae 100644 --- a/test/Engine.test.ts +++ b/test/Engine.test.ts @@ -915,7 +915,9 @@ describe('Report system', () => { explanation: 'bar.', level: NagMessageLevel.ERROR, rule: function (node2: CfnResource): NagRuleCompliance { - if (node2.cfnResourceType !== 'Error') { + if (node2.cfnResourceType === 'N/A') { + return NagRuleCompliance.NOT_APPLICABLE; + } else if (node2.cfnResourceType !== 'Error') { return compliance; } throw Error('foobar'); @@ -958,6 +960,18 @@ describe('Report system', () => { app.synth(); expect(pack.readReportStacks.length).toEqual(2); }); + test('Reports are initialized for stacks with no relevant resources', () => { + const app = new App(); + const stack = new Stack(app, 'Stack1'); + const pack = new TestPack(); + Aspects.of(app).add(pack); + new CfnResource(stack, 'rNAResource', { + type: 'N/A', + }); + app.synth(); + expect(pack.readReportStacks.length).toEqual(1); + expect(pack.lines.length).toBe(0); + }); test('Nested Stack reports do not contain tokens in names', () => { const app = new App(); const parent = new Stack(app, 'Parent');