Skip to content

Commit 525b648

Browse files
authored
Merge pull request #3374 from github/henrymercer/scan-debug-artifacts
CI: Perform a best-effort scan of the debug artifacts during release validation
2 parents eb823a7 + ac6c41b commit 525b648

File tree

14 files changed

+3722
-2463
lines changed

14 files changed

+3722
-2463
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
name: Verify that the best-effort debug artifact scan completed
2+
description: Verifies that the best-effort debug artifact scan completed successfully during tests
3+
runs:
4+
using: node24
5+
main: index.js
6+
post: post.js
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// The main step is a no-op, since we can only verify artifact scan completion in the post step.
2+
console.log("Will verify artifact scan completion in the post step.");
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Post step - runs after the workflow completes, when artifact scan has finished
2+
const process = require("process");
3+
4+
const scanFinished = process.env.CODEQL_ACTION_ARTIFACT_SCAN_FINISHED;
5+
6+
if (scanFinished !== "true") {
7+
console.error("Error: Best-effort artifact scan did not complete. Expected CODEQL_ACTION_ARTIFACT_SCAN_FINISHED=true");
8+
process.exit(1);
9+
}
10+
11+
console.log("✓ Best-effort artifact scan completed successfully");

.github/workflows/debug-artifacts-failure-safe.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ jobs:
5858
uses: actions/setup-dotnet@v5
5959
with:
6060
dotnet-version: '9.x'
61+
- name: Assert best-effort artifact scan completed
62+
uses: ./../action/.github/actions/verify-debug-artifact-scan-completed
6163
- uses: ./../action/init
6264
with:
6365
tools: ${{ steps.prepare-test.outputs.tools-url }}

.github/workflows/debug-artifacts-safe.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ jobs:
5454
uses: actions/setup-dotnet@v5
5555
with:
5656
dotnet-version: '9.x'
57+
- name: Assert best-effort artifact scan completed
58+
uses: ./../action/.github/actions/verify-debug-artifact-scan-completed
5759
- uses: ./../action/init
5860
id: init
5961
with:

lib/analyze-action-post.js

Lines changed: 1040 additions & 794 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/init-action-post.js

Lines changed: 1072 additions & 829 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/start-proxy-action-post.js

Lines changed: 45 additions & 42 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/upload-sarif-action-post.js

Lines changed: 1035 additions & 789 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/artifact-scanner.test.ts

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import * as fs from "fs";
2+
import * as os from "os";
3+
import * as path from "path";
4+
5+
import test from "ava";
6+
7+
import { scanArtifactsForTokens } from "./artifact-scanner";
8+
import { getRunnerLogger } from "./logging";
9+
import { getRecordingLogger, LoggedMessage } from "./testing-utils";
10+
11+
test("scanArtifactsForTokens detects GitHub tokens in files", async (t) => {
12+
const logger = getRunnerLogger(true);
13+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "scanner-test-"));
14+
15+
try {
16+
// Create a test file with a fake GitHub token
17+
const testFile = path.join(tempDir, "test.txt");
18+
fs.writeFileSync(
19+
testFile,
20+
"This is a test file with token ghp_1234567890123456789012345678901234AB",
21+
);
22+
23+
const error = await t.throwsAsync(
24+
async () => await scanArtifactsForTokens([testFile], logger),
25+
);
26+
27+
t.regex(
28+
error?.message || "",
29+
/Found 1 potential GitHub token.*Personal Access Token/,
30+
);
31+
t.regex(error?.message || "", /test\.txt/);
32+
} finally {
33+
// Clean up
34+
fs.rmSync(tempDir, { recursive: true, force: true });
35+
}
36+
});
37+
38+
test("scanArtifactsForTokens handles files without tokens", async (t) => {
39+
const logger = getRunnerLogger(true);
40+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "scanner-test-"));
41+
42+
try {
43+
// Create a test file without tokens
44+
const testFile = path.join(tempDir, "test.txt");
45+
fs.writeFileSync(
46+
testFile,
47+
"This is a test file without any sensitive data",
48+
);
49+
50+
await t.notThrowsAsync(
51+
async () => await scanArtifactsForTokens([testFile], logger),
52+
);
53+
} finally {
54+
// Clean up
55+
fs.rmSync(tempDir, { recursive: true, force: true });
56+
}
57+
});
58+
59+
if (os.platform() !== "win32") {
60+
test("scanArtifactsForTokens finds token in debug artifacts", async (t) => {
61+
t.timeout(15000); // 15 seconds
62+
const messages: LoggedMessage[] = [];
63+
const logger = getRecordingLogger(messages, { logToConsole: false });
64+
// The zip here is a regression test based on
65+
// https://github.com/github/codeql-action/security/advisories/GHSA-vqf5-2xx6-9wfm
66+
const testZip = path.join(
67+
__dirname,
68+
"..",
69+
"src",
70+
"testdata",
71+
"debug-artifacts-with-fake-token.zip",
72+
);
73+
74+
// This zip file contains a nested structure with a fake token in:
75+
// my-db-java-partial.zip/trap/java/invocations/kotlin.9017231652989744319.trap
76+
const error = await t.throwsAsync(
77+
async () => await scanArtifactsForTokens([testZip], logger),
78+
);
79+
80+
t.regex(
81+
error?.message || "",
82+
/Found.*potential GitHub token/,
83+
"Should detect token in nested zip",
84+
);
85+
t.regex(
86+
error?.message || "",
87+
/kotlin\.9017231652989744319\.trap/,
88+
"Should report the .trap file containing the token",
89+
);
90+
91+
const logOutput = messages.map((msg) => msg.message).join("\n");
92+
t.regex(
93+
logOutput,
94+
/^Extracting gz file: .*\.gz$/m,
95+
"Logs should show that .gz files were extracted",
96+
);
97+
});
98+
}

0 commit comments

Comments
 (0)