Skip to content

Commit 5862e97

Browse files
authored
Merge pull request #91 from lazy-actions/refactor/inputs
refactor: Separate the process of input parameters
2 parents 245f58d + f95014a commit 5862e97

File tree

9 files changed

+232
-102
lines changed

9 files changed

+232
-102
lines changed

__tests__/helper.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import * as fs from 'fs';
2+
import * as path from 'path';
23
import { Downloader } from '../src/downloader';
34

5+
export const template = path.join(__dirname, '../src/template/default.tpl');
6+
47
const downloader = new Downloader();
58

69
export function removeTrivyCmd(path: string) {

__tests__/inputs.test.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { Inputs } from '../src/inputs';
2+
import { template } from './helper';
3+
4+
describe('Inputs class Test', () => {
5+
const initEnv = process.env;
6+
7+
beforeEach(() => {
8+
process.env = {
9+
INPUT_TOKEN: 'xxxxx',
10+
INPUT_IMAGE: 'yyyyy',
11+
...initEnv
12+
};
13+
});
14+
15+
test('Specify required parameters only', () => {
16+
expect(() => new Inputs()).not.toThrow();
17+
});
18+
19+
test('Specify all parameter', () => {
20+
process.env = {
21+
INPUT_TOKEN: 'xxx',
22+
INPUT_IMAGE: 'yyy',
23+
INPUT_TRIVY_VERSION: '0.18.3',
24+
INPUT_SEVERITY: 'HIGH',
25+
INPUT_VULN_TYPE: 'os',
26+
INPUT_IGNORE_UNFIXED: 'true',
27+
INPUT_TEMPLATE: template,
28+
INPUT_ISSUE_TITLE: 'hello',
29+
INPUT_ISSUE_LABEL: 'world',
30+
INPUT_ISSUE_ASSIGNEE: 'aaaa',
31+
...initEnv
32+
};
33+
const inputs = new Inputs();
34+
expect(() => inputs.validate()).not.toThrow();
35+
});
36+
});

__tests__/trivy.test.ts

Lines changed: 3 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as path from 'path';
22
import { Downloader } from '../src/downloader';
33
import { scan } from '../src/trivy';
4-
import { TrivyOption } from '../src/interface';
4+
import { TrivyCmdOption } from '../src/interface';
55
import { removeTrivyCmd } from './helper';
66

77
const downloader = new Downloader();
@@ -22,7 +22,7 @@ describe('Trivy scan', () => {
2222
});
2323

2424
test('with valid option', () => {
25-
const option: TrivyOption = {
25+
const option: TrivyCmdOption = {
2626
severity: 'HIGH,CRITICAL',
2727
vulnType: 'os,library',
2828
ignoreUnfixed: true,
@@ -33,7 +33,7 @@ describe('Trivy scan', () => {
3333
});
3434

3535
test('without ignoreUnfixed', () => {
36-
const option: TrivyOption = {
36+
const option: TrivyCmdOption = {
3737
severity: 'HIGH,CRITICAL',
3838
vulnType: 'os,library',
3939
ignoreUnfixed: false,
@@ -42,28 +42,4 @@ describe('Trivy scan', () => {
4242
const result: string = scan(trivyPath, image, option) as string;
4343
expect(result.length).toBeGreaterThanOrEqual(1);
4444
});
45-
46-
test('with invalid severity', () => {
47-
const invalidOption: TrivyOption = {
48-
severity: 'INVALID',
49-
vulnType: 'os,library',
50-
ignoreUnfixed: true,
51-
template
52-
};
53-
expect(() => {
54-
scan(trivyPath, image, invalidOption);
55-
}).toThrowError('Trivy option error: INVALID is unknown severity');
56-
});
57-
58-
test('with invalid vulnType', () => {
59-
const invalidOption: TrivyOption = {
60-
severity: 'HIGH',
61-
vulnType: 'INVALID',
62-
ignoreUnfixed: true,
63-
template
64-
};
65-
expect(() => {
66-
scan(trivyPath, image, invalidOption);
67-
}).toThrowError('Trivy option error: INVALID is unknown vuln-type');
68-
});
6945
});

__tests__/validator.test.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { TrivyCmdOptionValidator } from '../src/validator';
2+
import { template } from './helper';
3+
4+
describe('TrivyCmdOptionValidator Test', () => {
5+
test('Correct option', () => {
6+
const validator = new TrivyCmdOptionValidator({
7+
severity: 'HIGH',
8+
vulnType: 'os',
9+
ignoreUnfixed: false,
10+
template
11+
});
12+
expect(() => validator.validate()).not.toThrow();
13+
});
14+
15+
test('Invalid severity', () => {
16+
const validator = new TrivyCmdOptionValidator({
17+
severity: '?',
18+
vulnType: 'os',
19+
ignoreUnfixed: false,
20+
template
21+
});
22+
expect(() => validator.validate()).toThrow(
23+
'Trivy option error: ? is unknown severity'
24+
);
25+
});
26+
27+
test('Invalid vuln_type', () => {
28+
const validator = new TrivyCmdOptionValidator({
29+
severity: 'HIGH',
30+
vulnType: '?',
31+
ignoreUnfixed: false,
32+
template
33+
});
34+
expect(() => validator.validate()).toThrow(
35+
'Trivy option error: ? is unknown vuln-type'
36+
);
37+
});
38+
39+
test('Invalid template', () => {
40+
const validator = new TrivyCmdOptionValidator({
41+
severity: 'HIGH',
42+
vulnType: 'os',
43+
ignoreUnfixed: false,
44+
template: '?'
45+
});
46+
expect(() => validator.validate()).toThrow('Could not find ?');
47+
});
48+
});

src/index.ts

Lines changed: 9 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,29 @@
11
import * as core from '@actions/core';
22
import { Downloader } from './downloader';
33
import { GitHub } from './github';
4+
import { Inputs } from './inputs';
45
import { scan } from './trivy';
5-
import { TrivyOption } from './interface';
66

77
async function run(): Promise<void> {
8-
const trivyVersion = core.getInput('trivy_version').replace(/^v/, '');
9-
const image = core.getInput('image') || process.env.IMAGE_NAME;
10-
11-
if (!image) {
12-
throw new Error('Please specify scan target image name');
13-
}
14-
15-
const trivyOption: TrivyOption = {
16-
severity: core.getInput('severity').replace(/\s+/g, ''),
17-
vulnType: core.getInput('vuln_type').replace(/\s+/g, ''),
18-
ignoreUnfixed: core.getInput('ignore_unfixed').toLowerCase() === 'true',
19-
template: core.getInput('template') || `${__dirname}/template/default.tpl`,
20-
};
8+
const inputs = new Inputs();
9+
inputs.validate();
2110

2211
const downloader = new Downloader();
23-
const trivyCmdPath = await downloader.download(trivyVersion);
24-
const result = scan(trivyCmdPath, image, trivyOption);
12+
const trivyCmdPath = await downloader.download(inputs.trivy.version);
13+
const result = scan(trivyCmdPath, inputs.image, inputs.trivy.option);
2514

2615
if (!result) {
2716
return;
2817
}
2918

30-
const issueOption = {
31-
title: core.getInput('issue_title'),
32-
body: result,
33-
labels: core
34-
.getInput('issue_label')
35-
.replace(/\s+/g, '')
36-
.split(','),
37-
assignees: core
38-
.getInput('issue_assignee')
39-
.replace(/\s+/g, '')
40-
.split(','),
41-
};
42-
const token = core.getInput('token', { required: true });
43-
const github = new GitHub(token);
44-
const output = await github.createOrUpdateIssue(image, issueOption);
19+
const github = new GitHub(inputs.token);
20+
const issueOption = { body: result, ...inputs.issue };
21+
const output = await github.createOrUpdateIssue(inputs.image, issueOption);
4522

4623
core.setOutput('html_url', output.htmlUrl);
4724
core.setOutput('issue_number', output.issueNumber.toString());
4825

49-
if (core.getInput('fail_on_vulnerabilities') === 'true') {
26+
if (inputs.fail_on_vulnerabilities) {
5027
throw new Error('Abnormal termination because vulnerabilities found');
5128
}
5229
}

src/inputs.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import * as core from '@actions/core';
2+
import { IssueInputs, TrivyInputs } from './interface';
3+
import { TrivyCmdOptionValidator } from './validator';
4+
5+
export class Inputs {
6+
token: string;
7+
image: string;
8+
trivy: TrivyInputs;
9+
issue: IssueInputs;
10+
fail_on_vulnerabilities: boolean;
11+
12+
constructor() {
13+
this.token = core.getInput('token', { required: true });
14+
15+
const image = core.getInput('image') || process.env.IMAGE_NAME;
16+
if (!image) {
17+
throw new Error('Please specify target image');
18+
}
19+
this.image = image;
20+
21+
this.trivy = {
22+
version: core.getInput('trivy_version').replace(/^v/, ''),
23+
option: {
24+
severity: core.getInput('severity').replace(/\s+/g, ''),
25+
vulnType: core.getInput('vuln_type').replace(/\s+/g, ''),
26+
ignoreUnfixed: core.getInput('ignore_unfixed').toLowerCase() === 'true',
27+
template:
28+
core.getInput('template') || `${__dirname}/template/default.tpl`
29+
}
30+
};
31+
32+
this.issue = {
33+
title: core.getInput('issue_title'),
34+
labels: core
35+
.getInput('issue_label')
36+
.replace(/\s+/g, '')
37+
.split(','),
38+
assignees: core
39+
.getInput('issue_assignee')
40+
.replace(/\s+/g, '')
41+
.split(',')
42+
};
43+
44+
this.fail_on_vulnerabilities =
45+
core.getInput('fail_on_vulnerabilities') === 'true';
46+
}
47+
48+
validate(): void {
49+
const trivy = new TrivyCmdOptionValidator(this.trivy.option);
50+
trivy.validate();
51+
}
52+
}

src/interface.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,28 @@
1-
export interface IssueOption {
1+
export interface Validator {
2+
validate(): void;
3+
}
4+
5+
export interface IssueInputs {
26
title: string;
3-
body: string;
47
labels?: string[];
58
assignees?: string[];
69
}
710

11+
export interface IssueOption extends IssueInputs {
12+
body: string;
13+
}
14+
815
export interface IssueResponse {
916
issueNumber: number;
1017
htmlUrl: string;
1118
}
1219

13-
export interface TrivyOption {
20+
export interface TrivyInputs {
21+
version: string;
22+
option: TrivyCmdOption;
23+
}
24+
25+
export interface TrivyCmdOption {
1426
severity: string;
1527
vulnType: string;
1628
ignoreUnfixed: boolean;

src/trivy.ts

Lines changed: 2 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
import { spawnSync } from 'child_process';
22
import * as core from '@actions/core';
3-
import { TrivyOption } from './interface';
3+
import { TrivyCmdOption } from './interface';
44

55
export function scan(
66
trivyPath: string,
77
image: string,
8-
option: TrivyOption
8+
option: TrivyCmdOption
99
): string | undefined {
10-
validateOption(option);
11-
1210
const args = [
1311
'--severity',
1412
option.severity,
@@ -44,39 +42,3 @@ export function scan(
4442
stderr: ${result.stderr}`);
4543
}
4644
}
47-
48-
function validateOption(option: TrivyOption): void {
49-
validateSeverity(option.severity.split(','));
50-
validateVulnType(option.vulnType.split(','));
51-
}
52-
53-
function validateSeverity(severities: string[]): boolean {
54-
const allowedSeverities = /UNKNOWN|LOW|MEDIUM|HIGH|CRITICAL/;
55-
if (!validateArrayOption(allowedSeverities, severities)) {
56-
throw new Error(
57-
`Trivy option error: ${severities.join(',')} is unknown severity.
58-
Trivy supports UNKNOWN, LOW, MEDIUM, HIGH and CRITICAL.`
59-
);
60-
}
61-
return true;
62-
}
63-
64-
function validateVulnType(vulnTypes: string[]): boolean {
65-
const allowedVulnTypes = /os|library/;
66-
if (!validateArrayOption(allowedVulnTypes, vulnTypes)) {
67-
throw new Error(
68-
`Trivy option error: ${vulnTypes.join(',')} is unknown vuln-type.
69-
Trivy supports os and library.`
70-
);
71-
}
72-
return true;
73-
}
74-
75-
function validateArrayOption(allowedValue: RegExp, options: string[]): boolean {
76-
for (const option of options) {
77-
if (!allowedValue.test(option)) {
78-
return false;
79-
}
80-
}
81-
return true;
82-
}

0 commit comments

Comments
 (0)