Skip to content

Commit a95b6de

Browse files
authored
Merge pull request #85 from lazy-actions/refactor
Refactor
2 parents 04f21ba + 3d8c771 commit a95b6de

File tree

12 files changed

+427
-490
lines changed

12 files changed

+427
-490
lines changed

.github/workflows/integration-test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ on:
77
- completed
88

99
env:
10-
IMAGE_NAME: alpine:3.10.3
10+
IMAGE_NAME: knqyf263/vuln-image
1111

1212
jobs:
1313
test1:

__tests__/downloader.test.ts

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import * as fs from 'fs';
2+
import { removeTrivyCmd } from './helper';
3+
import { Downloader } from '../src/downloader';
4+
5+
const downloader = new Downloader();
6+
7+
describe('Check Platform', () => {
8+
test('is Liniux', () => {
9+
const result = downloader.checkPlatform('linux');
10+
expect(result).toBe('Linux');
11+
});
12+
13+
test('is Darwin', () => {
14+
const result = downloader.checkPlatform('darwin');
15+
expect(result).toBe('macOS');
16+
});
17+
18+
test('is not linux and darwin', () => {
19+
expect(() => {
20+
downloader.checkPlatform('other');
21+
}).toThrowError('Sorry, other is not supported.');
22+
});
23+
});
24+
25+
describe('getDownloadUrl', () => {
26+
test('with latest version and linux', async () => {
27+
const version = 'latest';
28+
const os = 'Linux';
29+
const result = await downloader.getDownloadUrl(version, os);
30+
expect(result).toMatch(
31+
/releases\/download\/v[0-9]+\.[0-9]+\.[0-9]+\/trivy_[0-9]+\.[0-9]+\.[0-9]+_Linux-64bit\.tar\.gz$/
32+
);
33+
});
34+
35+
test('with 0.18.3 and macOS', async () => {
36+
const version = '0.18.3';
37+
const os = 'macOS';
38+
const result = await downloader.getDownloadUrl(version, os);
39+
expect(result).toMatch(
40+
/releases\/download\/v0\.18\.3\/trivy_0\.18\.3_macOS-64bit\.tar\.gz$/
41+
);
42+
});
43+
44+
test('with non-supported version', async () => {
45+
const version = 'none';
46+
const os = 'Linux';
47+
await expect(downloader.getDownloadUrl(version, os)).rejects.toThrowError(
48+
'Could not find Trivy asset that you specified.'
49+
);
50+
});
51+
52+
test('with non-supported os', async () => {
53+
const version = 'latest';
54+
const os = 'none';
55+
await expect(downloader.getDownloadUrl(version, os)).rejects.toThrowError(
56+
'Could not find Trivy asset that you specified.'
57+
);
58+
});
59+
});
60+
61+
describe('Download trivy command', () => {
62+
afterAll(() => {
63+
removeTrivyCmd('__tests__');
64+
});
65+
66+
test('with valid download URL and save in __tests__', async () => {
67+
let downloadUrl = 'https://github.com/aquasecurity/trivy';
68+
downloadUrl += '/releases/download/v0.18.3/trivy_0.18.3_Linux-64bit.tar.gz';
69+
const savePath = './__tests__';
70+
await expect(
71+
downloader.downloadTrivyCmd(downloadUrl, savePath)
72+
).resolves.toEqual(`${savePath}/trivy`);
73+
}, 300000);
74+
75+
test('with invalid download URL', async () => {
76+
const downloadUrl = 'https://github.com/this_is_invalid';
77+
await expect(downloader.downloadTrivyCmd(downloadUrl)).rejects.toThrow();
78+
});
79+
});
80+
81+
describe('Trivy command', () => {
82+
beforeAll(() => {
83+
fs.writeFileSync('./trivy', '');
84+
});
85+
86+
afterAll(() => {
87+
removeTrivyCmd('.');
88+
});
89+
90+
test('exists', () => {
91+
const result = downloader.trivyExists('.');
92+
expect(result).toBeTruthy();
93+
});
94+
95+
test('does not exist', () => {
96+
const result = downloader.trivyExists('src');
97+
expect(result).toBeFalsy();
98+
});
99+
});

__tests__/helper.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import * as fs from 'fs';
2+
import { Downloader } from '../src/downloader';
3+
4+
const downloader = new Downloader();
5+
6+
export function removeTrivyCmd(path: string) {
7+
path = path.replace(/\/trivy$/, '');
8+
if (downloader.trivyExists(path)) {
9+
fs.unlinkSync(`${path}/trivy`);
10+
}
11+
}

__tests__/trivy.test.ts

Lines changed: 8 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,98 +1,15 @@
1-
import { Downloader, Trivy } from '../src/trivy';
2-
import { unlinkSync, writeFileSync } from 'fs';
1+
import * as fs from 'fs';
2+
import { Downloader } from '../src/downloader';
3+
import { Trivy } from '../src/trivy';
34
import { Vulnerability, TrivyOption } from '../src/interface';
5+
import { removeTrivyCmd } from './helper';
46

57
const downloader = new Downloader();
68
const trivy = new Trivy();
79

8-
function removeTrivyCmd(path: string) {
9-
path = path.replace(/\/trivy$/, '');
10-
if (downloader.trivyExists(path)) {
11-
unlinkSync(`${path}/trivy`);
12-
}
13-
}
14-
15-
describe('Platform', () => {
16-
test('is Liniux', () => {
17-
const result = downloader['checkPlatform']('linux');
18-
expect(result).toBe('Linux');
19-
});
20-
21-
test('is Darwin', () => {
22-
const result = downloader['checkPlatform']('darwin');
23-
expect(result).toBe('macOS');
24-
});
25-
26-
test('is not linux and darwin', () => {
27-
expect(() => {
28-
downloader['checkPlatform']('other');
29-
}).toThrowError('Sorry, other is not supported.');
30-
});
31-
});
32-
33-
describe('getDownloadUrl', () => {
34-
test('with latest version and linux', async () => {
35-
const version = 'latest';
36-
const os = 'Linux';
37-
const result = await downloader['getDownloadUrl'](version, os);
38-
expect(result).toMatch(
39-
/releases\/download\/v[0-9]+\.[0-9]+\.[0-9]+\/trivy_[0-9]+\.[0-9]+\.[0-9]+_Linux-64bit\.tar\.gz$/
40-
);
41-
});
42-
43-
test('with 0.2.0 and macOS', async () => {
44-
const version = '0.2.0';
45-
const os = 'macOS';
46-
const result = await downloader['getDownloadUrl'](version, os);
47-
expect(result).toMatch(
48-
/releases\/download\/v0\.2\.0\/trivy_0\.2\.0_macOS-64bit\.tar\.gz$/
49-
);
50-
});
51-
52-
test('with non-supported version', async () => {
53-
const version = 'none';
54-
const os = 'Linux';
55-
await expect(
56-
downloader['getDownloadUrl'](version, os)
57-
).rejects.toThrowError(
58-
'Cloud not be found a Trivy asset that you specified.'
59-
);
60-
});
61-
62-
test('with non-supported os', async () => {
63-
const version = 'latest';
64-
const os = 'none';
65-
await expect(
66-
downloader['getDownloadUrl'](version, os)
67-
).rejects.toThrowError(
68-
'Cloud not be found a Trivy asset that you specified.'
69-
);
70-
});
71-
});
72-
73-
describe('Download trivy command', () => {
74-
afterAll(() => {
75-
removeTrivyCmd('__tests__');
76-
});
77-
78-
test('with valid download URL and save in __tests__', async () => {
79-
let downloadUrl = 'https://github.com/aquasecurity/trivy';
80-
downloadUrl += '/releases/download/v0.2.1/trivy_0.2.1_Linux-64bit.tar.gz';
81-
const savePath = './__tests__';
82-
await expect(
83-
downloader['downloadTrivyCmd'](downloadUrl, savePath)
84-
).resolves.toEqual(`${savePath}/trivy`);
85-
}, 300000);
86-
87-
test('with invalid download URL', async () => {
88-
const downloadUrl = 'https://github.com/this_is_invalid';
89-
await expect(downloader['downloadTrivyCmd'](downloadUrl)).rejects.toThrow();
90-
});
91-
});
92-
9310
describe('Trivy command', () => {
9411
beforeAll(() => {
95-
writeFileSync('./trivy', '');
12+
fs.writeFileSync('./trivy', '');
9613
});
9714

9815
afterAll(() => {
@@ -115,9 +32,9 @@ describe('Trivy scan', () => {
11532
const image: string = 'alpine:3.10';
11633

11734
beforeAll(async () => {
118-
trivyPath = !downloader.trivyExists('./__tests__')
119-
? await downloader.download('latest', './__tests__')
120-
: './__tests__/trivy';
35+
trivyPath = !downloader.trivyExists(__dirname)
36+
? await downloader.download('latest', __dirname)
37+
: `${__dirname}/trivy`;
12138
}, 300000);
12239

12340
afterAll(() => {

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@
2222
"author": "homoluctus",
2323
"license": "MIT",
2424
"dependencies": {
25-
"@actions/core": "^1.2.6",
26-
"@actions/github": "^1.1.0",
25+
"@actions/core": "^1.2.7",
26+
"@actions/github": "^4.0.0",
27+
"@octokit/rest": "^18.5.6",
2728
"node-fetch": "^2.6.1",
2829
"tar": "^5.0.5"
2930
},

src/downloader.ts

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import fs from 'fs';
2+
import zlib from 'zlib';
3+
import tar from 'tar';
4+
import * as core from '@actions/core';
5+
import { Octokit } from '@octokit/rest';
6+
import fetch, { Response } from 'node-fetch';
7+
8+
export class Downloader {
9+
readonly trivyRepo = {
10+
owner: 'aquasecurity',
11+
repo: 'trivy',
12+
};
13+
14+
async download(
15+
version: string,
16+
trivyCmdDir: string = __dirname
17+
): Promise<string> {
18+
const os = this.checkPlatform(process.platform);
19+
const downloadUrl = await this.getDownloadUrl(version, os);
20+
console.debug(`Download URL: ${downloadUrl}`);
21+
const trivyCmdBaseDir = process.env.GITHUB_WORKSPACE || trivyCmdDir;
22+
const trivyCmdPath = await this.downloadTrivyCmd(
23+
downloadUrl,
24+
trivyCmdBaseDir
25+
);
26+
console.debug(`Trivy Command Path: ${trivyCmdPath}`);
27+
return trivyCmdPath;
28+
}
29+
30+
checkPlatform(platform: string): string {
31+
switch (platform) {
32+
case 'linux':
33+
return 'Linux';
34+
case 'darwin':
35+
return 'macOS';
36+
default:
37+
const errorMsg = `Sorry, ${platform} is not supported.
38+
Trivy support Linux, MacOS, FreeBSD and OpenBSD.`;
39+
throw new Error(errorMsg);
40+
}
41+
}
42+
43+
async getDownloadUrl(version: string, os: string): Promise<string> {
44+
try {
45+
const response = await this.getAssets(version);
46+
const filename = `trivy_${response.version}_${os}-64bit.tar.gz`;
47+
for (const asset of response.assets) {
48+
if (asset.name === filename) {
49+
return asset.browser_download_url;
50+
}
51+
}
52+
throw new Error(`${filename} does not include in GitHub releases`);
53+
} catch (err) {
54+
core.error(err.message);
55+
56+
const errMsg = `Could not find Trivy asset that you specified.
57+
Version: ${version}
58+
OS: ${os}
59+
`;
60+
throw new Error(errMsg);
61+
}
62+
}
63+
64+
async getAssets(
65+
version: string
66+
): Promise<{
67+
assets: any;
68+
version: string;
69+
}> {
70+
let response;
71+
const client = new Octokit();
72+
73+
if (version === 'latest') {
74+
response = await client.repos.getLatestRelease({ ...this.trivyRepo });
75+
version = response.data.tag_name.replace(/v/, '');
76+
} else {
77+
response = await client.repos.getReleaseByTag({
78+
...this.trivyRepo,
79+
tag: `v${version}`,
80+
});
81+
}
82+
83+
return { assets: response.data.assets, version };
84+
}
85+
86+
async downloadTrivyCmd(
87+
downloadUrl: string,
88+
savedPath: string = '.'
89+
): Promise<string> {
90+
const response: Response = await fetch(downloadUrl);
91+
92+
return new Promise((resolve, reject) => {
93+
const gunzip = zlib.createGunzip();
94+
const extract = tar.extract({ C: savedPath }, ['trivy']);
95+
response.body
96+
.on('error', reject)
97+
.pipe(gunzip)
98+
.on('error', reject)
99+
.pipe(extract)
100+
.on('error', reject)
101+
.on('finish', () => {
102+
if (!this.trivyExists(savedPath)) {
103+
reject('Failed to extract Trivy command file.');
104+
}
105+
resolve(`${savedPath}/trivy`);
106+
});
107+
});
108+
}
109+
110+
trivyExists(targetDir: string): boolean {
111+
const trivyCmdPaths: string[] = fs
112+
.readdirSync(targetDir)
113+
.filter(f => f === 'trivy');
114+
return trivyCmdPaths.length === 1;
115+
}
116+
}

0 commit comments

Comments
 (0)