11import { error , getInput , info , setOutput } from '@actions/core'
2- import { existsSync , readFileSync } from 'fs'
2+ import { copyFileSync , existsSync , mkdirSync } from 'fs'
3+ import * as path from 'path'
34import {
45 downloadArtifact ,
56 postCommentIfInPr ,
67 resolveExistingCommentIfFound ,
78 uploadArtifact ,
89} from './actions'
9- import { callLaceworkCli , debug , generateUILink , getOptionalEnvVariable } from './util'
10+ import { callCommand , runCodesec , getOptionalEnvVariable , readMarkdownFile } from './util'
1011
11- import path from 'path'
12-
13- const artifactPrefix = getInput ( 'artifact-prefix' )
14- const sarifReportPath = getInput ( 'code-scanning-path' )
15- const comparisonMarkdownPath = 'comparison.md'
12+ // Global scanner toggles - set to false to disable a scanner globally
13+ const enableScaRunning = true
14+ const enableIacRunning = false // TODO: change to true when ready
1615
1716async function runAnalysis ( ) {
1817 const target = getInput ( 'target' )
@@ -30,85 +29,142 @@ async function runAnalysis() {
3029 info ( 'Analyzing ' + target )
3130 const toUpload : string [ ] = [ ]
3231
33- // command to print both sarif and lwjson formats
34- var args = [ 'scan' , '.' , '--formats' , 'sarif' , '--output' , sarifReportPath , '--deployment' , 'ci' ]
35- if ( target === 'push' ) {
36- args . push ( '--save-results' )
32+ // Run codesec Docker scanner
33+ // targetScan: 'new'/'old' for PR mode, 'scan' for push mode (should upload results to db)
34+ var targetScan = target
35+ if ( target == 'push' ) {
36+ targetScan = 'scan'
37+ }
38+ const resultsPath = await runCodesec ( 'scan' , enableIacRunning , enableScaRunning , targetScan )
39+
40+ // Upload SCA SARIF from the returned results path
41+ if ( enableScaRunning ) {
42+ const scaSarifFile = path . join ( resultsPath , 'sca' , `sca-${ targetScan } .sarif` )
43+ if ( existsSync ( scaSarifFile ) ) {
44+ info ( `Found SCA SARIF file to upload: ${ scaSarifFile } ` )
45+ toUpload . push ( scaSarifFile )
46+
47+ // Copy SARIF to code-scanning-path for backward compatibility
48+ const codeScanningPath = getInput ( 'code-scanning-path' )
49+ if ( codeScanningPath ) {
50+ info ( `Copying SARIF to code-scanning-path: ${ codeScanningPath } ` )
51+ copyFileSync ( scaSarifFile , codeScanningPath )
52+ }
53+ } else {
54+ info ( `SCA SARIF file not found at: ${ scaSarifFile } ` )
55+ }
3756 }
38- if ( debug ( ) ) {
39- args . push ( '--debug' )
57+
58+ // Upload IAC JSON from the returned results path
59+ if ( enableIacRunning ) {
60+ const iacJsonFile = path . join ( resultsPath , 'iac' , `iac-${ targetScan } .json` )
61+ if ( existsSync ( iacJsonFile ) ) {
62+ info ( `Found IAC JSON file to upload: ${ iacJsonFile } ` )
63+ toUpload . push ( iacJsonFile )
64+ } else {
65+ info ( `IAC JSON file not found at: ${ iacJsonFile } ` )
66+ }
4067 }
41- await callLaceworkCli ( ...args )
42- toUpload . push ( sarifReportPath )
4368
44- await uploadArtifact ( getArtifactName ( target ) , ...toUpload )
69+ const artifactPrefix = getInput ( 'artifact-prefix' )
70+ const artifactName =
71+ artifactPrefix !== '' ? artifactPrefix + '-results-' + target : 'results-' + target
72+ info ( `Uploading artifact '${ artifactName } ' with ${ toUpload . length } file(s)` )
73+ await uploadArtifact ( artifactName , ...toUpload )
4574 setOutput ( `${ target } -completed` , true )
4675}
4776
48- export async function compareResults ( oldReport : string , newReport : string ) : Promise < string > {
49- const args = [
50- 'compare' ,
51- '--old' ,
52- oldReport ,
53- '--new' ,
54- newReport ,
55- '--output' ,
56- sarifReportPath ,
57- '--markdown' ,
58- comparisonMarkdownPath ,
59- '--markdown-variant' ,
60- 'GitHub' ,
61- '--deployment' ,
62- 'ci' ,
63- ]
64- const uiLink = generateUILink ( )
65- if ( uiLink ) args . push ( ...[ '--ui-link' , uiLink ] )
66- if ( debug ( ) ) args . push ( '--debug' )
77+ async function displayResults ( ) {
78+ info ( 'Displaying results' )
6779
68- await callLaceworkCli ( ...args )
69- await uploadArtifact ( getArtifactName ( 'compare' ) , sarifReportPath , comparisonMarkdownPath )
80+ // Download artifacts from previous jobs
81+ const artifactOld = await downloadArtifact ( 'results-old' )
82+ const artifactNew = await downloadArtifact ( 'results-new' )
7083
71- return existsSync ( comparisonMarkdownPath ) ? readFileSync ( comparisonMarkdownPath , 'utf8' ) : ''
72- }
84+ // Create local scan-results directory for compare
85+ if ( enableScaRunning ) {
86+ mkdirSync ( 'scan-results/sca' , { recursive : true } )
87+ }
88+ if ( enableIacRunning ) {
89+ mkdirSync ( 'scan-results/iac' , { recursive : true } )
90+ }
7391
74- async function displayResults ( ) {
75- info ( 'Displaying results' )
76- const downloadStart = Date . now ( )
77- const artifactOld = await downloadArtifact ( getArtifactName ( 'old' ) )
78- const artifactNew = await downloadArtifact ( getArtifactName ( 'new' ) )
79- const sarifFileOld = path . join ( artifactOld , sarifReportPath )
80- const sarifFileNew = path . join ( artifactNew , sarifReportPath )
81-
82- var compareMessage : string
83- if ( existsSync ( sarifFileOld ) && existsSync ( sarifFileNew ) ) {
84- compareMessage = await compareResults ( sarifFileOld , sarifFileNew )
85- } else {
86- throw new Error ( 'SARIF file not found' )
92+ // Check and copy files for each scanner type
93+ const scaAvailable =
94+ enableScaRunning && ( await prepareScannerFiles ( 'sca' , artifactOld , artifactNew ) )
95+ const iacAvailable =
96+ enableIacRunning && ( await prepareScannerFiles ( 'iac' , artifactOld , artifactNew ) )
97+
98+ // Need at least one scanner to compare
99+ if ( ! scaAvailable && ! iacAvailable ) {
100+ info ( 'No scanner files available for comparison. Nothing to compare.' )
101+ setOutput ( 'display-completed' , true )
102+ return
87103 }
88104
89- const commentStart = Date . now ( )
90- if ( compareMessage . length > 0 && getInput ( 'token' ) . length > 0 ) {
91- info ( 'Posting comment to GitHub PR as there were new issues introduced:' )
92- if ( getInput ( 'footer' ) !== '' ) {
93- compareMessage += '\n\n' + getInput ( 'footer' )
105+ // Run codesec compare mode with available scanners
106+ await runCodesec ( 'compare' , enableIacRunning && iacAvailable , enableScaRunning && scaAvailable )
107+
108+ // Read comparison output - check all possible outputs
109+ const outputs = [
110+ 'scan-results/compare/merged-compare.md' ,
111+ 'scan-results/compare/sca-compare.md' ,
112+ 'scan-results/compare/iac-compare.md' ,
113+ ]
114+
115+ let message : string | null = null
116+ for ( const output of outputs ) {
117+ if ( existsSync ( output ) ) {
118+ info ( `Using comparison output: ${ output } ` )
119+ message = readMarkdownFile ( output )
120+ break
94121 }
95- info ( compareMessage )
96- const commentUrl = await postCommentIfInPr ( compareMessage )
122+ }
123+
124+ if ( ! message ) {
125+ info ( 'No comparison output produced. No changes detected.' )
126+ setOutput ( 'display-completed' , true )
127+ return
128+ }
129+
130+ // Check if there are new violations (non-zero count in "Found N new potential violations")
131+ const hasViolations = / F o u n d \s + [ 1 - 9 ] \d * \s + / . test ( message )
132+
133+ if ( hasViolations && getInput ( 'token' ) . length > 0 ) {
134+ info ( 'Posting comment to GitHub PR as there were new issues introduced' )
135+ const commentUrl = await postCommentIfInPr ( message )
97136 if ( commentUrl !== undefined ) {
98137 setOutput ( 'posted-comment' , commentUrl )
99138 }
100139 } else {
140+ // No new violations or no token - resolve existing comment if found
101141 await resolveExistingCommentIfFound ( )
102142 }
103- setOutput ( `display-completed` , true )
143+
144+ setOutput ( 'display-completed' , true )
104145}
105146
106- function getArtifactName ( target : string ) : string {
107- var artifactName = 'results-'
108- if ( artifactPrefix !== '' ) {
109- artifactName = artifactPrefix + '-' + artifactName
147+ async function prepareScannerFiles (
148+ scanner : 'sca' | 'iac' ,
149+ artifactOld : string ,
150+ artifactNew : string
151+ ) : Promise < boolean > {
152+ const ext = scanner === 'sca' ? 'sarif' : 'json'
153+ const oldPath = path . join ( artifactOld , 'scan-results' , scanner , `${ scanner } -old.${ ext } ` )
154+ const newPath = path . join ( artifactNew , 'scan-results' , scanner , `${ scanner } -new.${ ext } ` )
155+
156+ const oldExists = existsSync ( oldPath )
157+ const newExists = existsSync ( newPath )
158+
159+ if ( ! oldExists || ! newExists ) {
160+ info ( `${ scanner . toUpperCase ( ) } files not found for compare. old=${ oldExists } , new=${ newExists } ` )
161+ return false
110162 }
111- return artifactName + target
163+
164+ info ( `Copying ${ scanner . toUpperCase ( ) } files for compare` )
165+ await callCommand ( 'cp' , oldPath , path . join ( 'scan-results' , scanner , `${ scanner } -old.${ ext } ` ) )
166+ await callCommand ( 'cp' , newPath , path . join ( 'scan-results' , scanner , `${ scanner } -new.${ ext } ` ) )
167+ return true
112168}
113169
114170async function main ( ) {
0 commit comments