@@ -5,6 +5,8 @@ const fs = require('fs');
55const { promisify } = require ( 'util' ) ;
66const readFile = promisify ( fs . readFile ) ;
77const CloudConformity = require ( "cloud-conformity" ) ;
8+ const readDir = promisify ( fs . readdir ) ;
9+ const readOptions = { encoding : "utf8" }
810
911const computeFailures = ( result , messages ) => {
1012 console . log ( JSON . stringify ( result , null , 2 ) ) ;
@@ -31,26 +33,40 @@ const computeFailures = (result, messages) => {
3133 } ) ;
3234}
3335
34- const scan = async ( templatePath , ccEndpoint , ccApiKey , profileId , accountId ) => {
36+ const scan = async ( templatePath , ccEndpoint , ccApiKey , profileId , accountId , templatesDirPath ) => {
3537 const cc = new CloudConformity . CloudConformity ( ccEndpoint , ccApiKey ) ;
36- const template = await readFile ( templatePath , 'utf8' ) ;
38+ if ( templatesDirPath ) {
39+ return batchScanTemplates ( cc , templatesDirPath , profileId , accountId )
40+ }
41+ return scanTemplate ( cc , templatePath , profileId , accountId )
42+ }
43+
44+ const failOnFailure = ( failures , acceptedQty ) => {
45+ return ( ( failures . extreme > acceptedQty . extreme ) || ( failures . veryHigh > acceptedQty . veryHigh ) || ( failures . high > acceptedQty . high ) || ( failures . medium > acceptedQty . medium ) || ( failures . low > acceptedQty . low ) )
46+ } ;
47+
48+ const batchScanTemplates = async ( cc , templatesDirPath , profileId , accountId ) => {
49+ const dir = await readDir ( templatesDirPath , readOptions )
50+ return Promise . all ( dir . map ( async ( template ) => {
51+ const fullPath = templatesDirPath + "/" + template
52+ return scanTemplate ( cc , fullPath , profileId , accountId )
53+ } ) )
54+ }
55+
56+ const scanTemplate = async ( cc , templatePath , profileId , accountId ) => {
57+ const template = await readFile ( templatePath , readOptions ) ;
3758 // Scans the template using Conformity module.
59+ console . log ( "Scan template: (%s)" , templatePath )
3860 const result = await cc . scanACloudFormationTemplateAndReturAsArrays ( template , profileId , accountId ) ;
3961 const messages = [ ] ;
4062 const results = computeFailures ( result , messages ) ;
4163 return {
42- detections : result . failure ,
43- results : results ,
44- messages : messages
64+ template : templatePath ,
65+ detections : result . failure ,
66+ results : results ,
67+ messages : messages
4568 } ;
46- } ;
47-
48- const failOnFailure = ( failures , acceptedQty ) => {
49- if ( ( failures . extreme > acceptedQty . extreme ) || ( failures . veryHigh > acceptedQty . veryHigh ) || ( failures . high > acceptedQty . high ) || ( failures . medium > acceptedQty . medium ) || ( failures . low > acceptedQty . low ) ) {
50- return true ;
51- }
52- return false ;
53- } ;
69+ }
5470
5571const region = process . env . cc_region ;
5672const apikey = process . env . cc_apikey ;
@@ -65,26 +81,40 @@ const acceptedResults = {
6581const outputResults = process . env . cc_output_results ? true : false ;
6682const profileId = process . env . profileId ;
6783const accountId = process . env . accountId ;
84+ const templatesDirPath = process . env . templatesDirPath ;
6885
69- scan ( templatePath , region , apikey , profileId , accountId )
70- . then ( res => {
71- console . log ( `Failures found: ${ JSON . stringify ( res . results , null , 2 ) } ` ) ;
72- console . log ( '\n' ) ;
73- console . log ( `Quantity of failures allowed: ${ JSON . stringify ( acceptedResults , null , 2 ) } ` ) ;
74- if ( outputResults && res . messages ) {
75- console . log ( '\n' ) ;
76- console . log ( 'Results:\n' ) ;
77- console . log ( res . messages . join ( '\n' ) ) ;
86+ scan ( templatePath , region , apikey , profileId , accountId , templatesDirPath )
87+ . then ( value => {
88+ const results = Array . isArray ( value ) ? value : [ value ]
89+ const COMPLIANT_MESSASGE = "Template passes configured checks."
90+ const NON_COMPLIANT_MESSAGE = "Security and/or misconfiguration issue(s) found in template(s): "
91+ const nonCompliantTemplates = [ ] ;
92+ let isCompliant = true ;
93+ for ( const result of results ) {
94+ console . log ( `\nFailures found: ${ JSON . stringify ( result . results , null , 2 ) } ` ) ;
95+ console . log ( '\n' ) ;
96+ console . log ( `Quantity of failures allowed: ${ JSON . stringify ( acceptedResults , null , 2 ) } ` ) ;
97+ if ( outputResults && result . messages ) {
98+ console . log ( '\n' ) ;
99+ console . log ( 'Results:\n' ) ;
100+ console . log ( result . messages . join ( '\n' ) ) ;
101+ }
102+ console . log ( '\n' ) ;
103+ if ( failOnFailure ( result . results , acceptedResults ) ) {
104+ isCompliant = false ;
105+ nonCompliantTemplates . push ( result . template )
106+ }
78107 }
79- console . log ( '\n' ) ;
80- return failOnFailure ( res . results , acceptedResults ) ;
108+ return {
109+ status : isCompliant ,
110+ message : isCompliant ? COMPLIANT_MESSASGE : NON_COMPLIANT_MESSAGE + " [" + nonCompliantTemplates + "]"
111+ } ;
81112 } )
82113 . then ( res => {
83- if ( res ) {
84- console . log ( "Security and/or misconfiguration issue(s) found in template." ) ;
85- process . exit ( 1 ) ;
114+ console . log ( res . message )
115+ if ( ! res . status ) {
116+ process . exit ( 1 ) ;
86117 }
87- console . log ( "Template passes configured checks." ) ;
88118 process . exit ( 0 ) ;
89119 } )
90120 . catch ( err => {
0 commit comments