Skip to content
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
45 changes: 42 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
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,
postCommentIfInPr,
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
Expand Down Expand Up @@ -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')

Expand All @@ -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
Expand All @@ -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
Expand Down
31 changes: 29 additions & 2 deletions src/util.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -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:
Expand All @@ -130,7 +155,8 @@ export async function runCodesec(
runIac: boolean = false,
runSca: boolean = false,
reportsDir: string,
scanTarget?: string
scanTarget?: string,
modifiedFiles?: string
): Promise<boolean> {
const lwAccount = getRequiredEnvVariable('LW_ACCOUNT')
const lwApiKey = getRequiredEnvVariable('LW_API_KEY')
Expand Down Expand Up @@ -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',
]
Expand Down
Loading