Use JSON for known checksums (#415)

Ports the hardening from astral-sh/setup-uv#1025 to ruff-action.\n\nThis
stores generated checksums as JSON data behind a small typed TypeScript
wrapper, preventing values sourced from release metadata from being
mixed into generated executable code. It also updates the checksum
workflow and packaged action artifacts, and adds a regression test for
code-like keys and escaped checksum values.\n\nTests: npm run build, npm
run check, npm test, npm run package
This commit is contained in:
eifinger-bot
2026-08-28 16:15:05 -04:00
committed by GitHub
parent 6d5deb5cee
commit 127e6a115e
10 changed files with 6692 additions and 6659 deletions
+1
View File
@@ -1,2 +1,3 @@
* text=auto eol=lf
dist/** -diff linguist-generated=true
src/download/checksum/known-checksums.json linguist-generated=true
+1 -1
View File
@@ -22,7 +22,7 @@ jobs:
id: update-known-checksums
run:
node dist/update-known-checksums/index.cjs
src/download/checksum/known-checksums.ts ${{ secrets.GITHUB_TOKEN }}
src/download/checksum/known-checksums.json ${{ secrets.GITHUB_TOKEN }}
- run: npm ci --ignore-scripts && npm run all
- name: Create Pull Request
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
@@ -0,0 +1,38 @@
import { promises as fs } from "node:fs";
import os from "node:os";
import path from "node:path";
import { expect, jest, test } from "@jest/globals";
const mockDownloadTool = jest.fn<() => Promise<string>>();
jest.unstable_mockModule("@actions/tool-cache", () => ({
downloadTool: mockDownloadTool,
}));
const { updateChecksums } = await import(
"../../../src/download/checksum/update-known-checksums"
);
test("serializes checksum entries as JSON data", async () => {
const tempDirectory = await fs.mkdtemp(
path.join(os.tmpdir(), "ruff-action-checksums-test-"),
);
const checksumPath = path.join(tempDirectory, "checksum");
const outputPath = path.join(tempDirectory, "known-checksums.json");
const checksum = 'checksum"\\value';
const platform = 'platform"\n};\ncompromised = true;';
const downloadUrl = `https://example.com/v1.0.0/ruff-1.0.0-${platform}.tar.gz.sha256`;
try {
await fs.writeFile(checksumPath, `${checksum} ruff.tar.gz`);
mockDownloadTool.mockResolvedValue(checksumPath);
await updateChecksums(outputPath, [downloadUrl]);
const content = await fs.readFile(outputPath, "utf8");
expect(JSON.parse(content)).toEqual({ [`${platform}-1.0.0`]: checksum });
expect(content.endsWith("\n")).toBe(true);
} finally {
await fs.rm(tempDirectory, { force: true, recursive: true });
}
});
Generated Vendored
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+1 -1
View File
@@ -20,7 +20,7 @@
"check": "biome check --write",
"package": "node scripts/build-dist.mjs",
"act": "act pull_request -W .github/workflows/test.yml --container-architecture linux/amd64 -s GITHUB_TOKEN=\"$(gh auth token)\"",
"update-known-checksums": "RUNNER_TEMP=known_checksums node dist/update-known-checksums/index.cjs src/download/checksum/known-checksums.ts \"$(gh auth token)\"",
"update-known-checksums": "RUNNER_TEMP=known_checksums node dist/update-known-checksums/index.cjs src/download/checksum/known-checksums.json \"$(gh auth token)\"",
"test:unit": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js",
"test": "npm run build && npm run test:unit",
"all": "npm run build && npm run check && npm run package && npm run test:unit"
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -5,25 +5,16 @@ export async function updateChecksums(
filePath: string,
downloadUrls: string[],
): Promise<void> {
await fs.rm(filePath);
await fs.appendFile(
filePath,
"// AUTOGENERATED_DO_NOT_EDIT\nexport const KNOWN_CHECKSUMS: { [key: string]: string } = {\n",
);
let firstLine = true;
const checksums: Record<string, string> = {};
for (const downloadUrl of downloadUrls) {
const key = getKey(downloadUrl);
if (key === undefined) {
continue;
}
const checksum = await getOrDownloadChecksum(key, downloadUrl);
if (!firstLine) {
await fs.appendFile(filePath, ",\n");
checksums[key] = checksum;
}
await fs.appendFile(filePath, ` "${key}":\n "${checksum}"`);
firstLine = false;
}
await fs.appendFile(filePath, ",\n};\n");
await fs.writeFile(filePath, `${JSON.stringify(checksums, null, 2)}\n`);
}
function getKey(downloadUrl: string): string | undefined {
+1
View File
@@ -5,6 +5,7 @@
"module": "esnext",
"moduleResolution": "bundler",
"noImplicitAny": true,
"resolveJsonModule": true,
"strict": true,
"target": "ES2022"
},