From 389bb17def41ca498082ba4ab01bd93486020223 Mon Sep 17 00:00:00 2001 From: Teodor-Ioan Baltoi Date: Mon, 26 Jan 2026 14:10:51 +0000 Subject: [PATCH 01/15] initial basis for docker image support --- src/index.ts | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/util.ts | 45 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 95 insertions(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 00f79bc5..ba81459f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,12 +10,14 @@ import { compareResults } from './tool' import { callCommand, callLaceworkCli, + codesecRun, debug, getActionRef, getMsSinceStart, getOptionalEnvVariable, getRequiredEnvVariable, getRunUrl, + readMarkdownFile, telemetryCollector, } from './util' @@ -43,6 +45,16 @@ async function runAnalysis() { telemetryCollector.addField('tools', 'sca') const toUpload: string[] = [] + + // codesec-integrations method start + var targetScan = target + if (target == 'push') { + targetScan = 'scan' + } + await codesecRun("scan", false, false, targetScan) + + // codesec-integrations method end + // command to print both sarif and lwjson formats var args = ['sca', 'scan', '.', '-o', scaDir, '--formats', 'sarif,lw-json', '--deployment', 'ci'] if (target === 'push') { @@ -52,6 +64,14 @@ async function runAnalysis() { args.push('--debug') } await callLaceworkCli(...args) + + // codesec-integrations start + + var scaSarifReportIntegrations = `scan-results/sca/sca-${targetScan}.sarif` + args = [scaSarifReportIntegrations, scaReport] + + // codesec-integrations end + // make a copy of the sarif file args = [scaSarifReport, scaReport] await callCommand('cp', ...args) @@ -71,6 +91,37 @@ async function runAnalysis() { async function displayResults() { info('Displaying results') + // codesec-integrations start + + // we call compare on an already twice scanned repo with the new/old target. + if ((existsSync("scan-results/sca/sca-new.sarif") && existsSync("scan-results/sca/sca-old.sarif")) || + (existsSync("scan-results/iac/iac-new.sarif") && existsSync("scan-results/sca/iac-old.sarif"))) { + await codesecRun("compare", false, false) + var mergedOutput = "scan-results/compare/merged-compare.md" + // If agreed to be able to run only one type, we need to revisit the conditional to take into account only one type of scanning as well + // var scaOutput = "scan-results/compare/sca-compare.md" + // var iacOutput = "scan-results/compare/iac-compare.md" + if (existsSync(mergedOutput)) { + var message: string = await readMarkdownFile(mergedOutput) + + // Check if compare contains "Found ..." that indicates there are newly found violations + const hasViolations = /Found\s+[1-9]\d*\s+/.test(message); + if (hasViolations) { + info('Posting comment to GitHub PR as there were new issues introduced:') + const commentUrl = await postCommentIfInPr(message) + if (commentUrl !== undefined) { + setOutput('posted-comment', commentUrl) + } + } else { + await resolveExistingCommentIfFound() + } + + } + } else { + throw new Error('SARIF file not found for SCA or IAC') + } + + // codesec-integrations end const downloadStart = Date.now() const artifactOld = await downloadArtifact('results-old') const artifactNew = await downloadArtifact('results-new') diff --git a/src/util.ts b/src/util.ts index d213c790..eb37f782 100644 --- a/src/util.ts +++ b/src/util.ts @@ -2,7 +2,7 @@ import { error, getInput, info, isDebug } from '@actions/core' import { context } from '@actions/github' import { spawn } from 'child_process' import { TelemetryCollector } from './telemetry' -import { readFileSync } from 'fs' +import { readFileSync, readFile } from 'fs' export const telemetryCollector = new TelemetryCollector() @@ -110,3 +110,46 @@ export function generateUILink() { return url } + +// codesecTool: this method is to be used in 3 ways depending on action and scanTarget +// 1. action: scan, scanTarget: new/old -> will produce an analysis report that will be used in generating the PR comment +// 2. action: scan, scanTarget: scan -> will scan the repo and send the results back to lacework (use in scheduled events) +// 3. action: compare -> will use the previously generated new/old targets to compare them and generate the diffed markdown that will be displayed in the PR comment +export async function codesecRun(action: string, runIac: boolean = true, runSca: boolean = true, scanTarget?: string): Promise { + const dockerArgs = [ + 'run', + '--rm', + '-v', + '/var/run/docker.sock:/var/run/docker.sock', + '-v', + `${process.cwd()}:/workspace`, + '-e', + `HOST_REPO_PATH=${process.cwd()}`, + '-e', + `ACCOUNT=${getRequiredEnvVariable('LW_ACCOUNT_NAME')}`, + '-e', + `API_KEY=${getRequiredEnvVariable('LW_API_KEY')}`, + '-e', + `SECRET=${getRequiredEnvVariable('LW_API_SECRET')}`, + '-e', + `RUN_IAC=${runIac}`, + '-e', + `RUN_SCA=${runSca}`, + '-e', + `SCAN_TARGET=${scanTarget}`, + 'codesec-integrations:test', + `${action}` + ] + + info('Running codesec-integrations') + await callCommand('docker', ...dockerArgs) +} + +export async function readMarkdownFile(filePath: string): Promise { + try { + const content = await readFile(filePath, 'utf-8'); + return content; + } catch (error) { + throw new Error(`Failed to read scanner output file: ${error}`); + } +} From 9f37f4d53ec104ac1d6298b5dc6141b22702788f Mon Sep 17 00:00:00 2001 From: Teodor-Ioan Baltoi Date: Mon, 26 Jan 2026 14:23:50 +0000 Subject: [PATCH 02/15] initial basis for docker image support --- src/index.ts | 29 +++++++++++++++-------------- src/util.ts | 19 ++++++++++++------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/index.ts b/src/index.ts index ba81459f..94827a7a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -45,15 +45,14 @@ async function runAnalysis() { telemetryCollector.addField('tools', 'sca') const toUpload: string[] = [] - // codesec-integrations method start var targetScan = target if (target == 'push') { targetScan = 'scan' } - await codesecRun("scan", false, false, targetScan) + await codesecRun('scan', false, false, targetScan) - // codesec-integrations method end + // codesec-integrations method end // command to print both sarif and lwjson formats var args = ['sca', 'scan', '.', '-o', scaDir, '--formats', 'sarif,lw-json', '--deployment', 'ci'] @@ -65,8 +64,8 @@ async function runAnalysis() { } await callLaceworkCli(...args) - // codesec-integrations start - + // codesec-integrations start + var scaSarifReportIntegrations = `scan-results/sca/sca-${targetScan}.sarif` args = [scaSarifReportIntegrations, scaReport] @@ -93,11 +92,14 @@ async function displayResults() { info('Displaying results') // codesec-integrations start - // we call compare on an already twice scanned repo with the new/old target. - if ((existsSync("scan-results/sca/sca-new.sarif") && existsSync("scan-results/sca/sca-old.sarif")) || - (existsSync("scan-results/iac/iac-new.sarif") && existsSync("scan-results/sca/iac-old.sarif"))) { - await codesecRun("compare", false, false) - var mergedOutput = "scan-results/compare/merged-compare.md" + // we call compare on an already twice scanned repo with the new/old target. + if ( + (existsSync('scan-results/sca/sca-new.sarif') && + existsSync('scan-results/sca/sca-old.sarif')) || + (existsSync('scan-results/iac/iac-new.sarif') && existsSync('scan-results/sca/iac-old.sarif')) + ) { + await codesecRun('compare', false, false) + var mergedOutput = 'scan-results/compare/merged-compare.md' // If agreed to be able to run only one type, we need to revisit the conditional to take into account only one type of scanning as well // var scaOutput = "scan-results/compare/sca-compare.md" // var iacOutput = "scan-results/compare/iac-compare.md" @@ -105,7 +107,7 @@ async function displayResults() { var message: string = await readMarkdownFile(mergedOutput) // Check if compare contains "Found ..." that indicates there are newly found violations - const hasViolations = /Found\s+[1-9]\d*\s+/.test(message); + const hasViolations = /Found\s+[1-9]\d*\s+/.test(message) if (hasViolations) { info('Posting comment to GitHub PR as there were new issues introduced:') const commentUrl = await postCommentIfInPr(message) @@ -115,13 +117,12 @@ async function displayResults() { } else { await resolveExistingCommentIfFound() } - } } else { throw new Error('SARIF file not found for SCA or IAC') } - - // codesec-integrations end + + // codesec-integrations end const downloadStart = Date.now() const artifactOld = await downloadArtifact('results-old') const artifactNew = await downloadArtifact('results-new') diff --git a/src/util.ts b/src/util.ts index eb37f782..a176ada1 100644 --- a/src/util.ts +++ b/src/util.ts @@ -115,7 +115,12 @@ export function generateUILink() { // 1. action: scan, scanTarget: new/old -> will produce an analysis report that will be used in generating the PR comment // 2. action: scan, scanTarget: scan -> will scan the repo and send the results back to lacework (use in scheduled events) // 3. action: compare -> will use the previously generated new/old targets to compare them and generate the diffed markdown that will be displayed in the PR comment -export async function codesecRun(action: string, runIac: boolean = true, runSca: boolean = true, scanTarget?: string): Promise { +export async function codesecRun( + action: string, + runIac: boolean = true, + runSca: boolean = true, + scanTarget?: string +): Promise { const dockerArgs = [ 'run', '--rm', @@ -135,10 +140,10 @@ export async function codesecRun(action: string, runIac: boolean = true, runSca: `RUN_IAC=${runIac}`, '-e', `RUN_SCA=${runSca}`, - '-e', + '-e', `SCAN_TARGET=${scanTarget}`, - 'codesec-integrations:test', - `${action}` + 'codesec-integrations:test', + `${action}`, ] info('Running codesec-integrations') @@ -147,9 +152,9 @@ export async function codesecRun(action: string, runIac: boolean = true, runSca: export async function readMarkdownFile(filePath: string): Promise { try { - const content = await readFile(filePath, 'utf-8'); - return content; + const content = await readFile(filePath, 'utf-8') + return content } catch (error) { - throw new Error(`Failed to read scanner output file: ${error}`); + throw new Error(`Failed to read scanner output file: ${error}`) } } From 540626b3fb6eee502f5469026e651f29b00a81ea Mon Sep 17 00:00:00 2001 From: Teodor-Ioan Baltoi Date: Thu, 5 Mar 2026 14:29:38 +0000 Subject: [PATCH 03/15] trying the new codesec image --- src/index.ts | 169 ++++++++++++++++++------------------------- src/tool.ts | 5 ++ src/util.ts | 198 +++++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 238 insertions(+), 134 deletions(-) diff --git a/src/index.ts b/src/index.ts index 94827a7a..5f889287 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,17 +1,12 @@ import { error, getInput, info, setOutput, warning } from '@actions/core' import { appendFileSync, existsSync } from 'fs' import { - downloadArtifact, postCommentIfInPr, resolveExistingCommentIfFound, uploadArtifact, } from './actions' -import { compareResults } from './tool' import { - callCommand, - callLaceworkCli, codesecRun, - debug, getActionRef, getMsSinceStart, getOptionalEnvVariable, @@ -21,12 +16,11 @@ import { telemetryCollector, } from './util' -import path from 'path' - -const scaSarifReport = 'scaReport/output.sarif' -const scaReport = 'sca.sarif' -const scaLWJSONReport = 'scaReport/output-lw.json' -const scaDir = 'scaReport' +// Constants for old Lacework CLI flow - kept for reference when re-enabling +// const scaSarifReport = 'scaReport/output.sarif' +// const scaReport = 'sca.sarif' +// const scaLWJSONReport = 'scaReport/output-lw.json' +// const scaDir = 'scaReport' async function runAnalysis() { const target = getInput('target') @@ -45,37 +39,36 @@ async function runAnalysis() { telemetryCollector.addField('tools', 'sca') const toUpload: string[] = [] - // codesec-integrations method start + // Run codesec Docker scanner + // targetScan: 'new'/'old' for PR mode, 'scan' for push mode (uploads to Lacework UI) var targetScan = target if (target == 'push') { targetScan = 'scan' } - await codesecRun('scan', false, false, targetScan) - - // codesec-integrations method end - - // command to print both sarif and lwjson formats - var args = ['sca', 'scan', '.', '-o', scaDir, '--formats', 'sarif,lw-json', '--deployment', 'ci'] - if (target === 'push') { - args.push('--save-results') + await codesecRun('scan', true, true, targetScan) + + // TODO: Copy SARIF from codesec results to expected location for artifact upload + // The codesec scanner outputs to scan-results/sca/sca-{target}.sarif + // For now, keeping the old Lacework CLI flow commented below as fallback + + /* + * OLD LACEWORK CLI FLOW - Commented out, to be removed once codesec is fully tested + * + * var args = ['sca', 'scan', '.', '-o', scaDir, '--formats', 'sarif,lw-json', '--deployment', 'ci'] + * if (target === 'push') { args.push('--save-results') } + * if (debug()) { args.push('--debug') } + * await callLaceworkCli(...args) + * args = [scaSarifReport, scaReport] + * await callCommand('cp', ...args) + * toUpload.push(scaReport) + */ + + // Placeholder: upload the SARIF from codesec results + // TODO: Update this path once codesec output location is finalized + const scaSarifFromCodesec = `scan-results/sca/sca-${targetScan}.sarif` + if (existsSync(scaSarifFromCodesec)) { + toUpload.push(scaSarifFromCodesec) } - if (debug()) { - args.push('--debug') - } - await callLaceworkCli(...args) - - // codesec-integrations start - - var scaSarifReportIntegrations = `scan-results/sca/sca-${targetScan}.sarif` - args = [scaSarifReportIntegrations, scaReport] - - // codesec-integrations end - - // make a copy of the sarif file - args = [scaSarifReport, scaReport] - await callCommand('cp', ...args) - - toUpload.push(scaReport) const uploadStart = Date.now() const artifactPrefix = getInput('artifact-prefix') @@ -90,78 +83,58 @@ async function runAnalysis() { async function displayResults() { info('Displaying results') - // codesec-integrations start - - // we call compare on an already twice scanned repo with the new/old target. - if ( - (existsSync('scan-results/sca/sca-new.sarif') && - existsSync('scan-results/sca/sca-old.sarif')) || - (existsSync('scan-results/iac/iac-new.sarif') && existsSync('scan-results/sca/iac-old.sarif')) - ) { - await codesecRun('compare', false, false) - var mergedOutput = 'scan-results/compare/merged-compare.md' - // If agreed to be able to run only one type, we need to revisit the conditional to take into account only one type of scanning as well - // var scaOutput = "scan-results/compare/sca-compare.md" - // var iacOutput = "scan-results/compare/iac-compare.md" - if (existsSync(mergedOutput)) { - var message: string = await readMarkdownFile(mergedOutput) - - // Check if compare contains "Found ..." that indicates there are newly found violations - const hasViolations = /Found\s+[1-9]\d*\s+/.test(message) - if (hasViolations) { - info('Posting comment to GitHub PR as there were new issues introduced:') - const commentUrl = await postCommentIfInPr(message) - if (commentUrl !== undefined) { - setOutput('posted-comment', commentUrl) - } - } else { - await resolveExistingCommentIfFound() - } - } - } else { - throw new Error('SARIF file not found for SCA or IAC') + + // Use codesec compare - expects scan-results/sca/sca-{new,old}.sarif to exist + // These are produced by the previous scan steps with target='new' and target='old' + const scaOldExists = existsSync('scan-results/sca/sca-old.sarif') + const scaNewExists = existsSync('scan-results/sca/sca-new.sarif') + + if (!scaOldExists || !scaNewExists) { + throw new Error( + `SARIF files not found for comparison. old=${scaOldExists}, new=${scaNewExists}` + ) } - // codesec-integrations end - const downloadStart = Date.now() - const artifactOld = await downloadArtifact('results-old') - const artifactNew = await downloadArtifact('results-new') - telemetryCollector.addField( - 'duration.download-artifacts', - (Date.now() - downloadStart).toString() - ) - const sarifFileOld = path.join(artifactOld, scaReport) - const sarifFileNew = path.join(artifactNew, scaReport) - - const issuesByTool: { [tool: string]: string } = {} - if (existsSync(sarifFileOld) && existsSync(sarifFileNew)) { - issuesByTool['sca'] = await compareResults('sca', sarifFileOld, sarifFileNew) - } else { - throw new Error('SARIF file not found for SCA') + // Run codesec compare mode + await codesecRun('compare', false, false) + + // Read the merged comparison output + const mergedOutput = 'scan-results/compare/merged-compare.md' + if (!existsSync(mergedOutput)) { + throw new Error(`Comparison output not found at ${mergedOutput}`) } - const commentStart = Date.now() - if (Object.values(issuesByTool).some((x) => x.length > 0) && getInput('token').length > 0) { - info('Posting comment to GitHub PR as there were new issues introduced:') - let message = '' - for (const [, issues] of Object.entries(issuesByTool)) { - if (issues.length > 0) { - message += issues - } - } - if (getInput('footer') !== '') { - message += '\n\n' + getInput('footer') - } - info(message) + const message = readMarkdownFile(mergedOutput) + + // Check if there are new violations (non-zero count in "Found N new potential violations") + const hasViolations = /Found\s+[1-9]\d*\s+/.test(message) + + if (hasViolations && getInput('token').length > 0) { + info('Posting comment to GitHub PR as there were new issues introduced') const commentUrl = await postCommentIfInPr(message) if (commentUrl !== undefined) { setOutput('posted-comment', commentUrl) } } else { + // No new violations or no token - resolve existing comment if found await resolveExistingCommentIfFound() } - telemetryCollector.addField('duration.comment', (Date.now() - commentStart).toString()) - setOutput(`display-completed`, true) + + setOutput('display-completed', true) + + /* + * OLD FLOW - Commented out, to be removed once codesec is fully tested + * + * const downloadStart = Date.now() + * const artifactOld = await downloadArtifact('results-old') + * const artifactNew = await downloadArtifact('results-new') + * const sarifFileOld = path.join(artifactOld, scaReport) + * const sarifFileNew = path.join(artifactNew, scaReport) + * const compareMessage = await compareResults(sarifFileOld, sarifFileNew) + * if (compareMessage.length > 0 && getInput('token').length > 0) { + * await postCommentIfInPr(compareMessage) + * } + */ } async function main() { diff --git a/src/tool.ts b/src/tool.ts index 81935c53..d08edada 100644 --- a/src/tool.ts +++ b/src/tool.ts @@ -1,3 +1,7 @@ +// DISABLED: This file is no longer used - codesecRun('compare') handles comparison +// Keeping commented for reference when migrating back to Lacework CLI + +/* import { endGroup, startGroup } from '@actions/core' import { existsSync, readFileSync } from 'fs' import { callLaceworkCli, debug, generateUILink } from './util' @@ -31,3 +35,4 @@ export async function compareResults( endGroup() return existsSync(`${tool}.md`) ? readFileSync(`${tool}.md`, 'utf8') : '' } +*/ diff --git a/src/util.ts b/src/util.ts index a176ada1..2adb4323 100644 --- a/src/util.ts +++ b/src/util.ts @@ -2,7 +2,10 @@ import { error, getInput, info, isDebug } from '@actions/core' import { context } from '@actions/github' import { spawn } from 'child_process' import { TelemetryCollector } from './telemetry' -import { readFileSync, readFile } from 'fs' +import { readFileSync } from 'fs' +import * as tmp from 'tmp' +import * as path from 'path' +import { mkdirSync, existsSync } from 'fs' export const telemetryCollector = new TelemetryCollector() @@ -111,49 +114,172 @@ export function generateUILink() { return url } -// codesecTool: this method is to be used in 3 ways depending on action and scanTarget -// 1. action: scan, scanTarget: new/old -> will produce an analysis report that will be used in generating the PR comment -// 2. action: scan, scanTarget: scan -> will scan the repo and send the results back to lacework (use in scheduled events) -// 3. action: compare -> will use the previously generated new/old targets to compare them and generate the diffed markdown that will be displayed in the PR comment +// codesecRun - Docker-based scanner using codesec:latest image +// Follows the pattern from test-unified-scanner.sh for CI runner compatibility +// +// Modes: +// 1. action='scan', scanTarget='new'/'old' -> produces analysis for PR comment +// 2. action='scan', scanTarget='scan' -> full scan for scheduled events (uploads to Lacework) +// 3. action='compare' -> compares new/old results, generates diff markdown for PR comment +// +// Parameters: +// - runIac/runSca: which scanners to enable (default false - enable when ready to test) +// - scanTarget: 'new', 'old', or 'scan' depending on mode export async function codesecRun( action: string, - runIac: boolean = true, - runSca: boolean = true, + runIac: boolean = false, + runSca: boolean = false, scanTarget?: string ): Promise { - const dockerArgs = [ - 'run', - '--rm', - '-v', - '/var/run/docker.sock:/var/run/docker.sock', - '-v', - `${process.cwd()}:/workspace`, - '-e', - `HOST_REPO_PATH=${process.cwd()}`, - '-e', - `ACCOUNT=${getRequiredEnvVariable('LW_ACCOUNT_NAME')}`, - '-e', - `API_KEY=${getRequiredEnvVariable('LW_API_KEY')}`, - '-e', - `SECRET=${getRequiredEnvVariable('LW_API_SECRET')}`, - '-e', - `RUN_IAC=${runIac}`, - '-e', - `RUN_SCA=${runSca}`, - '-e', - `SCAN_TARGET=${scanTarget}`, - 'codesec-integrations:test', - `${action}`, - ] + const lwAccount = getRequiredEnvVariable('LW_ACCOUNT_NAME') + const lwApiKey = getRequiredEnvVariable('LW_API_KEY') + const lwApiSecret = getRequiredEnvVariable('LW_API_SECRET') + + // Create temp directory for scan results (auto-cleaned after use) + const tmpDir = tmp.dirSync({ unsafeCleanup: true }) + const reportsDir = path.join(tmpDir.name, 'scan-results') + + try { + if (action === 'scan') { + // Scan mode: mount repo as /app/src, results go to /tmp/scan-results/ in container + const containerName = `codesec-scan-${scanTarget || 'default'}` + + info(`Running codesec scan (target: ${scanTarget || 'scan'})`) + + // Run the scanner + const dockerArgs = [ + 'run', + '--name', + containerName, + '-v', + `${process.cwd()}:/app/src`, + '-e', + `WORKSPACE=src`, + '-e', + `LW_ACCOUNT=${lwAccount}`, + '-e', + `LW_API_KEY=${lwApiKey}`, + '-e', + `LW_API_SECRET=${lwApiSecret}`, + '-e', + `RUN_SCA=${runSca}`, + '-e', + `RUN_IAC=${runIac}`, + '-e', + `SCAN_TARGET=${scanTarget || 'scan'}`, + 'codesec:latest', + 'scan', + ] + + await callCommand('docker', ...dockerArgs) + + // Copy results out of container to temp dir + if (runSca) { + const scaDir = path.join(reportsDir, 'sca') + mkdirSync(scaDir, { recursive: true }) + await callCommand( + 'docker', + 'container', + 'cp', + `${containerName}:/tmp/scan-results/sca/sca-${scanTarget || 'scan'}.sarif`, + path.join(scaDir, `sca-${scanTarget || 'scan'}.sarif`) + ) + } + + if (runIac) { + const iacDir = path.join(reportsDir, 'iac') + mkdirSync(iacDir, { recursive: true }) + await callCommand( + 'docker', + 'container', + 'cp', + `${containerName}:/tmp/scan-results/iac/iac-${scanTarget || 'scan'}.json`, + path.join(iacDir, `iac-${scanTarget || 'scan'}.json`) + ) + } - info('Running codesec-integrations') - await callCommand('docker', ...dockerArgs) + // Cleanup container + await callCommand('docker', 'rm', containerName) + } else if (action === 'compare') { + // Compare mode: copy scan results into place first, then run compare + const srcDir = path.join(reportsDir, 'sca') + const scaOld = path.join(srcDir, 'sca-old.sarif') + const scaNew = path.join(srcDir, 'sca-new.sarif') + + // Verify required files exist before running compare + if (!existsSync(scaOld) || !existsSync(scaNew)) { + throw new Error(`Compare requires sca-old.sarif and sca-new.sarif. Found: old=${existsSync(scaOld)}, new=${existsSync(scaNew)}`) + } + + const containerName = 'codesec-compare' + + info('Running codesec compare') + + // Note: mounts both the repo and the scan-results directory separately + const dockerArgs = [ + 'run', + '--name', + containerName, + '-v', + `${process.cwd()}:/app/src`, + '-v', + `${path.join(process.cwd(), 'scan-results')}:/app/scan-results`, + '-e', + `WORKSPACE=src`, + '-e', + `LW_ACCOUNT=${lwAccount}`, + '-e', + `LW_API_KEY=${lwApiKey}`, + '-e', + `LW_API_SECRET=${lwApiSecret}`, + '-e', + `RUN_SCA=true`, + '-e', + `RUN_IAC=true`, + 'codesec:latest', + 'compare', + ] + + await callCommand('docker', ...dockerArgs) + + // Copy comparison results out + const compareDir = path.join(reportsDir, 'compare') + mkdirSync(compareDir, { recursive: true }) + + await callCommand( + 'docker', + 'container', + 'cp', + `${containerName}:/tmp/sca-compare.md`, + path.join(compareDir, 'sca-compare.md') + ) + await callCommand( + 'docker', + 'container', + 'cp', + `${containerName}:/tmp/iac-compare.md`, + path.join(compareDir, 'iac-compare.md') + ) + await callCommand( + 'docker', + 'container', + 'cp', + `${containerName}:/tmp/merged-compare.md`, + path.join(compareDir, 'merged-compare.md') + ) + + // Cleanup container + await callCommand('docker', 'rm', containerName) + } + } finally { + // Always cleanup temp directory, even if something fails + tmpDir.removeCallback() + } } -export async function readMarkdownFile(filePath: string): Promise { +export function readMarkdownFile(filePath: string): string { try { - const content = await readFile(filePath, 'utf-8') - return content + return readFileSync(filePath, 'utf-8') } catch (error) { throw new Error(`Failed to read scanner output file: ${error}`) } From 0c5118448b6e4c6d81065cc2a84f7f986735b5cc Mon Sep 17 00:00:00 2001 From: Teodor-Ioan Baltoi Date: Thu, 5 Mar 2026 14:30:31 +0000 Subject: [PATCH 04/15] trying the new codesec image --- src/index.ts | 6 +----- src/util.ts | 6 +++++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/index.ts b/src/index.ts index 5f889287..bfe34839 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,10 +1,6 @@ import { error, getInput, info, setOutput, warning } from '@actions/core' import { appendFileSync, existsSync } from 'fs' -import { - postCommentIfInPr, - resolveExistingCommentIfFound, - uploadArtifact, -} from './actions' +import { postCommentIfInPr, resolveExistingCommentIfFound, uploadArtifact } from './actions' import { codesecRun, getActionRef, diff --git a/src/util.ts b/src/util.ts index 2adb4323..d21ce65b 100644 --- a/src/util.ts +++ b/src/util.ts @@ -208,7 +208,11 @@ export async function codesecRun( // Verify required files exist before running compare if (!existsSync(scaOld) || !existsSync(scaNew)) { - throw new Error(`Compare requires sca-old.sarif and sca-new.sarif. Found: old=${existsSync(scaOld)}, new=${existsSync(scaNew)}`) + throw new Error( + `Compare requires sca-old.sarif and sca-new.sarif. Found: old=${existsSync( + scaOld + )}, new=${existsSync(scaNew)}` + ) } const containerName = 'codesec-compare' From dd1eba1c70413c51a2c425f56ca5b693eaf7828c Mon Sep 17 00:00:00 2001 From: Teodor-Ioan Baltoi Date: Thu, 5 Mar 2026 14:37:02 +0000 Subject: [PATCH 05/15] add full address of image --- src/util.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util.ts b/src/util.ts index d21ce65b..30d4ee32 100644 --- a/src/util.ts +++ b/src/util.ts @@ -167,7 +167,7 @@ export async function codesecRun( `RUN_IAC=${runIac}`, '-e', `SCAN_TARGET=${scanTarget || 'scan'}`, - 'codesec:latest', + 'lacework/codesec:latest', 'scan', ] @@ -240,7 +240,7 @@ export async function codesecRun( `RUN_SCA=true`, '-e', `RUN_IAC=true`, - 'codesec:latest', + 'lacework/codesec:latest', 'compare', ] From 6a8a143f46db903cd467f26a8a3936e5d532eac6 Mon Sep 17 00:00:00 2001 From: Teodor-Ioan Baltoi Date: Thu, 5 Mar 2026 16:53:17 +0000 Subject: [PATCH 06/15] change the file structure --- src/index.ts | 47 +++++++++++++++++++++-------------------------- src/util.ts | 12 ++++-------- 2 files changed, 25 insertions(+), 34 deletions(-) diff --git a/src/index.ts b/src/index.ts index bfe34839..b514334c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,9 @@ import { error, getInput, info, setOutput, warning } from '@actions/core' -import { appendFileSync, existsSync } from 'fs' -import { postCommentIfInPr, resolveExistingCommentIfFound, uploadArtifact } from './actions' +import { appendFileSync, existsSync, mkdirSync } from 'fs' +import * as path from 'path' +import { downloadArtifact, postCommentIfInPr, resolveExistingCommentIfFound, uploadArtifact } from './actions' import { + callCommand, codesecRun, getActionRef, getMsSinceStart, @@ -41,29 +43,12 @@ async function runAnalysis() { if (target == 'push') { targetScan = 'scan' } - await codesecRun('scan', true, true, targetScan) + const resultsPath = await codesecRun('scan', true, true, targetScan) - // TODO: Copy SARIF from codesec results to expected location for artifact upload - // The codesec scanner outputs to scan-results/sca/sca-{target}.sarif - // For now, keeping the old Lacework CLI flow commented below as fallback - - /* - * OLD LACEWORK CLI FLOW - Commented out, to be removed once codesec is fully tested - * - * var args = ['sca', 'scan', '.', '-o', scaDir, '--formats', 'sarif,lw-json', '--deployment', 'ci'] - * if (target === 'push') { args.push('--save-results') } - * if (debug()) { args.push('--debug') } - * await callLaceworkCli(...args) - * args = [scaSarifReport, scaReport] - * await callCommand('cp', ...args) - * toUpload.push(scaReport) - */ - - // Placeholder: upload the SARIF from codesec results - // TODO: Update this path once codesec output location is finalized - const scaSarifFromCodesec = `scan-results/sca/sca-${targetScan}.sarif` - if (existsSync(scaSarifFromCodesec)) { - toUpload.push(scaSarifFromCodesec) + // Upload SARIF from the returned results path + const scaSarifFile = path.join(resultsPath, 'sca', `sca-${targetScan}.sarif`) + if (existsSync(scaSarifFile)) { + toUpload.push(scaSarifFile) } const uploadStart = Date.now() @@ -80,8 +65,18 @@ async function runAnalysis() { async function displayResults() { info('Displaying results') - // Use codesec compare - expects scan-results/sca/sca-{new,old}.sarif to exist - // These are produced by the previous scan steps with target='new' and target='old' + // Download artifacts from previous jobs + const artifactOld = await downloadArtifact('results-old') + const artifactNew = await downloadArtifact('results-new') + + // Create local scan-results directory for compare + mkdirSync('scan-results/sca', { recursive: true }) + + // Copy SARIF files from artifacts to expected location for compare + await callCommand('cp', path.join(artifactOld, 'sca-old.sarif'), 'scan-results/sca/sca-old.sarif') + await callCommand('cp', path.join(artifactNew, 'sca-new.sarif'), 'scan-results/sca/sca-new.sarif') + + // Verify files exist const scaOldExists = existsSync('scan-results/sca/sca-old.sarif') const scaNewExists = existsSync('scan-results/sca/sca-new.sarif') diff --git a/src/util.ts b/src/util.ts index 30d4ee32..e2a5f381 100644 --- a/src/util.ts +++ b/src/util.ts @@ -130,17 +130,16 @@ export async function codesecRun( runIac: boolean = false, runSca: boolean = false, scanTarget?: string -): Promise { +): Promise { const lwAccount = getRequiredEnvVariable('LW_ACCOUNT_NAME') const lwApiKey = getRequiredEnvVariable('LW_API_KEY') const lwApiSecret = getRequiredEnvVariable('LW_API_SECRET') - // Create temp directory for scan results (auto-cleaned after use) + // Create temp directory for scan results const tmpDir = tmp.dirSync({ unsafeCleanup: true }) const reportsDir = path.join(tmpDir.name, 'scan-results') - try { - if (action === 'scan') { + if (action === 'scan') { // Scan mode: mount repo as /app/src, results go to /tmp/scan-results/ in container const containerName = `codesec-scan-${scanTarget || 'default'}` @@ -275,10 +274,7 @@ export async function codesecRun( // Cleanup container await callCommand('docker', 'rm', containerName) } - } finally { - // Always cleanup temp directory, even if something fails - tmpDir.removeCallback() - } + return reportsDir } export function readMarkdownFile(filePath: string): string { From 7da82d3b8abb1dcb0eaee02d1238c9dc7a2b3203 Mon Sep 17 00:00:00 2001 From: Teodor-Ioan Baltoi Date: Thu, 5 Mar 2026 16:54:21 +0000 Subject: [PATCH 07/15] nits --- src/index.ts | 7 +- src/util.ts | 244 +++++++++++++++++++++++++-------------------------- 2 files changed, 128 insertions(+), 123 deletions(-) diff --git a/src/index.ts b/src/index.ts index b514334c..96c836f1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,12 @@ import { error, getInput, info, setOutput, warning } from '@actions/core' import { appendFileSync, existsSync, mkdirSync } from 'fs' import * as path from 'path' -import { downloadArtifact, postCommentIfInPr, resolveExistingCommentIfFound, uploadArtifact } from './actions' +import { + downloadArtifact, + postCommentIfInPr, + resolveExistingCommentIfFound, + uploadArtifact, +} from './actions' import { callCommand, codesecRun, diff --git a/src/util.ts b/src/util.ts index e2a5f381..a497d066 100644 --- a/src/util.ts +++ b/src/util.ts @@ -140,140 +140,140 @@ export async function codesecRun( const reportsDir = path.join(tmpDir.name, 'scan-results') if (action === 'scan') { - // Scan mode: mount repo as /app/src, results go to /tmp/scan-results/ in container - const containerName = `codesec-scan-${scanTarget || 'default'}` - - info(`Running codesec scan (target: ${scanTarget || 'scan'})`) - - // Run the scanner - const dockerArgs = [ - 'run', - '--name', - containerName, - '-v', - `${process.cwd()}:/app/src`, - '-e', - `WORKSPACE=src`, - '-e', - `LW_ACCOUNT=${lwAccount}`, - '-e', - `LW_API_KEY=${lwApiKey}`, - '-e', - `LW_API_SECRET=${lwApiSecret}`, - '-e', - `RUN_SCA=${runSca}`, - '-e', - `RUN_IAC=${runIac}`, - '-e', - `SCAN_TARGET=${scanTarget || 'scan'}`, - 'lacework/codesec:latest', - 'scan', - ] - - await callCommand('docker', ...dockerArgs) - - // Copy results out of container to temp dir - if (runSca) { - const scaDir = path.join(reportsDir, 'sca') - mkdirSync(scaDir, { recursive: true }) - await callCommand( - 'docker', - 'container', - 'cp', - `${containerName}:/tmp/scan-results/sca/sca-${scanTarget || 'scan'}.sarif`, - path.join(scaDir, `sca-${scanTarget || 'scan'}.sarif`) - ) - } - - if (runIac) { - const iacDir = path.join(reportsDir, 'iac') - mkdirSync(iacDir, { recursive: true }) - await callCommand( - 'docker', - 'container', - 'cp', - `${containerName}:/tmp/scan-results/iac/iac-${scanTarget || 'scan'}.json`, - path.join(iacDir, `iac-${scanTarget || 'scan'}.json`) - ) - } - - // Cleanup container - await callCommand('docker', 'rm', containerName) - } else if (action === 'compare') { - // Compare mode: copy scan results into place first, then run compare - const srcDir = path.join(reportsDir, 'sca') - const scaOld = path.join(srcDir, 'sca-old.sarif') - const scaNew = path.join(srcDir, 'sca-new.sarif') - - // Verify required files exist before running compare - if (!existsSync(scaOld) || !existsSync(scaNew)) { - throw new Error( - `Compare requires sca-old.sarif and sca-new.sarif. Found: old=${existsSync( - scaOld - )}, new=${existsSync(scaNew)}` - ) - } - - const containerName = 'codesec-compare' - - info('Running codesec compare') - - // Note: mounts both the repo and the scan-results directory separately - const dockerArgs = [ - 'run', - '--name', - containerName, - '-v', - `${process.cwd()}:/app/src`, - '-v', - `${path.join(process.cwd(), 'scan-results')}:/app/scan-results`, - '-e', - `WORKSPACE=src`, - '-e', - `LW_ACCOUNT=${lwAccount}`, - '-e', - `LW_API_KEY=${lwApiKey}`, - '-e', - `LW_API_SECRET=${lwApiSecret}`, - '-e', - `RUN_SCA=true`, - '-e', - `RUN_IAC=true`, - 'lacework/codesec:latest', - 'compare', - ] - - await callCommand('docker', ...dockerArgs) - - // Copy comparison results out - const compareDir = path.join(reportsDir, 'compare') - mkdirSync(compareDir, { recursive: true }) - - await callCommand( - 'docker', - 'container', - 'cp', - `${containerName}:/tmp/sca-compare.md`, - path.join(compareDir, 'sca-compare.md') - ) + // Scan mode: mount repo as /app/src, results go to /tmp/scan-results/ in container + const containerName = `codesec-scan-${scanTarget || 'default'}` + + info(`Running codesec scan (target: ${scanTarget || 'scan'})`) + + // Run the scanner + const dockerArgs = [ + 'run', + '--name', + containerName, + '-v', + `${process.cwd()}:/app/src`, + '-e', + `WORKSPACE=src`, + '-e', + `LW_ACCOUNT=${lwAccount}`, + '-e', + `LW_API_KEY=${lwApiKey}`, + '-e', + `LW_API_SECRET=${lwApiSecret}`, + '-e', + `RUN_SCA=${runSca}`, + '-e', + `RUN_IAC=${runIac}`, + '-e', + `SCAN_TARGET=${scanTarget || 'scan'}`, + 'lacework/codesec:latest', + 'scan', + ] + + await callCommand('docker', ...dockerArgs) + + // Copy results out of container to temp dir + if (runSca) { + const scaDir = path.join(reportsDir, 'sca') + mkdirSync(scaDir, { recursive: true }) await callCommand( 'docker', 'container', 'cp', - `${containerName}:/tmp/iac-compare.md`, - path.join(compareDir, 'iac-compare.md') + `${containerName}:/tmp/scan-results/sca/sca-${scanTarget || 'scan'}.sarif`, + path.join(scaDir, `sca-${scanTarget || 'scan'}.sarif`) ) + } + + if (runIac) { + const iacDir = path.join(reportsDir, 'iac') + mkdirSync(iacDir, { recursive: true }) await callCommand( 'docker', 'container', 'cp', - `${containerName}:/tmp/merged-compare.md`, - path.join(compareDir, 'merged-compare.md') + `${containerName}:/tmp/scan-results/iac/iac-${scanTarget || 'scan'}.json`, + path.join(iacDir, `iac-${scanTarget || 'scan'}.json`) ) + } - // Cleanup container - await callCommand('docker', 'rm', containerName) + // Cleanup container + await callCommand('docker', 'rm', containerName) + } else if (action === 'compare') { + // Compare mode: copy scan results into place first, then run compare + const srcDir = path.join(reportsDir, 'sca') + const scaOld = path.join(srcDir, 'sca-old.sarif') + const scaNew = path.join(srcDir, 'sca-new.sarif') + + // Verify required files exist before running compare + if (!existsSync(scaOld) || !existsSync(scaNew)) { + throw new Error( + `Compare requires sca-old.sarif and sca-new.sarif. Found: old=${existsSync( + scaOld + )}, new=${existsSync(scaNew)}` + ) } + + const containerName = 'codesec-compare' + + info('Running codesec compare') + + // Note: mounts both the repo and the scan-results directory separately + const dockerArgs = [ + 'run', + '--name', + containerName, + '-v', + `${process.cwd()}:/app/src`, + '-v', + `${path.join(process.cwd(), 'scan-results')}:/app/scan-results`, + '-e', + `WORKSPACE=src`, + '-e', + `LW_ACCOUNT=${lwAccount}`, + '-e', + `LW_API_KEY=${lwApiKey}`, + '-e', + `LW_API_SECRET=${lwApiSecret}`, + '-e', + `RUN_SCA=true`, + '-e', + `RUN_IAC=true`, + 'lacework/codesec:latest', + 'compare', + ] + + await callCommand('docker', ...dockerArgs) + + // Copy comparison results out + const compareDir = path.join(reportsDir, 'compare') + mkdirSync(compareDir, { recursive: true }) + + await callCommand( + 'docker', + 'container', + 'cp', + `${containerName}:/tmp/sca-compare.md`, + path.join(compareDir, 'sca-compare.md') + ) + await callCommand( + 'docker', + 'container', + 'cp', + `${containerName}:/tmp/iac-compare.md`, + path.join(compareDir, 'iac-compare.md') + ) + await callCommand( + 'docker', + 'container', + 'cp', + `${containerName}:/tmp/merged-compare.md`, + path.join(compareDir, 'merged-compare.md') + ) + + // Cleanup container + await callCommand('docker', 'rm', containerName) + } return reportsDir } From f55b291568dda64402b49f34f072dc8770252a3a Mon Sep 17 00:00:00 2001 From: Teodor-Ioan Baltoi Date: Thu, 5 Mar 2026 17:37:40 +0000 Subject: [PATCH 08/15] dir change --- src/util.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/util.ts b/src/util.ts index a497d066..407df5c3 100644 --- a/src/util.ts +++ b/src/util.ts @@ -3,7 +3,6 @@ import { context } from '@actions/github' import { spawn } from 'child_process' import { TelemetryCollector } from './telemetry' import { readFileSync } from 'fs' -import * as tmp from 'tmp' import * as path from 'path' import { mkdirSync, existsSync } from 'fs' @@ -135,9 +134,8 @@ export async function codesecRun( const lwApiKey = getRequiredEnvVariable('LW_API_KEY') const lwApiSecret = getRequiredEnvVariable('LW_API_SECRET') - // Create temp directory for scan results - const tmpDir = tmp.dirSync({ unsafeCleanup: true }) - const reportsDir = path.join(tmpDir.name, 'scan-results') + // Create scan-results directory in workspace (required for artifact upload) + const reportsDir = path.join(process.cwd(), 'scan-results') if (action === 'scan') { // Scan mode: mount repo as /app/src, results go to /tmp/scan-results/ in container From 23765523a5d9d56e2b8b48f54ad9f4415b0c7574 Mon Sep 17 00:00:00 2001 From: Teodor-Ioan Baltoi Date: Fri, 6 Mar 2026 10:30:13 +0000 Subject: [PATCH 09/15] dir change --- src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/index.ts b/src/index.ts index 96c836f1..958320b5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -78,8 +78,8 @@ async function displayResults() { mkdirSync('scan-results/sca', { recursive: true }) // Copy SARIF files from artifacts to expected location for compare - await callCommand('cp', path.join(artifactOld, 'sca-old.sarif'), 'scan-results/sca/sca-old.sarif') - await callCommand('cp', path.join(artifactNew, 'sca-new.sarif'), 'scan-results/sca/sca-new.sarif') + await callCommand('cp', path.join(artifactOld, 'scan-results/sca/sca-old.sarif'), 'scan-results/sca/sca-old.sarif') + await callCommand('cp', path.join(artifactNew, 'scan-results/sca/sca-new.sarif'), 'scan-results/sca/sca-new.sarif') // Verify files exist const scaOldExists = existsSync('scan-results/sca/sca-old.sarif') From f2b88c4b7c4f14c8ab3967d8a5204c8da5278931 Mon Sep 17 00:00:00 2001 From: Teodor-Ioan Baltoi Date: Fri, 6 Mar 2026 10:30:30 +0000 Subject: [PATCH 10/15] dir change --- src/index.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/index.ts b/src/index.ts index 958320b5..a2ac12ad 100644 --- a/src/index.ts +++ b/src/index.ts @@ -78,8 +78,16 @@ async function displayResults() { mkdirSync('scan-results/sca', { recursive: true }) // Copy SARIF files from artifacts to expected location for compare - await callCommand('cp', path.join(artifactOld, 'scan-results/sca/sca-old.sarif'), 'scan-results/sca/sca-old.sarif') - await callCommand('cp', path.join(artifactNew, 'scan-results/sca/sca-new.sarif'), 'scan-results/sca/sca-new.sarif') + await callCommand( + 'cp', + path.join(artifactOld, 'scan-results/sca/sca-old.sarif'), + 'scan-results/sca/sca-old.sarif' + ) + await callCommand( + 'cp', + path.join(artifactNew, 'scan-results/sca/sca-new.sarif'), + 'scan-results/sca/sca-new.sarif' + ) // Verify files exist const scaOldExists = existsSync('scan-results/sca/sca-old.sarif') From de0b104df5418eea77422b8a00c10691423beb85 Mon Sep 17 00:00:00 2001 From: Teodor-Ioan Baltoi Date: Fri, 6 Mar 2026 10:41:14 +0000 Subject: [PATCH 11/15] check before copy --- src/util.ts | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/src/util.ts b/src/util.ts index 407df5c3..c4377d17 100644 --- a/src/util.ts +++ b/src/util.ts @@ -247,20 +247,7 @@ export async function codesecRun( const compareDir = path.join(reportsDir, 'compare') mkdirSync(compareDir, { recursive: true }) - await callCommand( - 'docker', - 'container', - 'cp', - `${containerName}:/tmp/sca-compare.md`, - path.join(compareDir, 'sca-compare.md') - ) - await callCommand( - 'docker', - 'container', - 'cp', - `${containerName}:/tmp/iac-compare.md`, - path.join(compareDir, 'iac-compare.md') - ) + // Copy merged output (required) await callCommand( 'docker', 'container', @@ -269,6 +256,30 @@ export async function codesecRun( path.join(compareDir, 'merged-compare.md') ) + // Copy individual reports if they exist (optional - may not exist if scan was skipped) + try { + await callCommand( + 'docker', + 'container', + 'cp', + `${containerName}:/tmp/sca-compare.md`, + path.join(compareDir, 'sca-compare.md') + ) + } catch { + info('SCA compare output not found (may have been skipped)') + } + try { + await callCommand( + 'docker', + 'container', + 'cp', + `${containerName}:/tmp/iac-compare.md`, + path.join(compareDir, 'iac-compare.md') + ) + } catch { + info('IAC compare output not found (may have been skipped)') + } + // Cleanup container await callCommand('docker', 'rm', containerName) } From da4f325396e5f70e5b6523d914424ced4764f037 Mon Sep 17 00:00:00 2001 From: Teodor-Ioan Baltoi Date: Fri, 6 Mar 2026 10:54:37 +0000 Subject: [PATCH 12/15] debug --- src/index.ts | 11 ++++++----- src/util.ts | 8 ++++---- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/index.ts b/src/index.ts index a2ac12ad..887cfd88 100644 --- a/src/index.ts +++ b/src/index.ts @@ -53,16 +53,17 @@ async function runAnalysis() { // Upload SARIF from the returned results path const scaSarifFile = path.join(resultsPath, 'sca', `sca-${targetScan}.sarif`) if (existsSync(scaSarifFile)) { + info(`Found SARIF file to upload: ${scaSarifFile}`) toUpload.push(scaSarifFile) + } else { + info(`SARIF file not found at: ${scaSarifFile}`) } const uploadStart = Date.now() const artifactPrefix = getInput('artifact-prefix') - if (artifactPrefix !== '') { - await uploadArtifact(artifactPrefix + '-results-' + target, ...toUpload) - } else { - await uploadArtifact('results-' + target, ...toUpload) - } + const artifactName = artifactPrefix !== '' ? artifactPrefix + '-results-' + target : 'results-' + target + info(`Uploading artifact '${artifactName}' with ${toUpload.length} file(s)`) + await uploadArtifact(artifactName, ...toUpload) telemetryCollector.addField('duration.upload-artifacts', (Date.now() - uploadStart).toString()) setOutput(`${target}-completed`, true) } diff --git a/src/util.ts b/src/util.ts index c4377d17..fb0bb44b 100644 --- a/src/util.ts +++ b/src/util.ts @@ -247,12 +247,12 @@ export async function codesecRun( const compareDir = path.join(reportsDir, 'compare') mkdirSync(compareDir, { recursive: true }) - // Copy merged output (required) + // Copy merged output (required) - files are at /tmp/scan-results/compare/ in container await callCommand( 'docker', 'container', 'cp', - `${containerName}:/tmp/merged-compare.md`, + `${containerName}:/tmp/scan-results/compare/merged-compare.md`, path.join(compareDir, 'merged-compare.md') ) @@ -262,7 +262,7 @@ export async function codesecRun( 'docker', 'container', 'cp', - `${containerName}:/tmp/sca-compare.md`, + `${containerName}:/tmp/scan-results/compare/sca-compare.md`, path.join(compareDir, 'sca-compare.md') ) } catch { @@ -273,7 +273,7 @@ export async function codesecRun( 'docker', 'container', 'cp', - `${containerName}:/tmp/iac-compare.md`, + `${containerName}:/tmp/scan-results/compare/iac-compare.md`, path.join(compareDir, 'iac-compare.md') ) } catch { From 20cbbbdfd0a9dd9dd38370b81d38e6eb9ea081f0 Mon Sep 17 00:00:00 2001 From: Teodor-Ioan Baltoi Date: Fri, 6 Mar 2026 11:24:23 +0000 Subject: [PATCH 13/15] debug --- src/index.ts | 46 ++++++++++++++++++++++++++++++++++++++-------- src/util.ts | 35 +++++++++++++++++++++++++---------- 2 files changed, 63 insertions(+), 18 deletions(-) diff --git a/src/index.ts b/src/index.ts index 887cfd88..0a69e95c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -61,7 +61,8 @@ async function runAnalysis() { const uploadStart = Date.now() const artifactPrefix = getInput('artifact-prefix') - const artifactName = artifactPrefix !== '' ? artifactPrefix + '-results-' + target : 'results-' + target + const artifactName = + artifactPrefix !== '' ? artifactPrefix + '-results-' + target : 'results-' + target info(`Uploading artifact '${artifactName}' with ${toUpload.length} file(s)`) await uploadArtifact(artifactName, ...toUpload) telemetryCollector.addField('duration.upload-artifacts', (Date.now() - uploadStart).toString()) @@ -77,20 +78,38 @@ async function displayResults() { // Create local scan-results directory for compare mkdirSync('scan-results/sca', { recursive: true }) + mkdirSync('scan-results/iac', { recursive: true }) - // Copy SARIF files from artifacts to expected location for compare + // Copy SCA SARIF files from artifacts to expected location for compare + info('Copying SCA files from artifacts') + info(` Old SARIF: ${path.join(artifactOld, 'scan-results/sca/sca-old.sarif')}`) await callCommand( 'cp', path.join(artifactOld, 'scan-results/sca/sca-old.sarif'), 'scan-results/sca/sca-old.sarif' ) + info(` New SARIF: ${path.join(artifactNew, 'scan-results/sca/sca-new.sarif')}`) await callCommand( 'cp', path.join(artifactNew, 'scan-results/sca/sca-new.sarif'), 'scan-results/sca/sca-new.sarif' ) - // Verify files exist + // Copy IAC JSON files from artifacts to expected location for compare + info('Checking for IAC files in artifacts') + const iacOldPath = path.join(artifactOld, 'scan-results/iac/iac-old.json') + const iacNewPath = path.join(artifactNew, 'scan-results/iac/iac-new.json') + info(` Old IAC: ${iacOldPath} (exists: ${existsSync(iacOldPath)})`) + info(` New IAC: ${iacNewPath} (exists: ${existsSync(iacNewPath)})`) + if (existsSync(iacOldPath) && existsSync(iacNewPath)) { + info(' Copying IAC files') + await callCommand('cp', iacOldPath, 'scan-results/iac/iac-old.json') + await callCommand('cp', iacNewPath, 'scan-results/iac/iac-new.json') + } else { + info(' IAC files not found in artifacts, skipping IAC compare') + } + + // Verify SCA files exist (required) const scaOldExists = existsSync('scan-results/sca/sca-old.sarif') const scaNewExists = existsSync('scan-results/sca/sca-new.sarif') @@ -103,14 +122,25 @@ async function displayResults() { // Run codesec compare mode await codesecRun('compare', false, false) - // Read the merged comparison output + // Read the comparison output + // merged-compare.md exists when both SCA and IAC comparisons succeed + // sca-compare.md exists when only SCA comparison succeeds (partial) const mergedOutput = 'scan-results/compare/merged-compare.md' - if (!existsSync(mergedOutput)) { - throw new Error(`Comparison output not found at ${mergedOutput}`) + const scaOutput = 'scan-results/compare/sca-compare.md' + + let message: string + if (existsSync(mergedOutput)) { + info('Using merged comparison output') + message = readMarkdownFile(mergedOutput) + } else if (existsSync(scaOutput)) { + info('Using SCA-only comparison output (partial)') + message = readMarkdownFile(scaOutput) + } else { + throw new Error( + `Comparison output not found. Tried: ${mergedOutput}, ${scaOutput}` + ) } - const message = readMarkdownFile(mergedOutput) - // Check if there are new violations (non-zero count in "Found N new potential violations") const hasViolations = /Found\s+[1-9]\d*\s+/.test(message) diff --git a/src/util.ts b/src/util.ts index fb0bb44b..f81230db 100644 --- a/src/util.ts +++ b/src/util.ts @@ -247,16 +247,24 @@ export async function codesecRun( const compareDir = path.join(reportsDir, 'compare') mkdirSync(compareDir, { recursive: true }) - // Copy merged output (required) - files are at /tmp/scan-results/compare/ in container - await callCommand( - 'docker', - 'container', - 'cp', - `${containerName}:/tmp/scan-results/compare/merged-compare.md`, - path.join(compareDir, 'merged-compare.md') - ) - - // Copy individual reports if they exist (optional - may not exist if scan was skipped) + // Copy all available comparison outputs + // merged-compare.md exists when both SCA and IAC comparisons succeed + // sca-compare.md / iac-compare.md exist for individual comparisons + let copiedAny = false + + try { + await callCommand( + 'docker', + 'container', + 'cp', + `${containerName}:/tmp/scan-results/compare/merged-compare.md`, + path.join(compareDir, 'merged-compare.md') + ) + copiedAny = true + } catch { + info('Merged compare output not found (partial compare mode)') + } + try { await callCommand( 'docker', @@ -265,9 +273,11 @@ export async function codesecRun( `${containerName}:/tmp/scan-results/compare/sca-compare.md`, path.join(compareDir, 'sca-compare.md') ) + copiedAny = true } catch { info('SCA compare output not found (may have been skipped)') } + try { await callCommand( 'docker', @@ -276,10 +286,15 @@ export async function codesecRun( `${containerName}:/tmp/scan-results/compare/iac-compare.md`, path.join(compareDir, 'iac-compare.md') ) + copiedAny = true } catch { info('IAC compare output not found (may have been skipped)') } + if (!copiedAny) { + throw new Error('No comparison outputs found in container') + } + // Cleanup container await callCommand('docker', 'rm', containerName) } From 35fc4f467d66333411a22b6138537a70731b15d3 Mon Sep 17 00:00:00 2001 From: Teodor-Ioan Baltoi Date: Fri, 6 Mar 2026 11:25:02 +0000 Subject: [PATCH 14/15] debug --- src/index.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/index.ts b/src/index.ts index 0a69e95c..6303fe23 100644 --- a/src/index.ts +++ b/src/index.ts @@ -136,9 +136,7 @@ async function displayResults() { info('Using SCA-only comparison output (partial)') message = readMarkdownFile(scaOutput) } else { - throw new Error( - `Comparison output not found. Tried: ${mergedOutput}, ${scaOutput}` - ) + throw new Error(`Comparison output not found. Tried: ${mergedOutput}, ${scaOutput}`) } // Check if there are new violations (non-zero count in "Found N new potential violations") From a4f159e49f5e14989748f692432cc4b39c1a5751 Mon Sep 17 00:00:00 2001 From: Teodor-Ioan Baltoi Date: Fri, 6 Mar 2026 11:48:48 +0000 Subject: [PATCH 15/15] debug --- src/index.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/index.ts b/src/index.ts index 6303fe23..a1b3111d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -50,13 +50,22 @@ async function runAnalysis() { } const resultsPath = await codesecRun('scan', true, true, targetScan) - // Upload SARIF from the returned results path + // Upload SCA SARIF from the returned results path const scaSarifFile = path.join(resultsPath, 'sca', `sca-${targetScan}.sarif`) if (existsSync(scaSarifFile)) { - info(`Found SARIF file to upload: ${scaSarifFile}`) + info(`Found SCA SARIF file to upload: ${scaSarifFile}`) toUpload.push(scaSarifFile) } else { - info(`SARIF file not found at: ${scaSarifFile}`) + info(`SCA SARIF file not found at: ${scaSarifFile}`) + } + + // Upload IAC JSON from the returned results path + const iacJsonFile = path.join(resultsPath, 'iac', `iac-${targetScan}.json`) + if (existsSync(iacJsonFile)) { + info(`Found IAC JSON file to upload: ${iacJsonFile}`) + toUpload.push(iacJsonFile) + } else { + info(`IAC JSON file not found at: ${iacJsonFile}`) } const uploadStart = Date.now()