80 lines
2.6 KiB
JavaScript
80 lines
2.6 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import fs from "node:fs";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import { afterEach, describe, it } from "vitest";
|
|
import { inspectWorkflowActionPins, verifyActionPins } from "./verify-action-pins.mjs";
|
|
|
|
const temporaryDirectories = [];
|
|
|
|
afterEach(() => {
|
|
for (const directory of temporaryDirectories.splice(0)) {
|
|
fs.rmSync(directory, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
function createWorkflow(source) {
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), "hivegrid-action-pins-"));
|
|
temporaryDirectories.push(root);
|
|
const directory = path.join(root, ".gitea/workflows");
|
|
fs.mkdirSync(directory, { recursive: true });
|
|
fs.writeFileSync(path.join(directory, "ci.yml"), source);
|
|
return root;
|
|
}
|
|
|
|
describe("workflow action pin verification", () => {
|
|
it("accepts commit SHAs, image digests, and local workflows", () => {
|
|
const source = [
|
|
"steps:",
|
|
" - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4",
|
|
` - uses: docker://alpine@sha256:${"a".repeat(64)}`,
|
|
" - uses: ./.gitea/workflows/local.yml",
|
|
].join("\n");
|
|
|
|
const result = inspectWorkflowActionPins(source, "ci.yml");
|
|
|
|
assert.equal(result.failures.length, 0);
|
|
assert.equal(result.passes.length, 3);
|
|
});
|
|
|
|
it("rejects tags, branches, missing refs, uppercase SHAs, and mutable images", () => {
|
|
const source = [
|
|
"steps:",
|
|
" - uses: actions/checkout@v4",
|
|
" - uses: owner/action@main",
|
|
" - uses: owner/action",
|
|
` - uses: owner/action@${"A".repeat(40)}`,
|
|
" - uses: docker://alpine:3.20",
|
|
].join("\n");
|
|
|
|
const result = inspectWorkflowActionPins(source, "ci.yml");
|
|
|
|
assert.equal(result.failures.length, 5);
|
|
});
|
|
|
|
it("checks workflow directories recursively", () => {
|
|
const root = createWorkflow("steps:\n - uses: actions/cache@v4\n");
|
|
|
|
const result = verifyActionPins(root);
|
|
|
|
assert.equal(result.failures.length, 1);
|
|
assert.match(result.failures[0], /actions\/cache@v4/);
|
|
});
|
|
|
|
it("accepts exact executable package versions and rejects mutable ranges", () => {
|
|
const source = [
|
|
"steps:",
|
|
" - run: npx --yes @lhci/cli@0.13.0 autorun",
|
|
" - run: pnpm dlx @lhci/cli@0.13.x autorun",
|
|
" - run: npx -y @subframe/cli@latest sync --all",
|
|
].join("\n");
|
|
|
|
const result = inspectWorkflowActionPins(source, "ci.yml");
|
|
|
|
assert.equal(result.passes.length, 1);
|
|
assert.equal(result.failures.length, 2);
|
|
assert.match(result.failures[0], /0\.13\.x/);
|
|
assert.match(result.failures[1], /latest/);
|
|
});
|
|
});
|