Skip to content

Commit 0c8bfea

Browse files
committed
Add artifact scanner
1 parent 5eb7519 commit 0c8bfea

File tree

3 files changed

+476
-0
lines changed

3 files changed

+476
-0
lines changed

src/artifact-scanner.test.ts

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
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 "../.github/workflows/artifact-scanner/artifact-scanner";
8+
import { getRunnerLogger } from "./logging";
9+
10+
test("scanArtifactsForTokens detects GitHub tokens in files", async (t) => {
11+
const logger = getRunnerLogger(true);
12+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "scanner-test-"));
13+
14+
try {
15+
// Create a test file with a fake GitHub token
16+
const testFile = path.join(tempDir, "test.txt");
17+
fs.writeFileSync(
18+
testFile,
19+
"This is a test file with token ghp_1234567890123456789012345678901234AB",
20+
);
21+
22+
const result = await scanArtifactsForTokens([testFile], logger);
23+
24+
t.is(result.scannedFiles, 1);
25+
t.is(result.findings.length, 1);
26+
t.is(result.findings[0].tokenType, "Personal Access Token");
27+
t.is(result.findings[0].filePath, "test.txt");
28+
} finally {
29+
// Clean up
30+
fs.rmSync(tempDir, { recursive: true, force: true });
31+
}
32+
});
33+
34+
test("scanArtifactsForTokens handles files without tokens", async (t) => {
35+
const logger = getRunnerLogger(true);
36+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "scanner-test-"));
37+
38+
try {
39+
// Create a test file without tokens
40+
const testFile = path.join(tempDir, "test.txt");
41+
fs.writeFileSync(
42+
testFile,
43+
"This is a test file without any sensitive data",
44+
);
45+
46+
const result = await scanArtifactsForTokens([testFile], logger);
47+
48+
t.is(result.scannedFiles, 1);
49+
t.is(result.findings.length, 0);
50+
} finally {
51+
// Clean up
52+
fs.rmSync(tempDir, { recursive: true, force: true });
53+
}
54+
});
55+
56+
test("scanArtifactsForTokens skips binary files", async (t) => {
57+
const logger = getRunnerLogger(true);
58+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "scanner-test-"));
59+
60+
try {
61+
// Create a binary file (we'll just use a simple zip for this test)
62+
const zipFile = path.join(tempDir, "test.zip");
63+
fs.writeFileSync(zipFile, Buffer.from([0x50, 0x4b, 0x03, 0x04])); // ZIP header
64+
65+
const result = await scanArtifactsForTokens([zipFile], logger);
66+
67+
// The zip file itself should be counted but not scanned for tokens
68+
t.is(result.findings.length, 0);
69+
} finally {
70+
// Clean up
71+
fs.rmSync(tempDir, { recursive: true, force: true });
72+
}
73+
});
74+
75+
test("scanArtifactsForTokens detects tokens in debug artifacts zip", async (t) => {
76+
const logger = getRunnerLogger(true);
77+
const testZipPath = path.join(
78+
__dirname,
79+
"..",
80+
"..",
81+
"..",
82+
"src",
83+
"testdata",
84+
"debug-artifacts-with-fake-token.zip",
85+
);
86+
87+
const result = await scanArtifactsForTokens([testZipPath], logger);
88+
89+
t.true(result.scannedFiles > 0, "Should have scanned files");
90+
t.true(
91+
result.findings.length > 0,
92+
"Should have found tokens in the test zip",
93+
);
94+
95+
// Check that the token types are tracked
96+
const serverToServerFindings = result.findings.filter(
97+
(f) => f.tokenType === "Server-to-Server Token",
98+
);
99+
t.is(
100+
serverToServerFindings.length,
101+
1,
102+
"Should have found exactly 1 Server-to-Server Token",
103+
);
104+
105+
// Check that the path includes the nested structure
106+
const expectedPath =
107+
"debug-artifacts-with-fake-token.zip/debug-artifacts-with-test-token/my-db-java-partial.zip/my-db-java-partial/trap/java/invocations/kotlin.9017231652989744319.trap";
108+
t.true(
109+
result.findings.some((f) => f.filePath === expectedPath),
110+
`Expected to find token at ${expectedPath}, but found: ${result.findings.map((f) => f.filePath).join(", ")}`,
111+
);
112+
});

0 commit comments

Comments
 (0)