Skip to content

Refactor #85

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jun 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/integration-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
- completed

env:
IMAGE_NAME: alpine:3.10.3
IMAGE_NAME: knqyf263/vuln-image

jobs:
test1:
Expand Down
99 changes: 99 additions & 0 deletions __tests__/downloader.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import * as fs from 'fs';
import { removeTrivyCmd } from './helper';
import { Downloader } from '../src/downloader';

const downloader = new Downloader();

describe('Check Platform', () => {
test('is Liniux', () => {
const result = downloader.checkPlatform('linux');
expect(result).toBe('Linux');
});

test('is Darwin', () => {
const result = downloader.checkPlatform('darwin');
expect(result).toBe('macOS');
});

test('is not linux and darwin', () => {
expect(() => {
downloader.checkPlatform('other');
}).toThrowError('Sorry, other is not supported.');
});
});

describe('getDownloadUrl', () => {
test('with latest version and linux', async () => {
const version = 'latest';
const os = 'Linux';
const result = await downloader.getDownloadUrl(version, os);
expect(result).toMatch(
/releases\/download\/v[0-9]+\.[0-9]+\.[0-9]+\/trivy_[0-9]+\.[0-9]+\.[0-9]+_Linux-64bit\.tar\.gz$/
);
});

test('with 0.18.3 and macOS', async () => {
const version = '0.18.3';
const os = 'macOS';
const result = await downloader.getDownloadUrl(version, os);
expect(result).toMatch(
/releases\/download\/v0\.18\.3\/trivy_0\.18\.3_macOS-64bit\.tar\.gz$/
);
});

test('with non-supported version', async () => {
const version = 'none';
const os = 'Linux';
await expect(downloader.getDownloadUrl(version, os)).rejects.toThrowError(
'Could not find Trivy asset that you specified.'
);
});

test('with non-supported os', async () => {
const version = 'latest';
const os = 'none';
await expect(downloader.getDownloadUrl(version, os)).rejects.toThrowError(
'Could not find Trivy asset that you specified.'
);
});
});

describe('Download trivy command', () => {
afterAll(() => {
removeTrivyCmd('__tests__');
});

test('with valid download URL and save in __tests__', async () => {
let downloadUrl = 'https://github.com/aquasecurity/trivy';
downloadUrl += '/releases/download/v0.18.3/trivy_0.18.3_Linux-64bit.tar.gz';
const savePath = './__tests__';
await expect(
downloader.downloadTrivyCmd(downloadUrl, savePath)
).resolves.toEqual(`${savePath}/trivy`);
}, 300000);

test('with invalid download URL', async () => {
const downloadUrl = 'https://github.com/this_is_invalid';
await expect(downloader.downloadTrivyCmd(downloadUrl)).rejects.toThrow();
});
});

describe('Trivy command', () => {
beforeAll(() => {
fs.writeFileSync('./trivy', '');
});

afterAll(() => {
removeTrivyCmd('.');
});

test('exists', () => {
const result = downloader.trivyExists('.');
expect(result).toBeTruthy();
});

test('does not exist', () => {
const result = downloader.trivyExists('src');
expect(result).toBeFalsy();
});
});
11 changes: 11 additions & 0 deletions __tests__/helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import * as fs from 'fs';
import { Downloader } from '../src/downloader';

const downloader = new Downloader();

export function removeTrivyCmd(path: string) {
path = path.replace(/\/trivy$/, '');
if (downloader.trivyExists(path)) {
fs.unlinkSync(`${path}/trivy`);
}
}
99 changes: 8 additions & 91 deletions __tests__/trivy.test.ts
Original file line number Diff line number Diff line change
@@ -1,98 +1,15 @@
import { Downloader, Trivy } from '../src/trivy';
import { unlinkSync, writeFileSync } from 'fs';
import * as fs from 'fs';
import { Downloader } from '../src/downloader';
import { Trivy } from '../src/trivy';
import { Vulnerability, TrivyOption } from '../src/interface';
import { removeTrivyCmd } from './helper';

const downloader = new Downloader();
const trivy = new Trivy();

function removeTrivyCmd(path: string) {
path = path.replace(/\/trivy$/, '');
if (downloader.trivyExists(path)) {
unlinkSync(`${path}/trivy`);
}
}

describe('Platform', () => {
test('is Liniux', () => {
const result = downloader['checkPlatform']('linux');
expect(result).toBe('Linux');
});

test('is Darwin', () => {
const result = downloader['checkPlatform']('darwin');
expect(result).toBe('macOS');
});

test('is not linux and darwin', () => {
expect(() => {
downloader['checkPlatform']('other');
}).toThrowError('Sorry, other is not supported.');
});
});

describe('getDownloadUrl', () => {
test('with latest version and linux', async () => {
const version = 'latest';
const os = 'Linux';
const result = await downloader['getDownloadUrl'](version, os);
expect(result).toMatch(
/releases\/download\/v[0-9]+\.[0-9]+\.[0-9]+\/trivy_[0-9]+\.[0-9]+\.[0-9]+_Linux-64bit\.tar\.gz$/
);
});

test('with 0.2.0 and macOS', async () => {
const version = '0.2.0';
const os = 'macOS';
const result = await downloader['getDownloadUrl'](version, os);
expect(result).toMatch(
/releases\/download\/v0\.2\.0\/trivy_0\.2\.0_macOS-64bit\.tar\.gz$/
);
});

test('with non-supported version', async () => {
const version = 'none';
const os = 'Linux';
await expect(
downloader['getDownloadUrl'](version, os)
).rejects.toThrowError(
'Cloud not be found a Trivy asset that you specified.'
);
});

test('with non-supported os', async () => {
const version = 'latest';
const os = 'none';
await expect(
downloader['getDownloadUrl'](version, os)
).rejects.toThrowError(
'Cloud not be found a Trivy asset that you specified.'
);
});
});

describe('Download trivy command', () => {
afterAll(() => {
removeTrivyCmd('__tests__');
});

test('with valid download URL and save in __tests__', async () => {
let downloadUrl = 'https://github.com/aquasecurity/trivy';
downloadUrl += '/releases/download/v0.2.1/trivy_0.2.1_Linux-64bit.tar.gz';
const savePath = './__tests__';
await expect(
downloader['downloadTrivyCmd'](downloadUrl, savePath)
).resolves.toEqual(`${savePath}/trivy`);
}, 300000);

test('with invalid download URL', async () => {
const downloadUrl = 'https://github.com/this_is_invalid';
await expect(downloader['downloadTrivyCmd'](downloadUrl)).rejects.toThrow();
});
});

describe('Trivy command', () => {
beforeAll(() => {
writeFileSync('./trivy', '');
fs.writeFileSync('./trivy', '');
});

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

beforeAll(async () => {
trivyPath = !downloader.trivyExists('./__tests__')
? await downloader.download('latest', './__tests__')
: './__tests__/trivy';
trivyPath = !downloader.trivyExists(__dirname)
? await downloader.download('latest', __dirname)
: `${__dirname}/trivy`;
}, 300000);

afterAll(() => {
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@
"author": "homoluctus",
"license": "MIT",
"dependencies": {
"@actions/core": "^1.2.6",
"@actions/github": "^1.1.0",
"@actions/core": "^1.2.7",
"@actions/github": "^4.0.0",
"@octokit/rest": "^18.5.6",
"node-fetch": "^2.6.1",
"tar": "^5.0.5"
},
Expand Down
116 changes: 116 additions & 0 deletions src/downloader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import fs from 'fs';
import zlib from 'zlib';
import tar from 'tar';
import * as core from '@actions/core';
import { Octokit } from '@octokit/rest';
import fetch, { Response } from 'node-fetch';

export class Downloader {
readonly trivyRepo = {
owner: 'aquasecurity',
repo: 'trivy',
};

async download(
version: string,
trivyCmdDir: string = __dirname
): Promise<string> {
const os = this.checkPlatform(process.platform);
const downloadUrl = await this.getDownloadUrl(version, os);
console.debug(`Download URL: ${downloadUrl}`);
const trivyCmdBaseDir = process.env.GITHUB_WORKSPACE || trivyCmdDir;
const trivyCmdPath = await this.downloadTrivyCmd(
downloadUrl,
trivyCmdBaseDir
);
console.debug(`Trivy Command Path: ${trivyCmdPath}`);
return trivyCmdPath;
}

checkPlatform(platform: string): string {
switch (platform) {
case 'linux':
return 'Linux';
case 'darwin':
return 'macOS';
default:
const errorMsg = `Sorry, ${platform} is not supported.
Trivy support Linux, MacOS, FreeBSD and OpenBSD.`;
throw new Error(errorMsg);
}
}

async getDownloadUrl(version: string, os: string): Promise<string> {
try {
const response = await this.getAssets(version);
const filename = `trivy_${response.version}_${os}-64bit.tar.gz`;
for (const asset of response.assets) {
if (asset.name === filename) {
return asset.browser_download_url;
}
}
throw new Error(`${filename} does not include in GitHub releases`);
} catch (err) {
core.error(err.message);

const errMsg = `Could not find Trivy asset that you specified.
Version: ${version}
OS: ${os}
`;
throw new Error(errMsg);
}
}

async getAssets(
version: string
): Promise<{
assets: any;
version: string;
}> {
let response;
const client = new Octokit();

if (version === 'latest') {
response = await client.repos.getLatestRelease({ ...this.trivyRepo });
version = response.data.tag_name.replace(/v/, '');
} else {
response = await client.repos.getReleaseByTag({
...this.trivyRepo,
tag: `v${version}`,
});
}

return { assets: response.data.assets, version };
}

async downloadTrivyCmd(
downloadUrl: string,
savedPath: string = '.'
): Promise<string> {
const response: Response = await fetch(downloadUrl);

return new Promise((resolve, reject) => {
const gunzip = zlib.createGunzip();
const extract = tar.extract({ C: savedPath }, ['trivy']);
response.body
.on('error', reject)
.pipe(gunzip)
.on('error', reject)
.pipe(extract)
.on('error', reject)
.on('finish', () => {
if (!this.trivyExists(savedPath)) {
reject('Failed to extract Trivy command file.');
}
resolve(`${savedPath}/trivy`);
});
});
}

trivyExists(targetDir: string): boolean {
const trivyCmdPaths: string[] = fs
.readdirSync(targetDir)
.filter(f => f === 'trivy');
return trivyCmdPaths.length === 1;
}
}
Loading