@codyswann/lisa 4.35.5 → 4.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/all/copy-overwrite/scripts/check-third-party-action-pins.mjs +554 -0
- package/dist/core/downstream-references.d.ts.map +1 -1
- package/dist/core/downstream-references.js +4 -0
- package/dist/core/downstream-references.js.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +10 -0
- package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
- package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
- package/dist/core/third-party-action-pins.d.ts +84 -0
- package/dist/core/third-party-action-pins.d.ts.map +1 -0
- package/dist/core/third-party-action-pins.js +153 -0
- package/dist/core/third-party-action-pins.js.map +1 -0
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +11 -4
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/dist/migrations/ensure-third-party-action-pins.d.ts +123 -0
- package/dist/migrations/ensure-third-party-action-pins.d.ts.map +1 -0
- package/dist/migrations/ensure-third-party-action-pins.js +238 -0
- package/dist/migrations/ensure-third-party-action-pins.js.map +1 -0
- package/dist/migrations/index.d.ts +1 -0
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/migrations/index.js +3 -0
- package/dist/migrations/index.js.map +1 -1
- package/package.json +4 -4
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/scripts/check-third-party-action-pins.mjs +111 -14
- package/scripts/two-channel-couplings.json +1 -1
- package/typescript/copy-overwrite/.prettierignore +1 -0
- package/typescript/package-lisa/package.lisa.json +1 -0
|
@@ -0,0 +1,554 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// This file is managed by Lisa and IS replaced on each `lisa` run.
|
|
3
|
+
// Do not edit directly — durable changes belong upstream in Lisa.
|
|
4
|
+
/**
|
|
5
|
+
* Deterministic gate that every THIRD-PARTY GitHub Action this repository uses
|
|
6
|
+
* — in its own workflows and in the workflow templates it seeds into consumers
|
|
7
|
+
* — is referenced by a full 40-character commit SHA (issue #3585).
|
|
8
|
+
*
|
|
9
|
+
* A mutable ref is not a version. `@master`, `@main`, `@v1` and `@v0.9.0` are
|
|
10
|
+
* pointers that resolve, at job start, to whatever the upstream owner has
|
|
11
|
+
* pushed at that instant, and for a third-party action that owner is a
|
|
12
|
+
* stranger. A tag is mutable by definition — `git push --force` moves it — and
|
|
13
|
+
* a floating major is *designed* to move.
|
|
14
|
+
*
|
|
15
|
+
* The exposure is not theoretical here. When this gate was written the tree
|
|
16
|
+
* held 19 distinct third-party references, none pinned, thirteen of them in
|
|
17
|
+
* jobs holding a credential:
|
|
18
|
+
*
|
|
19
|
+
* - `snyk/actions/node@master` with the Snyk token in the same step's `env:`;
|
|
20
|
+
* - `fossas/fossa-action@main` handed the FOSSA key as an input;
|
|
21
|
+
* - `noliran/branch-based-secrets@v1`, whose ENTIRE FUNCTION is to resolve
|
|
22
|
+
* repository secrets into the job environment, in the publish path;
|
|
23
|
+
* - `GeoWerkstatt/create-jira-release@v1`, handed a webhook secret.
|
|
24
|
+
*
|
|
25
|
+
* `quality.yml` and `release.yml` are reusable workflows the fleet calls at
|
|
26
|
+
* `@main`, so a compromised upstream would execute inside every consumer's
|
|
27
|
+
* quality gate on their next run.
|
|
28
|
+
*
|
|
29
|
+
* Pinning today does not stop the next `uses:` line, which is why the durable
|
|
30
|
+
* artifact is this detector rather than the pins. A stale comment in a
|
|
31
|
+
* consumer repository nearly caused two load-bearing references to be
|
|
32
|
+
* unpinned; prose does not hold a boundary, an executable check does.
|
|
33
|
+
*
|
|
34
|
+
* ## What is deliberately NOT in scope
|
|
35
|
+
*
|
|
36
|
+
* **First-party references.** `uses: CodySwannGT/lisa/...@main` is a different
|
|
37
|
+
* threat model — a stability question about an upstream we own and can read,
|
|
38
|
+
* tracked separately in #3488 — and a check that reddens it is the wrong check
|
|
39
|
+
* and will be turned off. First-party references never appear in this gate's
|
|
40
|
+
* findings and are never named in its output. That exemption is a required
|
|
41
|
+
* property with its own test, not an afterthought.
|
|
42
|
+
*
|
|
43
|
+
* **`actions/*` and `github/*`.** These are published by GitHub itself, the
|
|
44
|
+
* same party that runs the job, so pinning them moves no trust boundary. The
|
|
45
|
+
* decision is recorded here so it stays consistent, and the exemption is
|
|
46
|
+
* narrow: it is an OWNER allowlist, not a substring match.
|
|
47
|
+
*
|
|
48
|
+
* **Fixture trees** (`tests/fixtures/`, `parity/fixtures/`). These are inputs
|
|
49
|
+
* to other checks, never executed by GitHub Actions, and pinning them would
|
|
50
|
+
* distort the shapes those checks exist to recognise. The count of skipped
|
|
51
|
+
* fixture files is REPORTED in every run rather than silently dropped, so the
|
|
52
|
+
* boundary is visible in the output instead of buried in this comment.
|
|
53
|
+
*
|
|
54
|
+
* **Consumer repositories already seeded from the `create-only` templates.**
|
|
55
|
+
* A `create-only` file is written once and never overwritten, so correcting a
|
|
56
|
+
* template changes what FUTURE repositories are seeded with and changes
|
|
57
|
+
* nothing about any repository that already exists. This gate runs in this
|
|
58
|
+
* repository only; it cannot and does not report seeded consumers as fixed.
|
|
59
|
+
*
|
|
60
|
+
* ## Why a bare SHA is also a finding
|
|
61
|
+
*
|
|
62
|
+
* `uses: <vendor>/<action>@<40-hex>` with no trailing comment is
|
|
63
|
+
* immutable but unreadable: nobody can tell what is installed, so nobody
|
|
64
|
+
* upgrades it, so it rots at whatever commit it was pinned at. The pin and the
|
|
65
|
+
* human-readable version are one artifact, and the gate treats them that way.
|
|
66
|
+
*
|
|
67
|
+
* Determinism guarantees (so the unit test is reproducible and CI is stable):
|
|
68
|
+
* - zero third-party dependencies (Node built-ins only),
|
|
69
|
+
* - no network access,
|
|
70
|
+
* - no `Date` / `Math.random`,
|
|
71
|
+
* - the file list comes from `git ls-files`, so the gate sees exactly what a
|
|
72
|
+
* release would carry.
|
|
73
|
+
*
|
|
74
|
+
* ## `--warn`: how this arrives in a consumer without breaking it
|
|
75
|
+
*
|
|
76
|
+
* This file also ships to consumers through `all/copy-overwrite/scripts/`,
|
|
77
|
+
* which — unlike `create-only` — reaches every repository that already exists,
|
|
78
|
+
* on its next update. That reach is exactly why it must not arrive blocking.
|
|
79
|
+
*
|
|
80
|
+
* A consumer's seeded workflows carry mutable refs *today*: that is the defect
|
|
81
|
+
* #3588 exists to migrate away. A detector that hard-failed on arrival would
|
|
82
|
+
* redden the entire installed base at once, for a condition those repositories
|
|
83
|
+
* did not introduce and could not have fixed yet, since the migration that
|
|
84
|
+
* fixes it lands in the same update. That is not hypothetical — a guard added
|
|
85
|
+
* to a workflow consumed at `@main` failed closed on a pre-existing consumer
|
|
86
|
+
* misconfiguration and took four repositories' releases down for five hours;
|
|
87
|
+
* the repair was to keep it loud and stop it blocking (#3755, #3757).
|
|
88
|
+
*
|
|
89
|
+
* So `--warn` reports every finding and exits 0, and the shipped consumer
|
|
90
|
+
* script entry uses it. Tightening to blocking is a separate, deliberate
|
|
91
|
+
* decision for once the installed base has had a chance to migrate — one flag
|
|
92
|
+
* on one line — not something to "finish" while consumers are still mutable.
|
|
93
|
+
*
|
|
94
|
+
* `--warn` also softens the empty-scan usage error, because a consumer with no
|
|
95
|
+
* workflow files is an ordinary shape rather than a broken invocation. A
|
|
96
|
+
* misspelled flag stays a hard error in every mode: that one is the caller's.
|
|
97
|
+
*
|
|
98
|
+
* CLI:
|
|
99
|
+
* node scripts/check-third-party-action-pins.mjs [--root <dir>] [--json] [--warn]
|
|
100
|
+
*
|
|
101
|
+
* Exit codes (mirroring the sibling gates in this directory):
|
|
102
|
+
* 0 — every third-party reference is a 40-hex SHA carrying a version comment;
|
|
103
|
+
* or `--warn` was passed and the findings are being reported, not enforced.
|
|
104
|
+
* 1 — >=1 reference is mutable, or pinned without a version comment.
|
|
105
|
+
* 2 — operational/usage error: unknown flag, a flag missing its value,
|
|
106
|
+
* `--root` absent or not a git repository, git unavailable, or zero
|
|
107
|
+
* workflow files discovered. Finding nothing to check is a broken
|
|
108
|
+
* invocation, not conformance — except under `--warn`, see above.
|
|
109
|
+
*
|
|
110
|
+
* @module scripts/check-third-party-action-pins
|
|
111
|
+
*/
|
|
112
|
+
import fs from "node:fs";
|
|
113
|
+
import path from "node:path";
|
|
114
|
+
import process from "node:process";
|
|
115
|
+
import { fileURLToPath } from "node:url";
|
|
116
|
+
|
|
117
|
+
import { boundedExecFileSync } from "./lib/bounded-spawn.mjs";
|
|
118
|
+
import { invokedAsScript } from "./lib/invoked-as-script.mjs";
|
|
119
|
+
|
|
120
|
+
const REPO_ROOT = path.resolve(
|
|
121
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
122
|
+
".."
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Any workflow file, at any depth: this repository's own `.github/workflows/`
|
|
127
|
+
* and every seeded template tree, so a mutable ref cannot be introduced by
|
|
128
|
+
* seeding rather than by running.
|
|
129
|
+
*/
|
|
130
|
+
const WORKFLOW_RE = /(^|\/)\.github\/workflows\/[^/]+\.ya?ml$/;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Fixture trees: inputs to other checks, never executed by GitHub Actions.
|
|
134
|
+
* Prefix match on the repo-relative path, so a real workflow cannot hide
|
|
135
|
+
* behind a directory that merely contains the word "fixtures".
|
|
136
|
+
*/
|
|
137
|
+
const FIXTURE_PREFIXES = ["tests/fixtures/", "parity/fixtures/"];
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* The owner of this repository's own reusable workflows. Compared
|
|
141
|
+
* case-insensitively because GitHub owners are case-insensitive and a `uses:`
|
|
142
|
+
* line may spell one either way.
|
|
143
|
+
*/
|
|
144
|
+
const FIRST_PARTY_OWNERS = ["codyswanngt"];
|
|
145
|
+
|
|
146
|
+
/** GitHub's own action namespaces — the runner's publisher, see header. */
|
|
147
|
+
const GITHUB_OWNED_OWNERS = ["actions", "github"];
|
|
148
|
+
|
|
149
|
+
/** Classification of an action's owner against the two documented exemptions. */
|
|
150
|
+
const FIRST_PARTY = "first-party";
|
|
151
|
+
const GITHUB_OWNED = "github-owned";
|
|
152
|
+
const THIRD_PARTY = "third-party";
|
|
153
|
+
|
|
154
|
+
/** Verdicts `evaluateReference` returns; `MUTABLE` is the headline finding. */
|
|
155
|
+
const OK = "ok";
|
|
156
|
+
const MUTABLE = "mutable-ref";
|
|
157
|
+
const NO_VERSION = "missing-version-comment";
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* `uses: <owner>/<repo>[/<subpath>]@<ref>` with anything after it captured, so
|
|
161
|
+
* a trailing `# v1.2.3` can be read. Local (`./path`) and container
|
|
162
|
+
* (`docker://`) references have no owner and never match.
|
|
163
|
+
*/
|
|
164
|
+
const USES_RE = /^[ \t]*(?:-[ \t]*)?uses:[ \t]*(?<value>[^\s"'#]+)/;
|
|
165
|
+
|
|
166
|
+
/** A well-formed action owner. Anchored, so matching is linear. */
|
|
167
|
+
const OWNER_RE = /^[A-Za-z0-9][A-Za-z0-9-]*$/;
|
|
168
|
+
|
|
169
|
+
/** A well-formed action path below the owner. Anchored, so linear. */
|
|
170
|
+
const ACTION_PATH_RE = /^[A-Za-z0-9_.][A-Za-z0-9_./-]*$/;
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Split a `uses:` value into owner, action path, and ref.
|
|
174
|
+
*
|
|
175
|
+
* The shape is validated with anchored parts rather than described as one
|
|
176
|
+
* pattern. Spelling `owner/repo/path@ref` as adjacent character classes gives
|
|
177
|
+
* the engine several ways to divide the same text, which is super-linear on a
|
|
178
|
+
* long line; splitting first and validating each piece has one.
|
|
179
|
+
*
|
|
180
|
+
* @param {string} value - the raw value of a `uses:` line.
|
|
181
|
+
* @returns {{ owner: string, rest: string, ref: string } | null} parts, or null
|
|
182
|
+
* when this is not an `owner/repo@ref` reference (a local `./path` or a
|
|
183
|
+
* `docker://` image never is).
|
|
184
|
+
*/
|
|
185
|
+
function parseUsesValue(value) {
|
|
186
|
+
const at = value.lastIndexOf("@");
|
|
187
|
+
if (at <= 0) return null;
|
|
188
|
+
const action = value.slice(0, at);
|
|
189
|
+
const ref = value.slice(at + 1);
|
|
190
|
+
const slash = action.indexOf("/");
|
|
191
|
+
if (ref === "" || slash <= 0 || action.includes("://")) return null;
|
|
192
|
+
const owner = action.slice(0, slash);
|
|
193
|
+
const rest = action.slice(slash + 1);
|
|
194
|
+
if (!OWNER_RE.test(owner) || !ACTION_PATH_RE.test(rest)) return null;
|
|
195
|
+
return { owner, rest, ref };
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/** A full commit SHA. Abbreviated SHAs are ambiguous and do not count. */
|
|
199
|
+
const FULL_SHA_RE = /^[0-9a-f]{40}$/;
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Read the version out of a line's trailing `# ...` comment.
|
|
203
|
+
*
|
|
204
|
+
* Deliberately not a regex. Every spelling of "text after a hash to end of
|
|
205
|
+
* line" is either a backtracking shape or an unanchored `.*$` the engine
|
|
206
|
+
* retries from each position, and neither is worth it for a one-character
|
|
207
|
+
* search.
|
|
208
|
+
*
|
|
209
|
+
* @param {string} trailer - everything on the line after the `uses:` value.
|
|
210
|
+
* @returns {string | null} the comment text, or null when there is none.
|
|
211
|
+
*/
|
|
212
|
+
function versionComment(trailer) {
|
|
213
|
+
const hash = trailer.indexOf("#");
|
|
214
|
+
if (hash < 0) return null;
|
|
215
|
+
const version = trailer.slice(hash + 1).trim();
|
|
216
|
+
return version === "" ? null : version;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/** Max bytes of `git ls-files` output (7k+ tracked paths is ~0.3 MB today). */
|
|
220
|
+
const MAX_GIT_OUTPUT_BYTES = 64 * 1024 * 1024;
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Usage error — thrown for an invalid invocation or an unusable state so
|
|
224
|
+
* `main` can distinguish it (exit 2) from a finding (exit 1).
|
|
225
|
+
*/
|
|
226
|
+
export class UsageError extends Error {}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Classify an action owner against the two documented exemptions.
|
|
230
|
+
*
|
|
231
|
+
* @param {string} owner - the owner segment of a `uses:` reference.
|
|
232
|
+
* @returns {"first-party" | "github-owned" | "third-party"} the classification.
|
|
233
|
+
*/
|
|
234
|
+
export function classifyOwner(owner) {
|
|
235
|
+
const normalized = String(owner).toLowerCase();
|
|
236
|
+
if (FIRST_PARTY_OWNERS.includes(normalized)) return FIRST_PARTY;
|
|
237
|
+
if (GITHUB_OWNED_OWNERS.includes(normalized)) return GITHUB_OWNED;
|
|
238
|
+
return THIRD_PARTY;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Extract every action reference in `content`, already classified.
|
|
243
|
+
*
|
|
244
|
+
* Every reference is returned, first-party ones included, so the caller can
|
|
245
|
+
* count what it examined. Only third-party ones are ever eligible to become a
|
|
246
|
+
* finding — see `evaluateReference`.
|
|
247
|
+
*
|
|
248
|
+
* @param {string} content - full text of a workflow file.
|
|
249
|
+
* @returns {{ action: string, owner: string, ref: string, version: string | null, kind: string, line: number }[]}
|
|
250
|
+
* one entry per reference, with 1-based line numbers, in file order.
|
|
251
|
+
*/
|
|
252
|
+
export function findActionRefs(content) {
|
|
253
|
+
const lines = String(content).split(/\r?\n/);
|
|
254
|
+
const refs = [];
|
|
255
|
+
for (let index = 0; index < lines.length; index++) {
|
|
256
|
+
const match = USES_RE.exec(lines[index]);
|
|
257
|
+
if (match === null) continue;
|
|
258
|
+
const parsed = parseUsesValue(match.groups.value);
|
|
259
|
+
if (parsed === null) continue;
|
|
260
|
+
const { owner, ref, rest } = parsed;
|
|
261
|
+
refs.push({
|
|
262
|
+
action: `${owner}/${rest}`,
|
|
263
|
+
kind: classifyOwner(owner),
|
|
264
|
+
line: index + 1,
|
|
265
|
+
owner,
|
|
266
|
+
ref,
|
|
267
|
+
// The rest of the line is sliced rather than captured: a trailing `.*`
|
|
268
|
+
// after the value's character class gives the engine two ways to divide
|
|
269
|
+
// the same text, which is the super-linear shape.
|
|
270
|
+
version: versionComment(lines[index].slice(match[0].length)),
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
return refs;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Decide whether one reference is a finding.
|
|
278
|
+
*
|
|
279
|
+
* @param {{ kind: string, ref: string, version: string | null }} reference -
|
|
280
|
+
* a reference from `findActionRefs`.
|
|
281
|
+
* @returns {"ok" | "mutable-ref" | "missing-version-comment"} the verdict.
|
|
282
|
+
*/
|
|
283
|
+
export function evaluateReference(reference) {
|
|
284
|
+
if (reference.kind !== THIRD_PARTY) return OK;
|
|
285
|
+
if (!FULL_SHA_RE.test(reference.ref)) return MUTABLE;
|
|
286
|
+
return reference.version === null ? NO_VERSION : OK;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Whether a repo-relative path is a workflow this gate governs.
|
|
291
|
+
*
|
|
292
|
+
* @param {string} file - repo-relative path.
|
|
293
|
+
* @returns {boolean} whether the file is an in-scope workflow.
|
|
294
|
+
*/
|
|
295
|
+
export function isGovernedWorkflow(file) {
|
|
296
|
+
return WORKFLOW_RE.test(file) && !isFixtureWorkflow(file);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Whether a repo-relative path is an excluded fixture workflow.
|
|
301
|
+
*
|
|
302
|
+
* @param {string} file - repo-relative path.
|
|
303
|
+
* @returns {boolean} whether the file sits in an excluded fixture tree.
|
|
304
|
+
*/
|
|
305
|
+
export function isFixtureWorkflow(file) {
|
|
306
|
+
return (
|
|
307
|
+
WORKFLOW_RE.test(file) &&
|
|
308
|
+
FIXTURE_PREFIXES.some(prefix => file.startsWith(prefix))
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* List every tracked file in `root`, relative to it. Throws `UsageError` when
|
|
314
|
+
* git is unavailable or `root` is not a repository.
|
|
315
|
+
*
|
|
316
|
+
* @param {string} root - the repository root.
|
|
317
|
+
* @returns {string[]} tracked paths, relative to `root`.
|
|
318
|
+
*/
|
|
319
|
+
function listTrackedFiles(root) {
|
|
320
|
+
let stdout;
|
|
321
|
+
try {
|
|
322
|
+
stdout = boundedExecFileSync("git", ["-C", root, "ls-files", "-z"], {
|
|
323
|
+
encoding: "utf8",
|
|
324
|
+
maxBuffer: MAX_GIT_OUTPUT_BYTES,
|
|
325
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
326
|
+
});
|
|
327
|
+
} catch (error) {
|
|
328
|
+
throw new UsageError(
|
|
329
|
+
`could not list tracked files in ${root}: ${error.message}`
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
return stdout.split("\0").filter(entry => entry !== "");
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Assemble the machine-readable report.
|
|
337
|
+
*
|
|
338
|
+
* @param {ReadonlyArray<Record<string, unknown>>} findings - non-ok references.
|
|
339
|
+
* @param {{ root: string, workflows: number, fixturesSkipped: number, thirdParty: number, checked: number }} opts
|
|
340
|
+
* resolved options plus scan size.
|
|
341
|
+
* @returns {Record<string, unknown>} the report object.
|
|
342
|
+
*/
|
|
343
|
+
export function buildReport(findings, opts) {
|
|
344
|
+
return {
|
|
345
|
+
findings,
|
|
346
|
+
root: opts.root,
|
|
347
|
+
schemaVersion: 1,
|
|
348
|
+
summary: {
|
|
349
|
+
checked: opts.checked,
|
|
350
|
+
fixturesSkipped: opts.fixturesSkipped,
|
|
351
|
+
missingVersionComment: findings.filter(
|
|
352
|
+
finding => finding.verdict === NO_VERSION
|
|
353
|
+
).length,
|
|
354
|
+
mutable: findings.filter(finding => finding.verdict === MUTABLE).length,
|
|
355
|
+
thirdParty: opts.thirdParty,
|
|
356
|
+
workflows: opts.workflows,
|
|
357
|
+
},
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* The remediation text. A guard that reddens without teaching the fix gets
|
|
363
|
+
* bypassed, so the exact command that produces the SHA is in the output.
|
|
364
|
+
*
|
|
365
|
+
* @returns {string[]} lines of remediation guidance.
|
|
366
|
+
*/
|
|
367
|
+
function remediation() {
|
|
368
|
+
return [
|
|
369
|
+
"Pin every third-party action to a full 40-character commit SHA, with the",
|
|
370
|
+
"human-readable version in a trailing comment so the pin stays auditable:",
|
|
371
|
+
"",
|
|
372
|
+
" - uses: <vendor>/<action>@<40-hex-sha> # v1.2.3",
|
|
373
|
+
"",
|
|
374
|
+
"Resolve the SHA for the ref the workflow uses TODAY — pinning and",
|
|
375
|
+
"upgrading in one commit makes a regression unattributable:",
|
|
376
|
+
"",
|
|
377
|
+
" gh api repos/<owner>/<repo>/commits/<ref> --jq .sha",
|
|
378
|
+
"",
|
|
379
|
+
"Then name the version that SHA carries:",
|
|
380
|
+
"",
|
|
381
|
+
" gh api repos/<owner>/<repo>/tags --jq '.[] | select(.commit.sha==\"<sha>\") | .name'",
|
|
382
|
+
"",
|
|
383
|
+
"A branch head with no tag is pinned the same way; say so in the comment,",
|
|
384
|
+
"e.g. `# main (untagged, 2026-08-28)`.",
|
|
385
|
+
"",
|
|
386
|
+
"First-party workflow references and GitHub's own actions/* and github/*",
|
|
387
|
+
"are out of scope for this gate by design and are never reported here.",
|
|
388
|
+
];
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Render the human-readable report.
|
|
393
|
+
*
|
|
394
|
+
* @param {{ findings: ReadonlyArray<Record<string, unknown>>, summary: Record<string, number> }} report
|
|
395
|
+
* the report object.
|
|
396
|
+
* @returns {string} the rendered report.
|
|
397
|
+
*/
|
|
398
|
+
function humanReport(report) {
|
|
399
|
+
const { summary } = report;
|
|
400
|
+
const scanned =
|
|
401
|
+
`${summary.thirdParty} third-party reference(s) across ` +
|
|
402
|
+
`${summary.workflows} workflow file(s) ` +
|
|
403
|
+
`(${summary.fixturesSkipped} fixture workflow(s) skipped)`;
|
|
404
|
+
if (report.findings.length === 0) {
|
|
405
|
+
return `✓ ${scanned} — all pinned to a full commit SHA with a version comment`;
|
|
406
|
+
}
|
|
407
|
+
const lines = report.findings.map(finding =>
|
|
408
|
+
finding.verdict === MUTABLE
|
|
409
|
+
? `✗ ${finding.file}:${finding.line}\n ${finding.action}@${finding.ref} — mutable ref, not a commit SHA`
|
|
410
|
+
: `✗ ${finding.file}:${finding.line}\n ${finding.action}@${finding.ref} — pinned but no version comment`
|
|
411
|
+
);
|
|
412
|
+
return [
|
|
413
|
+
...lines,
|
|
414
|
+
"",
|
|
415
|
+
`${report.findings.length} of ${scanned} are not safely pinned.`,
|
|
416
|
+
"",
|
|
417
|
+
...remediation(),
|
|
418
|
+
].join("\n");
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Parse argv into resolved options. Throws `UsageError` on a bad invocation.
|
|
423
|
+
*
|
|
424
|
+
* @param {readonly string[]} argv - arguments (without node/script prefix).
|
|
425
|
+
* @returns {{ root: string, json: boolean }} options.
|
|
426
|
+
*/
|
|
427
|
+
export function parseArgs(argv) {
|
|
428
|
+
let root = null;
|
|
429
|
+
let json = false;
|
|
430
|
+
let warn = false;
|
|
431
|
+
for (let i = 0; i < argv.length; i++) {
|
|
432
|
+
const arg = argv[i];
|
|
433
|
+
if (arg === "--json") {
|
|
434
|
+
json = true;
|
|
435
|
+
} else if (arg === "--warn") {
|
|
436
|
+
warn = true;
|
|
437
|
+
} else if (arg === "--root") {
|
|
438
|
+
const next = argv[i + 1];
|
|
439
|
+
if (next === undefined || next.startsWith("--")) {
|
|
440
|
+
throw new UsageError("--root requires a value");
|
|
441
|
+
}
|
|
442
|
+
root = next;
|
|
443
|
+
i += 1;
|
|
444
|
+
} else {
|
|
445
|
+
throw new UsageError(`unknown argument: ${arg}`);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
return { json, warn, root: path.resolve(root ?? REPO_ROOT) };
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Resolve the file list for a run, or throw `UsageError`.
|
|
453
|
+
*
|
|
454
|
+
* @param {string} root - the repository root.
|
|
455
|
+
* @returns {{ workflows: string[], fixturesSkipped: number }} the scan inputs.
|
|
456
|
+
*/
|
|
457
|
+
function collectWorkflows(root) {
|
|
458
|
+
if (!fs.existsSync(root) || !fs.statSync(root).isDirectory()) {
|
|
459
|
+
throw new UsageError(`--root is not a directory: ${root}`);
|
|
460
|
+
}
|
|
461
|
+
const tracked = listTrackedFiles(root);
|
|
462
|
+
const workflows = tracked.filter(file => isGovernedWorkflow(file));
|
|
463
|
+
if (workflows.length === 0) {
|
|
464
|
+
// Finding nothing to check is a broken invocation, not conformance.
|
|
465
|
+
throw new UsageError(
|
|
466
|
+
`no workflow files found under ${root} — expected paths like ` +
|
|
467
|
+
".github/workflows/*.yml. Refusing to report a clean run for a scan " +
|
|
468
|
+
"that examined nothing."
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
return {
|
|
472
|
+
fixturesSkipped: tracked.filter(file => isFixtureWorkflow(file)).length,
|
|
473
|
+
workflows,
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* Run the gate. Returns the process exit code (does not call `exit`).
|
|
479
|
+
*
|
|
480
|
+
* @param {readonly string[]} argv - arguments (without node/script prefix).
|
|
481
|
+
* @param {{ stdout?: { write(s: string): void }, stderr?: { write(s: string): void } }} [io]
|
|
482
|
+
* injectable streams (defaults to process streams).
|
|
483
|
+
* @returns {number} exit code (0 clean, 1 finding, 2 usage error).
|
|
484
|
+
*/
|
|
485
|
+
export function main(argv, io = {}) {
|
|
486
|
+
const out = io.stdout ?? process.stdout;
|
|
487
|
+
const err = io.stderr ?? process.stderr;
|
|
488
|
+
let opts;
|
|
489
|
+
try {
|
|
490
|
+
opts = parseArgs(argv);
|
|
491
|
+
} catch (error) {
|
|
492
|
+
// A misspelled flag is the caller's mistake and stays loud in every mode.
|
|
493
|
+
err.write(`error: ${error.message}\n`);
|
|
494
|
+
return 2;
|
|
495
|
+
}
|
|
496
|
+
let scan;
|
|
497
|
+
try {
|
|
498
|
+
scan = collectWorkflows(opts.root);
|
|
499
|
+
} catch (error) {
|
|
500
|
+
// Under --warn this is a repository with nothing to scan, which for a
|
|
501
|
+
// consumer is an ordinary shape rather than a broken invocation.
|
|
502
|
+
err.write(`error: ${error.message}\n`);
|
|
503
|
+
return opts.warn ? 0 : 2;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
let checked = 0;
|
|
507
|
+
let thirdParty = 0;
|
|
508
|
+
const findings = [];
|
|
509
|
+
try {
|
|
510
|
+
for (const file of scan.workflows) {
|
|
511
|
+
const content = fs.readFileSync(path.join(opts.root, file), "utf8");
|
|
512
|
+
for (const reference of findActionRefs(content)) {
|
|
513
|
+
checked += 1;
|
|
514
|
+
if (reference.kind === THIRD_PARTY) thirdParty += 1;
|
|
515
|
+
const verdict = evaluateReference(reference);
|
|
516
|
+
if (verdict !== OK) {
|
|
517
|
+
findings.push({ ...reference, file, verdict });
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
} catch (error) {
|
|
522
|
+
err.write(`error: failed to scan workflows: ${error.message}\n`);
|
|
523
|
+
return 2;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
const report = buildReport(findings, {
|
|
527
|
+
checked,
|
|
528
|
+
fixturesSkipped: scan.fixturesSkipped,
|
|
529
|
+
root: opts.root,
|
|
530
|
+
thirdParty,
|
|
531
|
+
workflows: scan.workflows.length,
|
|
532
|
+
});
|
|
533
|
+
out.write(
|
|
534
|
+
`${opts.json ? JSON.stringify(report, null, 2) : humanReport(report)}\n`
|
|
535
|
+
);
|
|
536
|
+
if (report.findings.length === 0) {
|
|
537
|
+
return 0;
|
|
538
|
+
}
|
|
539
|
+
if (opts.warn) {
|
|
540
|
+
out.write(
|
|
541
|
+
"\nReporting only: this check is not blocking here. Nothing above stops " +
|
|
542
|
+
"a build.\nRun `npx @codyswann/lisa@latest .` to pin these " +
|
|
543
|
+
"references, then re-run.\n"
|
|
544
|
+
);
|
|
545
|
+
return 0;
|
|
546
|
+
}
|
|
547
|
+
return 1;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
if (invokedAsScript(import.meta.url)) {
|
|
551
|
+
// exitCode (not process.exit): when stdout is a pipe, writes are async and
|
|
552
|
+
// process.exit() truncates the report mid-flush.
|
|
553
|
+
process.exitCode = main(process.argv.slice(2));
|
|
554
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"downstream-references.d.ts","sourceRoot":"","sources":["../../src/core/downstream-references.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGzD;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,EAAE,SAAS,MAAM,
|
|
1
|
+
{"version":3,"file":"downstream-references.d.ts","sourceRoot":"","sources":["../../src/core/downstream-references.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGzD;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,EAAE,SAAS,MAAM,EAyExC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,EAAE,SAAS,MAAM,EAqB/C,CAAC;AA2BF;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,EAAE,SAAS,MAAM,EASjD,CAAC;AAEF,gEAAgE;AAChE,MAAM,WAAW,mBAAmB;IAClC,6BAA6B;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,wBAAwB;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,6BAA6B;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAuFD;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,WAA+B,GACvC,SAAS,mBAAmB,EAAE,CAQhC"}
|
|
@@ -9,6 +9,10 @@ import { findHostNames, hostNameEntries } from "./downstream-names.js";
|
|
|
9
9
|
export const PUBLIC_ORGS = [
|
|
10
10
|
"codyswanngt",
|
|
11
11
|
"actions",
|
|
12
|
+
// The Rust community's Actions org. Named only as a lookalike fixture: it is
|
|
13
|
+
// what proves the `actions` exemption is an owner allowlist rather than a
|
|
14
|
+
// prefix match, which a substring check would silently get wrong.
|
|
15
|
+
"actions-rs",
|
|
12
16
|
"github",
|
|
13
17
|
"anthropics",
|
|
14
18
|
"openai",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"downstream-references.js","sourceRoot":"","sources":["../../src/core/downstream-references.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"downstream-references.js","sourceRoot":"","sources":["../../src/core/downstream-references.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lisa-owned-hash-ledger.d.ts","sourceRoot":"","sources":["../../src/core/lisa-owned-hash-ledger.ts"],"names":[],"mappings":"AACA,6EAA6E;AAE7E;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAC3C,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"lisa-owned-hash-ledger.d.ts","sourceRoot":"","sources":["../../src/core/lisa-owned-hash-ledger.ts"],"names":[],"mappings":"AACA,6EAA6E;AAE7E;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAC3C,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAu+BjC,CAAC;AAEH;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,+BAA+B,EAAE,QAAQ,CACpD,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CA04BjC,CAAC"}
|
|
@@ -281,6 +281,11 @@ export const LISA_OWNED_HASH_LEDGER = Object.freeze({
|
|
|
281
281
|
"ac539349c86a7b5b0083ec0e6aaf0eb4c3e40fbd4dc31511c6460a2bad7af9a5",
|
|
282
282
|
"b218927f64a3db4f37762b60014e29d85a46899f4a0d17fcecc7782ce48bc499",
|
|
283
283
|
]),
|
|
284
|
+
"scripts/check-third-party-action-pins.mjs": Object.freeze([
|
|
285
|
+
"2c8b35c5ddab14420c24652d6fb907c882e93f308c6be7ab4c15f8bdf635479d",
|
|
286
|
+
"79117b039e0d7df001e60835ad3538126ba88273bc5fa3c1d30d8e8e704e77cc",
|
|
287
|
+
"b578c55d2d8d63243de502e332f2dd2e0ae2a670c6ed18c418c67abb96c8b8fd",
|
|
288
|
+
]),
|
|
284
289
|
"scripts/check-third-party-review-evidence.mjs": Object.freeze([
|
|
285
290
|
"225e69c41b56f2eb82cdc1a16da26770090c3ccac85fe0dad278938d8ae4f769",
|
|
286
291
|
"95f91f2a74ed4e523d064d5d374eadd1c1114f03a3a4fa41532c9b0fd32089de",
|
|
@@ -1272,6 +1277,11 @@ export const LISA_OWNED_HASH_HISTORY_DERIVED = Object.freeze({
|
|
|
1272
1277
|
"ac539349c86a7b5b0083ec0e6aaf0eb4c3e40fbd4dc31511c6460a2bad7af9a5",
|
|
1273
1278
|
"b218927f64a3db4f37762b60014e29d85a46899f4a0d17fcecc7782ce48bc499",
|
|
1274
1279
|
]),
|
|
1280
|
+
"scripts/check-third-party-action-pins.mjs": Object.freeze([
|
|
1281
|
+
"2c8b35c5ddab14420c24652d6fb907c882e93f308c6be7ab4c15f8bdf635479d",
|
|
1282
|
+
"79117b039e0d7df001e60835ad3538126ba88273bc5fa3c1d30d8e8e704e77cc",
|
|
1283
|
+
"b578c55d2d8d63243de502e332f2dd2e0ae2a670c6ed18c418c67abb96c8b8fd",
|
|
1284
|
+
]),
|
|
1275
1285
|
"scripts/check-third-party-review-evidence.mjs": Object.freeze([
|
|
1276
1286
|
"225e69c41b56f2eb82cdc1a16da26770090c3ccac85fe0dad278938d8ae4f769",
|
|
1277
1287
|
"95f91f2a74ed4e523d064d5d374eadd1c1114f03a3a4fa41532c9b0fd32089de",
|