From 83a1ac83b4d7472a5ad3d403a70a48eb018373da Mon Sep 17 00:00:00 2001 From: Owen Rumney Date: Fri, 14 Feb 2025 16:13:44 +0000 Subject: [PATCH] chore: version the trivy install so it's not repeatedly downloaded for each specified version of the trivy binary, download and call it the versioned name so that it can be reused if already present on the agent --- trivy-task/additionalReporting.ts | 23 +++++++++++------- trivy-task/trivyLoader.ts | 39 +++++++++++++++++-------------- trivy-task/utils.ts | 9 +++++++ 3 files changed, 45 insertions(+), 26 deletions(-) diff --git a/trivy-task/additionalReporting.ts b/trivy-task/additionalReporting.ts index cb65a08..809b6f0 100644 --- a/trivy-task/additionalReporting.ts +++ b/trivy-task/additionalReporting.ts @@ -1,5 +1,6 @@ import task = require('azure-pipelines-task-lib/task'); import { createRunner } from './trivyLoader'; +import { randomSuffix } from './utils'; const reportTypes = { sarif: { DisplayName: 'SARIF', Extension: '.json' }, @@ -20,6 +21,19 @@ export async function generateAdditionalReports(filename: string) { const inputKey = `${key}Output`; const outputKey = `${key}Report`; if (task.getBoolInput(inputKey, false)) { + if (key === 'json') { + // don't need to convert json to json + task.setVariable(outputKey, filename); + task.debug(`Uploading ${key} report...`); + const artifactKey = `${key}-${smallJobId}-${randomSuffix(8)}`; + task.uploadArtifact( + artifactKey, + filename, + `${jobId}${value.DisplayName}` + ); + continue; + } + console.log(`Generating ${key} report...`); const format = getReportFormat(key); const output = `${filename.replace(/json$/, format)}${value.Extension}`; @@ -66,12 +80,3 @@ function getReportFormat(key: string): string { return key; } } - -function randomSuffix(length: number): string { - const characters = '0123456789abcdef'; - let result = ''; - for (let i = 0; i < length; i++) { - result += characters[Math.floor(Math.random() * characters.length)]; - } - return result; -} diff --git a/trivy-task/trivyLoader.ts b/trivy-task/trivyLoader.ts index af6c322..e9c27e0 100644 --- a/trivy-task/trivyLoader.ts +++ b/trivy-task/trivyLoader.ts @@ -82,31 +82,31 @@ async function installTrivy(version: string): Promise { throw new Error('Only Linux is currently supported'); } - const url = await getArtifactURL(version); - - const bin = 'trivy'; + if (version === 'latest') { + task.debug('version set to latest, fetching latest version from GitHub'); + version = await getLatestTrivyVersion(); + } + const bin = `trivy_${stripV(version).replaceAll('.', '_')}`; + const binPath = tmpPath + bin; - const localPath = tmpPath + bin; - task.rmRF(localPath); + if (task.exist(binPath)) { + console.log('Trivy already installed, skipping installation'); + return binPath + } + const url = await getArtifactURL(version); console.log('Downloading Trivy...'); - const downloadPath = await tool.downloadTool(url, localPath); + const downloadPath = await tool.downloadTool(url); console.log('Extracting Trivy...'); await tool.extractTar(downloadPath, tmpPath); - const binPath = tmpPath + bin; - console.log('Setting permissions...'); - await task.exec('chmod', ['+x', binPath]); + task.mv(tmpPath + 'trivy', binPath); + task.execSync('chmod', ['+x', binPath]); return binPath; } async function getArtifactURL(version: string): Promise { - if (version === 'latest') { - task.debug('version set to latest, fetching latest version from GitHub'); - version = await getLatestTrivyVersion(); - } - const arch = getArchitecture(); const artifact = `trivy_${stripV(version)}_Linux-${arch}.tar.gz`; return `https://github.com/aquasecurity/trivy/releases/download/${version}/${artifact}`; @@ -122,18 +122,23 @@ async function getLatestTrivyVersion(): Promise { validateStatus: (status) => status >= 300 && status < 400, } ); - task.debug(`Response: ${JSON.stringify(response)}`); + task.debug(`Response: ${JSON.stringify(response.headers)}`); const location = response.headers['location']; + if (!location) { + throw new Error('Failed to retrieve latest version information from GitHub'); + } + const parts = location?.split('/'); if (parts) { - return parts[parts.length - 1]; + const latestVersion = parts[parts.length - 1]; + console.log(`Latest Trivy version is ${latestVersion}`); } } catch (error) { console.log( `Unable to Retrieve Latest Version information from GitHub, falling back to ${fallbackTrivyVersion}` ); if (error) { - console.error(JSON.stringify(error)); + console.log(`Error: ${error}`); } } return fallbackTrivyVersion; diff --git a/trivy-task/utils.ts b/trivy-task/utils.ts index ee09f3e..730ef14 100644 --- a/trivy-task/utils.ts +++ b/trivy-task/utils.ts @@ -29,3 +29,12 @@ export function getAquaAccount(): aquaCredentials { export function isDevMode(): boolean { return task.getBoolInput('devMode', false); } + +export function randomSuffix(length: number): string { + const characters = '0123456789abcdef'; + let result = ''; + for (let i = 0; i < length; i++) { + result += characters[Math.floor(Math.random() * characters.length)]; + } + return result; +}