diff --git a/audit_workflow_runs.js b/audit_workflow_runs.js index 928130c..cc441b9 100644 --- a/audit_workflow_runs.js +++ b/audit_workflow_runs.js @@ -109,6 +109,8 @@ async function createActionsRunResults(owner, repo, run, actions) { name: action[0], version: action[1], sha: action[2], + immutable_version: action[3], + digest: action[4], }); } return action_run_results; @@ -280,7 +282,7 @@ async function main() { ? auditEnterpriseOrOrg(orgOrEntName, orgOrEnt, startDate, endDate) : auditRepo(orgOrEntName, startDate, endDate); - console.log("org,repo,workflow,run_id,created_at,name,version,sha"); + console.log("org,repo,workflow,run_id,created_at,name,version,sha,immutable_version,digest"); const checkActions = Object.keys(actionsToAudit).length > 0; diff --git a/audit_workflow_runs.test.js b/audit_workflow_runs.test.js index 1e97bec..339081e 100644 --- a/audit_workflow_runs.test.js +++ b/audit_workflow_runs.test.js @@ -86,8 +86,8 @@ function testSearchForActionsLines() { 2025-03-28T12:00:02Z Some other log line `; const expected1 = [ - ["actions/checkout", "v4", "11bd71901bbe5b1630ceea73d27597364c9af683"], - ["actions/setup-node", "v3", "22cd71901bbe5b1630ceea73d27597364c9af684"], + ["actions/checkout", "v4", "11bd71901bbe5b1630ceea73d27597364c9af683", null, null], + ["actions/setup-node", "v3", "22cd71901bbe5b1630ceea73d27597364c9af684", null, null], ]; assert.deepStrictEqual( searchForActionsLines(logContent1), @@ -112,7 +112,7 @@ function testSearchForActionsLines() { 2025-03-28T12:00:02Z Download action repository 'actions/setup-node@v3' (SHA:22cd71901bbe5b1630ceea73d27597364c9af684) `; const expected3 = [ - ["actions/checkout", "v4", "11bd71901bbe5b1630ceea73d27597364c9af683"], + ["actions/checkout", "v4", "11bd71901bbe5b1630ceea73d27597364c9af683", null, null], ]; assert.deepStrictEqual( searchForActionsLines(logContent3), @@ -133,12 +133,88 @@ function testSearchForActionsLines() { const logContent5 = `2025-03-28T12:00:00Z Download action repository 'actions/checkout@v4' (SHA:invalid_sha) 2025-03-28T12:00:01Z Malformed log line `; - const expected5 = [["actions/checkout", "v4", "invalid_sha"]]; + const expected5 = [["actions/checkout", "v4", "invalid_sha", null, null]]; assert.deepStrictEqual( searchForActionsLines(logContent5), expected5, "Should handle malformed log lines gracefully" ); + + // Test case: Immutable action groups, plus mutable actions + const logContent6 = `2025-07-14T13:16:54.1997507Z Current runner version: '2.326.0' +2025-07-14T13:16:54.2032898Z ##[group]Runner Image Provisioner +2025-07-14T13:16:54.2034192Z Hosted Compute Agent +2025-07-14T13:16:54.2035482Z Version: 20250711.363 +2025-07-14T13:16:54.2036437Z Commit: 6785254374ce925a23743850c1cb91912ce5c14c +2025-07-14T13:16:54.2037793Z Build Date: 2025-07-11T20:04:25Z +2025-07-14T13:16:54.2038731Z ##[endgroup] +2025-07-14T13:16:54.2039633Z ##[group]Operating System +2025-07-14T13:16:54.2040684Z Ubuntu +2025-07-14T13:16:54.2041493Z 24.04.2 +2025-07-14T13:16:54.2042229Z LTS +2025-07-14T13:16:54.2043203Z ##[endgroup] +2025-07-14T13:16:54.2044107Z ##[group]Runner Image +2025-07-14T13:16:54.2045360Z Image: ubuntu-24.04 +2025-07-14T13:16:54.2046348Z Version: 20250710.1.0 +2025-07-14T13:16:54.2048046Z Included Software: https://github.com/actions/runner-images/blob/ubuntu24/20250710.1/images/ubuntu/Ubuntu2404-Readme.md +2025-07-14T13:16:54.2050926Z Image Release: https://github.com/actions/runner-images/releases/tag/ubuntu24%2F20250710.1 +2025-07-14T13:16:54.2052697Z ##[endgroup] +2025-07-14T13:16:54.2054600Z ##[group]GITHUB_TOKEN Permissions +2025-07-14T13:16:54.2057484Z Contents: read +2025-07-14T13:16:54.2058290Z Metadata: read +2025-07-14T13:16:54.2059196Z Packages: read +2025-07-14T13:16:54.2060021Z ##[endgroup] +2025-07-14T13:16:54.2062876Z Secret source: Actions +2025-07-14T13:16:54.2064129Z Prepare workflow directory +2025-07-14T13:16:54.2740053Z Prepare all required actions +2025-07-14T13:16:54.2798345Z Getting action download info +2025-07-14T13:16:54.8182595Z ##[group]Download immutable action package 'actions/checkout@v4' +2025-07-14T13:16:54.8183770Z Version: 4.2.2 +2025-07-14T13:16:54.8185077Z Digest: sha256:ccb2698953eaebd21c7bf6268a94f9c26518a7e38e27e0b83c1fe1ad049819b1 +2025-07-14T13:16:54.8186424Z Source commit SHA: 11bd71901bbe5b1630ceea73d27597364c9af683 +2025-07-14T13:16:54.8187251Z ##[endgroup] +2025-07-14T13:16:54.9067829Z ##[group]Download immutable action package 'actions/setup-java@v4' +2025-07-14T13:16:54.9068748Z Version: 4.7.1 +2025-07-14T13:16:54.9069568Z Digest: sha256:23223d64943473efb4336f60463c0429cd4f422cd5fc6c48a5cf0d5907c1aeac +2025-07-14T13:16:54.9070517Z Source commit SHA: c5195efecf7bdfc987ee8bae7a71cb8b11521c00 +2025-07-14T13:16:54.9071298Z ##[endgroup] +2025-07-14T13:16:55.2797700Z Download action repository 'sbt/setup-sbt@v1' (SHA:234370af1319038bf8dc432f8a7e4b83078a1781) +2025-07-14T13:16:55.7318730Z Getting action download info +2025-07-14T13:16:56.0547136Z ##[group]Download immutable action package 'actions/cache@v4' +2025-07-14T13:16:56.0547618Z Version: 4.2.3 +2025-07-14T13:16:56.0548031Z Digest: sha256:c8a3bb963e1f1826d8fcc8d1354f0dd29d8ac1db1d4f6f20247055ae11b81ed9 +2025-07-14T13:16:56.0548578Z Source commit SHA: 5a3ec84eff668545956fd18022155c47e93e2684 +2025-07-14T13:16:56.0548968Z ##[endgroup] +2025-07-14T13:16:56.1885796Z Complete job name: hello +2025-07-14T13:16:56.2569979Z ##[group]Run actions/checkout@v4 +2025-07-14T13:16:56.2570683Z with: +2025-07-14T13:16:56.2571032Z repository: github/invented-repo-for-test +2025-07-14T13:16:56.2571669Z token: *** +2025-07-14T13:16:56.2571947Z ssh-strict: true +2025-07-14T13:16:56.2572231Z ssh-user: git +2025-07-14T13:16:56.2572519Z persist-credentials: true +2025-07-14T13:16:56.2572857Z clean: true +2025-07-14T13:16:56.2573169Z sparse-checkout-cone-mode: true +2025-07-14T13:16:56.2573505Z fetch-depth: 1 +2025-07-14T13:16:56.2573786Z fetch-tags: false +2025-07-14T13:16:56.2574068Z show-progress: true +2025-07-14T13:16:56.2574357Z lfs: false +2025-07-14T13:16:56.2574617Z submodules: false +2025-07-14T13:16:56.2575090Z set-safe-directory: true +2025-07-14T13:16:56.2575645Z ##[endgroup] +2025-07-14T13:16:56.4670673Z Syncing repository: github/invented-repo-for-test` + + const expected6 = [ + ["actions/checkout", "v4", "11bd71901bbe5b1630ceea73d27597364c9af683", "4.2.2", "ccb2698953eaebd21c7bf6268a94f9c26518a7e38e27e0b83c1fe1ad049819b1"], + ["actions/setup-java", "v4", "c5195efecf7bdfc987ee8bae7a71cb8b11521c00", "4.7.1", "23223d64943473efb4336f60463c0429cd4f422cd5fc6c48a5cf0d5907c1aeac"], + ["sbt/setup-sbt", "v1", "234370af1319038bf8dc432f8a7e4b83078a1781", null, null], + ["actions/cache", "v4", "5a3ec84eff668545956fd18022155c47e93e2684", "4.2.3", "c8a3bb963e1f1826d8fcc8d1354f0dd29d8ac1db1d4f6f20247055ae11b81ed9"], + ]; + assert.deepStrictEqual( + searchForActionsLines(logContent6), + expected6, + "Should extract actions from an immutable action group" + ); } // Run the tests diff --git a/audit_workflow_runs_utils.js b/audit_workflow_runs_utils.js index 84216b9..e07c2dc 100644 --- a/audit_workflow_runs_utils.js +++ b/audit_workflow_runs_utils.js @@ -59,12 +59,18 @@ export function parseFromInputFile(actionsToAuditFilename) { } // Regex to spot, e.g. Download action repository 'actions/checkout@v4' (SHA:11bd71901bbe5b1630ceea73d27597364c9af683) -const actionRegex = /^Download action repository '(.+?)' \(SHA:(.+?)\)/; +const mutableActionPrefix = "Download action repository '"; +const mutableActionRegex = /^Download action repository '([^']+?)' \(SHA:([^)]+?)\)/; +const immutableActionPrefix = "##[group]Download immutable action package '"; +const immutableActionRegex = /^##\[group\]Download immutable action package '([^'@]+?)@([^']*)'/; +const gettingDownloadInfoLine = "Getting action download info"; export function searchForActionsLines(logContent) { const logLines = logContent.split("\n"); const actions = []; let foundActions = false; + let inImmutableGroup = false; + let immutableAction = {}; for (const line of logLines) { // separate the timestamp from the data @@ -72,18 +78,53 @@ export function searchForActionsLines(logContent) { if (data == undefined) { continue; } - if (data.startsWith("Download action repository '")) { + if (data.startsWith(mutableActionPrefix)) { foundActions = true; - const match = actionRegex.exec(data); + const match = mutableActionRegex.exec(data); if (match) { const action = match[1]; const sha = match[2]; const [repo, version] = action.split("@"); - actions.push([repo, version, sha]); + actions.push([repo, version, sha, null, null]); } - // quit processing the log after the first line that is not an action, if we already found actions + } else if (data.startsWith(immutableActionPrefix)) { + foundActions = true; + inImmutableGroup = true; + const match = immutableActionRegex.exec(data); + if (match) { + const action = match[1]; + const tag = match[2]; + + immutableAction = { + action: action, + tag: tag, + version: null, + sha: null, + digest: null, + } + } + } else if (inImmutableGroup && data.startsWith("##[endgroup]")) { + actions.push([immutableAction.action, immutableAction.tag, immutableAction.sha, immutableAction.version, immutableAction.digest]); + inImmutableGroup = false; + } else if (inImmutableGroup) { + const versionMatch = data.match(/Version: ([a-zA-Z0-9._-]+)/); + const shaMatch = data.match(/Source commit SHA: ([a-f0-9]{40,})/); + const digestMatch = data.match(/Digest: sha256:([a-f0-9]{64})/); + if (versionMatch) { + immutableAction.version = versionMatch[1]; + } + if (shaMatch) { + immutableAction.sha = shaMatch[1]; + } + if (digestMatch) { + immutableAction.digest = digestMatch[1]; + } + } else if (data == gettingDownloadInfoLine) { + // continue processing the log until we find an action line + continue; } else if (foundActions) { + // quit processing the log after the first line that is not an action, if we already found actions break; } } diff --git a/test.json b/test.json new file mode 100644 index 0000000..1d0ccf8 --- /dev/null +++ b/test.json @@ -0,0 +1,4 @@ +{ + "tj-actions/changed-files": ["0e58ed8671d6b60d0890c21b07f8835ace038e67"], + "reviewdog/*": ["*"] +} diff --git a/test2.json b/test2.json new file mode 100644 index 0000000..7e22e19 --- /dev/null +++ b/test2.json @@ -0,0 +1,5 @@ +{ + "aquasecurity/trivy-action": ["6e7b7d1fd3e4fef0c5fa8cce1229c54b2c9bd0d8"], + "actions/setup-node": ["*"], + "actions/checkout": ["*"] +}