From cbe46af0fbd312b9974f839f2196123bf13b68f8 Mon Sep 17 00:00:00 2001 From: Teodor-Ioan Baltoi Date: Thu, 4 Jun 2026 14:58:19 +0100 Subject: [PATCH 1/3] use new caching mechanism --- src/index.ts | 35 ++++++++++++++--------- src/util.ts | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 97 insertions(+), 16 deletions(-) diff --git a/src/index.ts b/src/index.ts index 3fa9be0e..c73253fc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -15,8 +15,8 @@ import { getOptionalEnvVariable, readMarkdownFile, shouldRunIaCScanner, + generateCacheKey, } from './util' -import { simpleGit } from 'simple-git' // Global scanner toggles - set to false to disable a scanner globally const enableScaRunning = true @@ -66,15 +66,19 @@ async function runAnalysis() { // Cache the analysis results when scanning the target branch let cacheHit = false - const commit = (await simpleGit().revparse(['HEAD'])).trim() - let cacheKey = `codesec-${commit}` + let cacheKey: string | undefined if (targetScan === 'old') { - const restored = await cache.restoreCache([resultsPath], cacheKey) - if (restored) { - info(`Cache hit for ${cacheKey} — skipping scan`) - cacheHit = true + cacheKey = await generateCacheKey() + if (cacheKey) { + const restored = await cache.restoreCache([resultsPath], cacheKey) + if (restored) { + info(`Cache hit for ${cacheKey} — skipping scan`) + cacheHit = true + } else { + info(`Cache miss for ${cacheKey} — running scan`) + } } else { - info(`Cache miss for ${cacheKey} — running scan`) + info('Cache key generation failed — running scan without cache') } } @@ -89,11 +93,16 @@ async function runAnalysis() { ) if (success && targetScan !== 'new') { // Save the analysis results when not scanning the PR source branch - try { - await cache.saveCache([resultsPath], cacheKey) - info(`Saved analysis results for ${cacheKey}`) - } catch (e) { - info(`Failed to save cache for ${cacheKey}: ${(e as Error).message}`) + if (!cacheKey) { + cacheKey = await generateCacheKey() + } + if (cacheKey) { + try { + await cache.saveCache([resultsPath], cacheKey) + info(`Saved analysis results for ${cacheKey}`) + } catch (e) { + info(`Failed to save cache for ${cacheKey}: ${(e as Error).message}`) + } } } } else { diff --git a/src/util.ts b/src/util.ts index 4e0ba95c..60f112a5 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,7 +1,7 @@ import { error, getInput, info, isDebug } from '@actions/core' import { context } from '@actions/github' import { spawn } from 'child_process' -import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'fs' +import { existsSync, readFileSync, mkdirSync, writeFileSync, unlinkSync } from 'fs' import * as os from 'os' import * as path from 'path' import { simpleGit } from 'simple-git' @@ -211,7 +211,7 @@ export async function runCodesec( '-e', `SCAN_TARGET=${scanTarget || 'scan'}`, ...(modifiedFiles ? ['-e', `MODIFIED_FILES=${modifiedFiles}`] : []), - 'lacework/codesec:latest', + 'lacework/codesec:test', 'scan', ] @@ -279,7 +279,7 @@ export async function runCodesec( `RUN_SCA=${runSca}`, '-e', `RUN_IAC=${runIac}`, - 'lacework/codesec:latest', + 'lacework/codesec:test', 'compare', ] @@ -319,3 +319,75 @@ export function readMarkdownFile(filePath: string): string { throw new Error(`Failed to read scanner output file: ${error}`) } } + +export async function generateCacheKey(): Promise { + const lwAccount = getRequiredEnvVariable('LW_ACCOUNT') + const lwApiKey = getRequiredEnvVariable('LW_API_KEY') + const lwApiSecret = getRequiredEnvVariable('LW_API_SECRET') + + const containerName = `codesec-cache-key-${Date.now()}` + const envFile = createEnvFile() + + const dockerArgs = [ + 'run', + '--name', + containerName, + '-v', + `${process.cwd()}:/app/src`, + '--env-file', + envFile, + '-e', + `WORKSPACE=src`, + '-e', + `LW_ACCOUNT=${lwAccount}`, + '-e', + `LW_API_KEY=${lwApiKey}`, + '-e', + `LW_API_SECRET=${lwApiSecret}`, + '-e', + `GENERATE_CACHE_KEY=true`, + 'lacework/codesec:test', + 'scan', + ] + + try { + await callCommand('docker', ...dockerArgs) + } catch (e) { + info(`Cache key generation failed: ${(e as Error).message}`) + await callCommand('docker', 'rm', containerName).catch(() => {}) + return undefined + } + + const outputFile = path.join(os.tmpdir(), `cache-key-${Date.now()}.txt`) + try { + await callCommand( + 'docker', + 'container', + 'cp', + `${containerName}:/tmp/scan-results/sca/cache-key.txt`, + outputFile + ) + } catch (e) { + info(`Failed to copy cache key from container: ${(e as Error).message}`) + await callCommand('docker', 'rm', containerName).catch(() => {}) + return undefined + } + + await callCommand('docker', 'rm', containerName) + + if (!existsSync(outputFile)) { + info('Cache key file not found after docker cp') + return undefined + } + + const cacheKey = readFileSync(outputFile, 'utf-8').trim() + unlinkSync(outputFile) + + if (!/^[a-f0-9]{64}$/.test(cacheKey)) { + info(`Cache key format invalid: ${cacheKey}`) + return undefined + } + + info(`Generated cache key: ${cacheKey}`) + return `codesec-${cacheKey}` +} From e997ad03981fc37fc5298a6c38bb8165f320c3bd Mon Sep 17 00:00:00 2001 From: Teodor-Ioan Baltoi Date: Fri, 5 Jun 2026 16:02:43 +0100 Subject: [PATCH 2/3] nit --- src/index.ts | 4 +-- src/util.ts | 93 ++++++++++++++++++++++------------------------------ 2 files changed, 41 insertions(+), 56 deletions(-) diff --git a/src/index.ts b/src/index.ts index c73253fc..dc34a669 100644 --- a/src/index.ts +++ b/src/index.ts @@ -68,7 +68,7 @@ async function runAnalysis() { let cacheHit = false let cacheKey: string | undefined if (targetScan === 'old') { - cacheKey = await generateCacheKey() + cacheKey = await generateCacheKey(enableIacRunning, enableScaRunning, targetScan, modifiedFiles) if (cacheKey) { const restored = await cache.restoreCache([resultsPath], cacheKey) if (restored) { @@ -94,7 +94,7 @@ async function runAnalysis() { if (success && targetScan !== 'new') { // Save the analysis results when not scanning the PR source branch if (!cacheKey) { - cacheKey = await generateCacheKey() + cacheKey = await generateCacheKey(enableIacRunning, enableScaRunning, targetScan, modifiedFiles) } if (cacheKey) { try { diff --git a/src/util.ts b/src/util.ts index 60f112a5..acd5a5a9 100644 --- a/src/util.ts +++ b/src/util.ts @@ -157,7 +157,7 @@ export function shouldRunIaCScanner(modifiedFiles: string): boolean { }) } -// runCodesec - Docker-based scanner using codesec:latest image +// runCodesec - Docker-based scanner using codesec:test image // // Modes: // 1. action='scan', scanTarget='new'/'old' -> produces analysis for PR comment @@ -167,22 +167,30 @@ export function shouldRunIaCScanner(modifiedFiles: string): boolean { // Parameters: // - runIac/runSca: which scanners to enable (default false - enable when ready to test) // - scanTarget: 'new', 'old', or 'scan' depending on mode +// - computeCacheKey: if true, runs GENERATE_CACHE_KEY mode instead of scanning export async function runCodesec( action: string, runIac: boolean = false, runSca: boolean = false, reportsDir: string, scanTarget?: string, - modifiedFiles?: string + modifiedFiles?: string, + computeCacheKey: boolean = false ): Promise { const lwAccount = getRequiredEnvVariable('LW_ACCOUNT') const lwApiKey = getRequiredEnvVariable('LW_API_KEY') const lwApiSecret = getRequiredEnvVariable('LW_API_SECRET') if (action === 'scan') { - const containerName = `codesec-scan-${scanTarget || 'default'}` - - info(`Running codesec scan (target: ${scanTarget || 'scan'})`) + const containerName = computeCacheKey + ? `codesec-cache-key-${Date.now()}` + : `codesec-scan-${scanTarget || 'default'}` + + info( + computeCacheKey + ? 'Running codesec cache key generation' + : `Running codesec scan (target: ${scanTarget || 'scan'})` + ) // Create env file with GitHub CI vars for the lacework iac binary const envFile = createEnvFile() @@ -211,12 +219,28 @@ export async function runCodesec( '-e', `SCAN_TARGET=${scanTarget || 'scan'}`, ...(modifiedFiles ? ['-e', `MODIFIED_FILES=${modifiedFiles}`] : []), + ...(computeCacheKey ? ['-e', 'GENERATE_CACHE_KEY=true'] : []), 'lacework/codesec:test', 'scan', ] await callCommand('docker', ...dockerArgs) + if (computeCacheKey) { + // Copy cache key out and cleanup + const outputFile = path.join(reportsDir, 'cache-key.txt') + mkdirSync(reportsDir, { recursive: true }) + await callCommand( + 'docker', + 'container', + 'cp', + `${containerName}:/tmp/scan-results/sca/cache-key.txt`, + outputFile + ) + await callCommand('docker', 'rm', containerName) + return true + } + // Copy results out of container to temp dir if (runSca) { const scaDir = path.join(reportsDir, 'sca') @@ -320,63 +344,24 @@ export function readMarkdownFile(filePath: string): string { } } -export async function generateCacheKey(): Promise { - const lwAccount = getRequiredEnvVariable('LW_ACCOUNT') - const lwApiKey = getRequiredEnvVariable('LW_API_KEY') - const lwApiSecret = getRequiredEnvVariable('LW_API_SECRET') - - const containerName = `codesec-cache-key-${Date.now()}` - const envFile = createEnvFile() - - const dockerArgs = [ - 'run', - '--name', - containerName, - '-v', - `${process.cwd()}:/app/src`, - '--env-file', - envFile, - '-e', - `WORKSPACE=src`, - '-e', - `LW_ACCOUNT=${lwAccount}`, - '-e', - `LW_API_KEY=${lwApiKey}`, - '-e', - `LW_API_SECRET=${lwApiSecret}`, - '-e', - `GENERATE_CACHE_KEY=true`, - 'lacework/codesec:test', - 'scan', - ] +export async function generateCacheKey( + runIac: boolean, + runSca: boolean, + scanTarget?: string, + modifiedFiles?: string +): Promise { + const reportsDir = path.join(os.tmpdir(), `codesec-cache-${Date.now()}`) try { - await callCommand('docker', ...dockerArgs) + await runCodesec('scan', runIac, runSca, reportsDir, scanTarget, modifiedFiles, true) } catch (e) { info(`Cache key generation failed: ${(e as Error).message}`) - await callCommand('docker', 'rm', containerName).catch(() => {}) - return undefined - } - - const outputFile = path.join(os.tmpdir(), `cache-key-${Date.now()}.txt`) - try { - await callCommand( - 'docker', - 'container', - 'cp', - `${containerName}:/tmp/scan-results/sca/cache-key.txt`, - outputFile - ) - } catch (e) { - info(`Failed to copy cache key from container: ${(e as Error).message}`) - await callCommand('docker', 'rm', containerName).catch(() => {}) return undefined } - await callCommand('docker', 'rm', containerName) - + const outputFile = path.join(reportsDir, 'cache-key.txt') if (!existsSync(outputFile)) { - info('Cache key file not found after docker cp') + info('Cache key file not found after generation') return undefined } From 7c6a5be13e5e678a208eb570bb28535cf08b3a1b Mon Sep 17 00:00:00 2001 From: Teodor-Ioan Baltoi Date: Fri, 5 Jun 2026 16:03:01 +0100 Subject: [PATCH 3/3] nit --- src/index.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index dc34a669..83a8bd78 100644 --- a/src/index.ts +++ b/src/index.ts @@ -94,7 +94,12 @@ async function runAnalysis() { if (success && targetScan !== 'new') { // Save the analysis results when not scanning the PR source branch if (!cacheKey) { - cacheKey = await generateCacheKey(enableIacRunning, enableScaRunning, targetScan, modifiedFiles) + cacheKey = await generateCacheKey( + enableIacRunning, + enableScaRunning, + targetScan, + modifiedFiles + ) } if (cacheKey) { try {