diff --git a/src/index.ts b/src/index.ts index fb43db75..e8d32fdf 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,6 @@ import * as cache from '@actions/cache' import { error, getInput, info, setOutput } from '@actions/core' -import { copyFileSync, existsSync, mkdirSync } from 'fs' +import { copyFileSync, existsSync, mkdirSync, renameSync } from 'fs' import * as path from 'path' import { downloadArtifact, @@ -8,7 +8,13 @@ import { resolveExistingCommentIfFound, uploadArtifact, } from './actions' -import { callCommand, runCodesec, getOptionalEnvVariable, readMarkdownFile } from './util' +import { + callCommand, + runCodesec, + getModifiedFiles, + getOptionalEnvVariable, + readMarkdownFile, +} from './util' import { simpleGit } from 'simple-git' // Global scanner toggles - set to false to disable a scanner globally @@ -38,6 +44,15 @@ async function runAnalysis() { targetScan = 'scan' } + // Only pass modified files for PR "new" scans — this optimises scanning to only changed files + let modifiedFiles: string | undefined + if (currBranch !== '' && target === 'new') { + modifiedFiles = getModifiedFiles() + if (modifiedFiles) { + info(`Modified files for optimised scanning: ${modifiedFiles}`) + } + } + // Create scan-results directory const resultsPath = path.join(process.cwd(), 'scan-results') @@ -61,7 +76,8 @@ async function runAnalysis() { enableIacRunning, enableScaRunning, resultsPath, - targetScan + targetScan, + modifiedFiles ) if (success && targetScan !== 'new') { // Save the analysis results when not scanning the PR source branch @@ -72,6 +88,29 @@ async function runAnalysis() { info(`Failed to save cache for ${cacheKey}: ${(e as Error).message}`) } } + } else { + // Cache restored — rename files to match current targetScan if needed + const possibleNames = ['old', 'scan'] + if (enableScaRunning) { + const scaDir = path.join(resultsPath, 'sca') + for (const name of possibleNames) { + const existing = path.join(scaDir, `sca-${name}.sarif`) + if (existsSync(existing) && name !== targetScan) { + renameSync(existing, path.join(scaDir, `sca-${targetScan}.sarif`)) + break + } + } + } + if (enableIacRunning) { + const iacDir = path.join(resultsPath, 'iac') + for (const name of possibleNames) { + const existing = path.join(iacDir, `iac-${name}.json`) + if (existsSync(existing) && name !== targetScan) { + renameSync(existing, path.join(iacDir, `iac-${targetScan}.json`)) + break + } + } + } } // Upload SCA SARIF from the returned results path diff --git a/src/util.ts b/src/util.ts index 5e57a3d4..6ea5589a 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,6 +1,6 @@ import { error, getInput, info, isDebug } from '@actions/core' import { context } from '@actions/github' -import { spawn } from 'child_process' +import { spawn, spawnSync } from 'child_process' import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'fs' import * as os from 'os' import * as path from 'path' @@ -115,6 +115,31 @@ export function generateUILink() { return url } +export function getModifiedFiles(): string | undefined { + const eventPath = process.env.GITHUB_EVENT_PATH + if (!eventPath) return undefined + + let eventData: any + try { + eventData = JSON.parse(readFileSync(eventPath, 'utf8')) + } catch (e) { + info(`Failed to parse GitHub event file: ${e}`) + return undefined + } + + const baseSha = eventData.pull_request?.base?.sha + if (!baseSha) return undefined + + const result = spawnSync('git', ['diff', '--name-only', `${baseSha}...HEAD`]) + if (result.status !== 0) { + info(`Failed to get modified files: ${result.stderr?.toString()}`) + return undefined + } + + const files = result.stdout.toString().trim().split('\n').filter(Boolean).join(',') + return files || undefined +} + // runCodesec - Docker-based scanner using codesec:latest image // // Modes: @@ -130,7 +155,8 @@ export async function runCodesec( runIac: boolean = false, runSca: boolean = false, reportsDir: string, - scanTarget?: string + scanTarget?: string, + modifiedFiles?: string ): Promise { const lwAccount = getRequiredEnvVariable('LW_ACCOUNT') const lwApiKey = getRequiredEnvVariable('LW_API_KEY') @@ -167,6 +193,7 @@ export async function runCodesec( `RUN_IAC=${runIac}`, '-e', `SCAN_TARGET=${scanTarget || 'scan'}`, + ...(modifiedFiles ? ['-e', `MODIFIED_FILES=${modifiedFiles}`] : []), 'lacework/codesec:latest', 'scan', ]