Skip to content

Commit b11e256

Browse files
authored
feat: Check if checkov is already installed (#3)
* check if checkov already exists * move code from index to plugin * fix build error * fix PR comments
1 parent 48fa376 commit b11e256

File tree

5 files changed

+98
-99
lines changed

5 files changed

+98
-99
lines changed

.projenrc.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,6 @@ const project = new cdk.JsiiProject({
3535

3636
project.gitignore.exclude('.idea');
3737

38-
project.addScripts({ postinstall: 'pip install -U checkov' });
38+
project.addScripts({ postinstall: '[ \"$(command -v checkov)\" ] && exit 0; pip install -U checkov' });
3939

4040
project.synth();

package.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/index.ts

Lines changed: 1 addition & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,94 +1 @@
1-
import {
2-
IPolicyValidationPluginBeta1,
3-
IPolicyValidationContextBeta1,
4-
PolicyValidationPluginReportBeta1,
5-
} from 'aws-cdk-lib';
6-
import { Report, processReport } from './report';
7-
import { exec } from './utils';
8-
9-
export interface CheckovValidatorProps {
10-
/**
11-
* List of checks to run
12-
*
13-
* @default - all checks are run
14-
*/
15-
readonly check?: string[];
16-
17-
/**
18-
* List of checks to skip
19-
*
20-
* @default - no checks are skipped
21-
*/
22-
readonly skipCheck?: string[];
23-
}
24-
25-
/**
26-
* A validation plugin using checkov
27-
*/
28-
export class CheckovValidator implements IPolicyValidationPluginBeta1 {
29-
public readonly name: string;
30-
31-
private readonly checkov: string;
32-
private readonly check: string[];
33-
private readonly skipCheck: string[];
34-
35-
private templatePaths: string[] = [];
36-
37-
constructor(props: CheckovValidatorProps = {}) {
38-
this.name = 'cdk-validator-checkov';
39-
40-
this.checkov = 'checkov'; // possible improvement allow Docker usage
41-
this.check = props.check ?? [];
42-
this.skipCheck = props.skipCheck ?? [];
43-
}
44-
45-
validate(context: IPolicyValidationContextBeta1): PolicyValidationPluginReportBeta1 {
46-
this.templatePaths = context.templatePaths;
47-
48-
return this.execCheckov();
49-
}
50-
51-
private execCheckov(): PolicyValidationPluginReportBeta1 {
52-
const flags = [
53-
'--framework',
54-
'cloudformation',
55-
'terraform_json',
56-
'--output',
57-
'json',
58-
'--soft-fail',
59-
];
60-
61-
this.templatePaths.forEach((templatePath) => {
62-
flags.push('-f');
63-
flags.push(templatePath);
64-
});
65-
66-
if (this.check?.length) {
67-
flags.push('--check');
68-
flags.push(this.check.join(','));
69-
}
70-
71-
if (this.skipCheck?.length) {
72-
flags.push('--skip-check');
73-
flags.push(this.skipCheck.join(','));
74-
}
75-
76-
try {
77-
const report: Report = exec([this.checkov, ...flags], {
78-
json: true,
79-
env: {
80-
LOG_LEVEL: 'ERROR',
81-
},
82-
});
83-
84-
return processReport(report);
85-
} catch (e) {
86-
console.error(`checkov plugin failed to scan the given templates. Error: ${e}`);
87-
88-
return {
89-
success: false,
90-
violations: [],
91-
};
92-
}
93-
}
94-
}
1+
export { CheckovValidator, CheckovValidatorProps } from './plugin';

src/plugin.ts

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import {
2+
IPolicyValidationPluginBeta1,
3+
IPolicyValidationContextBeta1,
4+
PolicyValidationPluginReportBeta1,
5+
} from 'aws-cdk-lib';
6+
import { Report, processReport } from './report';
7+
import { exec } from './utils';
8+
9+
export interface CheckovValidatorProps {
10+
/**
11+
* List of checks to run
12+
*
13+
* @default - all checks are run
14+
*/
15+
readonly check?: string[];
16+
17+
/**
18+
* List of checks to skip
19+
*
20+
* @default - no checks are skipped
21+
*/
22+
readonly skipCheck?: string[];
23+
}
24+
25+
/**
26+
* A validation plugin using checkov
27+
*/
28+
export class CheckovValidator implements IPolicyValidationPluginBeta1 {
29+
public readonly name: string;
30+
31+
private readonly checkov: string;
32+
private readonly check: string[];
33+
private readonly skipCheck: string[];
34+
35+
constructor(props: CheckovValidatorProps = {}) {
36+
this.name = 'cdk-validator-checkov';
37+
38+
this.checkov = 'checkov'; // possible improvement allow Docker usage
39+
this.check = props.check ?? [];
40+
this.skipCheck = props.skipCheck ?? [];
41+
}
42+
43+
validate(context: IPolicyValidationContextBeta1): PolicyValidationPluginReportBeta1 {
44+
return this.execCheckov(context.templatePaths);
45+
}
46+
47+
private execCheckov(templatePaths: string[]): PolicyValidationPluginReportBeta1 {
48+
const flags = [
49+
'--framework',
50+
'cloudformation',
51+
'terraform_json',
52+
'--output',
53+
'json',
54+
'--soft-fail',
55+
];
56+
57+
templatePaths.forEach((templatePath) => {
58+
flags.push('-f');
59+
flags.push(templatePath);
60+
});
61+
62+
if (this.check?.length) {
63+
flags.push('--check');
64+
flags.push(this.check.join(','));
65+
}
66+
67+
if (this.skipCheck?.length) {
68+
flags.push('--skip-check');
69+
flags.push(this.skipCheck.join(','));
70+
}
71+
72+
try {
73+
const report: Report = exec([this.checkov, ...flags], {
74+
json: true,
75+
env: {
76+
LOG_LEVEL: 'ERROR',
77+
},
78+
});
79+
80+
return processReport(report);
81+
} catch (e) {
82+
console.error(`checkov plugin failed to scan the given templates. ${e}`);
83+
84+
return {
85+
success: false,
86+
violations: [],
87+
};
88+
}
89+
}
90+
}

src/report.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export function processReport(report: Report): PolicyValidationPluginReportBeta1
4040
if ('checkov_version' in report) {
4141
// empty result
4242
return {
43-
pluginVersion: report.checkov_version as string,
43+
pluginVersion: report.checkov_version!.toString(),
4444
success: true,
4545
violations: [],
4646
};
@@ -51,7 +51,7 @@ export function processReport(report: Report): PolicyValidationPluginReportBeta1
5151
const violationsMap: Record<string, PolicyViolationBeta1> = {};
5252

5353
report.results.failed_checks.forEach((check) => {
54-
const violation: PolicyViolationBeta1 = {
54+
const violation = {
5555
ruleName: check.check_id,
5656
description: check.check_name,
5757
violatingResources: [{
@@ -64,7 +64,7 @@ export function processReport(report: Report): PolicyValidationPluginReportBeta1
6464
ruleMetadata: {
6565
DocumentationUrl: check.guideline,
6666
},
67-
};
67+
} satisfies PolicyViolationBeta1;
6868

6969
if (check.check_id in violationsMap) {
7070
violationsMap[check.check_id].violatingResources.push({
@@ -77,6 +77,8 @@ export function processReport(report: Report): PolicyValidationPluginReportBeta1
7777
}
7878
});
7979

80+
console.log(`More details: ${report.url}\n`);
81+
8082
return {
8183
pluginVersion,
8284
success,

0 commit comments

Comments
 (0)