@codyswann/lisa 3.46.0 → 3.46.2
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/lisa-gates.mjs +74 -3
- package/cdk/copy-overwrite/knip.json +1 -0
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +2 -0
- package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +13 -9
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/expo/copy-overwrite/eslint.config.ts +18 -13
- package/expo/copy-overwrite/knip.json +9 -1
- package/harper-fabric/copy-overwrite/knip.json +1 -1
- package/nestjs/copy-overwrite/knip.json +1 -0
- package/package.json +7 -3
- package/phaser/copy-overwrite/knip.json +1 -1
- 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-security-floors.mjs +110 -13
- package/typescript/copy-overwrite/knip.json +1 -0
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
* @module scripts/check-security-floors
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
+
import { execFileSync } from "node:child_process";
|
|
26
27
|
import { readFileSync } from "node:fs";
|
|
27
28
|
import { globSync } from "node:fs";
|
|
28
29
|
|
|
@@ -72,19 +73,80 @@ export function resolveSelfReference(manifest, name) {
|
|
|
72
73
|
return null;
|
|
73
74
|
}
|
|
74
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Where the governance manifests live.
|
|
78
|
+
*
|
|
79
|
+
* The root entry is listed separately because the per-stack pattern is one
|
|
80
|
+
* level deep and never matched it. That miss cost nine floors — the root
|
|
81
|
+
* manifest is the one whose force/defaults/merge sections decide what every
|
|
82
|
+
* project's package.json becomes — and the audit reported clean for all of
|
|
83
|
+
* them, because a floor nobody looked at is indistinguishable in the output
|
|
84
|
+
* from a floor that passed.
|
|
85
|
+
*/
|
|
86
|
+
const MANIFEST_PATTERNS = Object.freeze([
|
|
87
|
+
"package.lisa.json",
|
|
88
|
+
"*/package-lisa/package.lisa.json",
|
|
89
|
+
]);
|
|
90
|
+
|
|
91
|
+
/** Paths that are copies of a manifest rather than a manifest. */
|
|
92
|
+
const NOT_A_MANIFEST = [
|
|
93
|
+
"node_modules",
|
|
94
|
+
".worktrees",
|
|
95
|
+
"dist/",
|
|
96
|
+
"tests/fixtures",
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Every manifest git tracks, so the glob can be checked against reality.
|
|
101
|
+
*
|
|
102
|
+
* Returns null when the question cannot be asked — outside a checkout, or with
|
|
103
|
+
* no git. A null is reported as "not reconciled", never folded into "nothing
|
|
104
|
+
* missing": the whole defect being fixed here is a scan that narrowed silently.
|
|
105
|
+
* @returns {string[]|null} Tracked manifest paths, or null when unknowable.
|
|
106
|
+
*/
|
|
107
|
+
function trackedManifests() {
|
|
108
|
+
try {
|
|
109
|
+
return execFileSync(
|
|
110
|
+
"/usr/bin/git",
|
|
111
|
+
["ls-files", "-z", "*package.lisa.json"],
|
|
112
|
+
{
|
|
113
|
+
encoding: "utf8",
|
|
114
|
+
maxBuffer: 8 * 1024 * 1024,
|
|
115
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
116
|
+
}
|
|
117
|
+
)
|
|
118
|
+
.split("\0")
|
|
119
|
+
.filter(Boolean)
|
|
120
|
+
.filter(
|
|
121
|
+
file => !NOT_A_MANIFEST.some(fragment => file.includes(fragment))
|
|
122
|
+
);
|
|
123
|
+
} catch {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
75
128
|
/**
|
|
76
129
|
* Every version constraint declared across the template manifests.
|
|
77
130
|
* @returns {{found: Map<string, Array<{file: string, path: string, spec: string}>>,
|
|
78
|
-
* unresolved: Array<{file: string, path: string, name: string, spec: string}
|
|
79
|
-
*
|
|
131
|
+
* unresolved: Array<{file: string, path: string, name: string, spec: string}>,
|
|
132
|
+
* scanned: string[], unscanned: string[]|null}}
|
|
133
|
+
* Constraints by package name, self-references pointing at nothing, the
|
|
134
|
+
* manifests read, and any tracked manifest the patterns did not reach.
|
|
80
135
|
*/
|
|
81
|
-
function collectFloors() {
|
|
136
|
+
export function collectFloors() {
|
|
82
137
|
const found = new Map();
|
|
83
138
|
/** `$name` references pointing at nothing this manifest declares. */
|
|
84
139
|
const unresolved = [];
|
|
85
|
-
const files = globSync(
|
|
86
|
-
file => !
|
|
140
|
+
const files = globSync(MANIFEST_PATTERNS).filter(
|
|
141
|
+
file => !NOT_A_MANIFEST.some(fragment => file.includes(fragment))
|
|
87
142
|
);
|
|
143
|
+
// What the patterns reach, against what the repository actually carries. A
|
|
144
|
+
// glob is exactly the kind of thing that narrows by one level and keeps
|
|
145
|
+
// reporting a clean sheet for the files it stopped opening.
|
|
146
|
+
const tracked = trackedManifests();
|
|
147
|
+
const scanned = new Set(files);
|
|
148
|
+
const unscanned =
|
|
149
|
+
tracked === null ? null : tracked.filter(file => !scanned.has(file));
|
|
88
150
|
for (const file of files) {
|
|
89
151
|
let manifest;
|
|
90
152
|
try {
|
|
@@ -137,7 +199,7 @@ function collectFloors() {
|
|
|
137
199
|
}
|
|
138
200
|
}
|
|
139
201
|
}
|
|
140
|
-
return { found, unresolved };
|
|
202
|
+
return { found, unresolved, scanned: files, unscanned };
|
|
141
203
|
}
|
|
142
204
|
|
|
143
205
|
/**
|
|
@@ -246,10 +308,11 @@ async function advisoriesFor(name) {
|
|
|
246
308
|
|
|
247
309
|
/**
|
|
248
310
|
* Audit every collected floor.
|
|
249
|
-
* @returns {Promise<{problems: Array, unreachable: Array, unresolved: Array,
|
|
311
|
+
* @returns {Promise<{problems: Array, unreachable: Array, unresolved: Array,
|
|
312
|
+
* unscanned: string[]|null, manifests: number, checked: number}>} Findings.
|
|
250
313
|
*/
|
|
251
314
|
async function audit() {
|
|
252
|
-
const { found: floors, unresolved } = collectFloors();
|
|
315
|
+
const { found: floors, unresolved, scanned, unscanned } = collectFloors();
|
|
253
316
|
const problems = [];
|
|
254
317
|
const unreachable = [];
|
|
255
318
|
for (const [name, sites] of floors) {
|
|
@@ -281,21 +344,33 @@ async function audit() {
|
|
|
281
344
|
}
|
|
282
345
|
}
|
|
283
346
|
}
|
|
284
|
-
return {
|
|
347
|
+
return {
|
|
348
|
+
problems,
|
|
349
|
+
unreachable,
|
|
350
|
+
unresolved,
|
|
351
|
+
unscanned,
|
|
352
|
+
manifests: scanned.length,
|
|
353
|
+
checked: floors.size,
|
|
354
|
+
};
|
|
285
355
|
}
|
|
286
356
|
|
|
287
357
|
async function main() {
|
|
288
358
|
const strict = process.argv.includes("--strict");
|
|
289
359
|
const asJson = process.argv.includes("--json");
|
|
290
|
-
const { problems, unreachable, unresolved, checked } =
|
|
360
|
+
const { problems, unreachable, unresolved, unscanned, manifests, checked } =
|
|
361
|
+
await audit();
|
|
291
362
|
|
|
292
363
|
if (asJson) {
|
|
293
364
|
console.log(
|
|
294
|
-
JSON.stringify(
|
|
365
|
+
JSON.stringify(
|
|
366
|
+
{ problems, unreachable, unresolved, unscanned, manifests, checked },
|
|
367
|
+
null,
|
|
368
|
+
2
|
|
369
|
+
)
|
|
295
370
|
);
|
|
296
371
|
} else if (problems.length === 0) {
|
|
297
372
|
console.log(
|
|
298
|
-
`## Security floors\n\nNo force-pinned floor permits a high or critical vulnerable release. ${checked} package(s) checked.`
|
|
373
|
+
`## Security floors\n\nNo force-pinned floor permits a high or critical vulnerable release. ${checked} package(s) across ${manifests} manifest(s) checked.`
|
|
299
374
|
);
|
|
300
375
|
} else {
|
|
301
376
|
console.log("## Security floors\n");
|
|
@@ -331,6 +406,23 @@ async function main() {
|
|
|
331
406
|
);
|
|
332
407
|
}
|
|
333
408
|
|
|
409
|
+
// A manifest the patterns never opened is the same failure as a package the
|
|
410
|
+
// advisory API never answered for: unexamined, and reported clean unless the
|
|
411
|
+
// difference is stated. Printed whether or not anything else went wrong.
|
|
412
|
+
if (!asJson && unscanned === null) {
|
|
413
|
+
console.log(
|
|
414
|
+
"\n> **Manifest coverage was not reconciled.** `git ls-files` could not be consulted, so nothing confirms the patterns reached every tracked manifest."
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
if (!asJson && unscanned !== null && unscanned.length > 0) {
|
|
418
|
+
console.log(
|
|
419
|
+
`\n> **${unscanned.length} tracked manifest(s) were NOT scanned.** Their floors are audited by nothing, which reads as clean: ${unscanned.join(", ")}.`
|
|
420
|
+
);
|
|
421
|
+
console.log(
|
|
422
|
+
"> Fix by widening `MANIFEST_PATTERNS` in this script and the `paths:` trigger in `.github/workflows/security-floors.yml` to match."
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
|
|
334
426
|
if (!asJson && unreachable.length > 0) {
|
|
335
427
|
const limited = unreachable.filter(entry =>
|
|
336
428
|
entry.reason.includes("rate limited")
|
|
@@ -357,9 +449,14 @@ async function main() {
|
|
|
357
449
|
// degradation this script exists to prevent.
|
|
358
450
|
// An unresolved self-reference gates for the same reason an inconclusive one
|
|
359
451
|
// does: it is a floor nobody checked, and letting it pass reports it clean.
|
|
452
|
+
// An unscanned tracked manifest gates for the same reason an inconclusive
|
|
453
|
+
// lookup does: it is a floor nobody checked, and passing reports it clean.
|
|
360
454
|
if (
|
|
361
455
|
strict &&
|
|
362
|
-
(problems.length > 0 ||
|
|
456
|
+
(problems.length > 0 ||
|
|
457
|
+
unreachable.length > 0 ||
|
|
458
|
+
unresolved.length > 0 ||
|
|
459
|
+
(unscanned !== null && unscanned.length > 0))
|
|
363
460
|
) {
|
|
364
461
|
process.exitCode = 1;
|
|
365
462
|
}
|