From 127e6a115e4fdbd928e06e221a87d6dc07eb6f93 Mon Sep 17 00:00:00 2001 From: eifinger-bot Date: Fri, 28 Aug 2026 22:15:05 +0200 Subject: [PATCH] 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 --- .gitattributes | 1 + .github/workflows/update-known-checksums.yml | 2 +- .../checksum/update-known-checksums.test.ts | 38 + dist/ruff-action/index.cjs | Bin 1471020 -> 1471122 bytes dist/update-known-checksums/index.cjs | Bin 1428116 -> 1427867 bytes package.json | 2 +- src/download/checksum/known-checksums.json | 6644 ++++++++++++++++ src/download/checksum/known-checksums.ts | 6648 +---------------- .../checksum/update-known-checksums.ts | 15 +- tsconfig.json | 1 + 10 files changed, 6692 insertions(+), 6659 deletions(-) create mode 100644 __tests__/download/checksum/update-known-checksums.test.ts create mode 100644 src/download/checksum/known-checksums.json diff --git a/.gitattributes b/.gitattributes index 00f4c25..d51fc8e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,3 @@ * text=auto eol=lf dist/** -diff linguist-generated=true +src/download/checksum/known-checksums.json linguist-generated=true diff --git a/.github/workflows/update-known-checksums.yml b/.github/workflows/update-known-checksums.yml index edbd478..8d6c260 100644 --- a/.github/workflows/update-known-checksums.yml +++ b/.github/workflows/update-known-checksums.yml @@ -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 diff --git a/__tests__/download/checksum/update-known-checksums.test.ts b/__tests__/download/checksum/update-known-checksums.test.ts new file mode 100644 index 0000000..281a6c0 --- /dev/null +++ b/__tests__/download/checksum/update-known-checksums.test.ts @@ -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>(); + +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 }); + } +}); diff --git a/dist/ruff-action/index.cjs b/dist/ruff-action/index.cjs index 26c0f064e628306df4693889a6ea53423685dc13..777d9a6211f68bdd5a5186d6580600290fb27045 100644 GIT binary patch delta 155 zcmZ4UBy!Tz$c7fi7N!>F7M2#)7Pc1l7LFFq7OocV7M>Q~7QQX~$-F9A#rb($Wr;-! z*?IZpdGW~^sma;JrMboNDXD3Rr8y-EwhGnL4+itgw9n+_2Vwyr76f7;AQs*}lUF1s fYF7M2#)7Pc1l7LFFq7OocV7M>Q~7QQX~$-L9$Q$!>gtEU^* o@yoO?=H&-s0U#CxVj&F7M2#)7Pc1l7LFFq7OocV7M>Q~7QPn#Edp-oDp|$(d0b_Q zMGDz@`Q>@>$r-81*~O)~#qlYrX^EvdB?`6*)zb@S2*^yon>q_rZ^5mrk~j>S}2m7pI2O>0Ji~Ta_w}5{i3I*uRkoxAzmDvomv?S z6osj<=2B3o<(kfTKvcn|yeP9I)h#n8RU-{Z1SFPZ=qMzpRC@*c`{@;z6lLb6XQowZ bz;qVtDCCvqF7M2#)7Pc1l7LFFq7OocV7M>Q~7QPn#Edp-o)0y^(N-|bY ze|ShhroAm)0Eh*FSO|!PfmmdFTe@gY*mS>rqM_3l?-!NhEy~qM%gjj)NG!?Fw4T0T zzo=q;VnIP_UWyw~RD(+a2w?I$V4jk`zJg = knownChecksums; diff --git a/src/download/checksum/update-known-checksums.ts b/src/download/checksum/update-known-checksums.ts index 61c0071..97eb576 100644 --- a/src/download/checksum/update-known-checksums.ts +++ b/src/download/checksum/update-known-checksums.ts @@ -5,25 +5,16 @@ export async function updateChecksums( filePath: string, downloadUrls: string[], ): Promise { - 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 = {}; 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"); - } - await fs.appendFile(filePath, ` "${key}":\n "${checksum}"`); - firstLine = false; + checksums[key] = checksum; } - await fs.appendFile(filePath, ",\n};\n"); + await fs.writeFile(filePath, `${JSON.stringify(checksums, null, 2)}\n`); } function getKey(downloadUrl: string): string | undefined { diff --git a/tsconfig.json b/tsconfig.json index a4f725c..2faa490 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,7 @@ "module": "esnext", "moduleResolution": "bundler", "noImplicitAny": true, + "resolveJsonModule": true, "strict": true, "target": "ES2022" },