Skip to content

Immutable actions support #8

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
4 changes: 3 additions & 1 deletion audit_workflow_runs.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand Down
84 changes: 80 additions & 4 deletions audit_workflow_runs.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -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),
Expand All @@ -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
Expand Down
51 changes: 46 additions & 5 deletions audit_workflow_runs_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,31 +59,72 @@ 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
const data = line.split(" ").slice(1).join(" ");
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;
}
}
Expand Down
4 changes: 4 additions & 0 deletions test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"tj-actions/changed-files": ["0e58ed8671d6b60d0890c21b07f8835ace038e67"],
"reviewdog/*": ["*"]
}
5 changes: 5 additions & 0 deletions test2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"aquasecurity/trivy-action": ["6e7b7d1fd3e4fef0c5fa8cce1229c54b2c9bd0d8"],
"actions/setup-node": ["*"],
"actions/checkout": ["*"]
}