Files
fusion/scripts/__tests__/dependency-security-floor.test.mjs
gsxdsm 83294a6958 fix(test): the protobufjs security floor was asserted against an empty object (reads like a deleted pin; it moved) (#3035)
`scripts/__tests__` has **seven** files failing on main. None are in the
merge gate, so nobody is blocked — which is exactly how this survived.
This fixes the one that names a real risk.

## It reads like a deleted security pin. It isn't.

```
AssertionError: package.json pnpm.overrides: protobufjs range undefined
                must include an explicit semver version
```

#2220 moved pnpm overrides from `package.json` to `pnpm-workspace.yaml`
for pnpm 11 readiness. The assertion kept reading `package.json`, where
`pnpm.overrides` is now `{}`.

**Nothing was ever exposed**: `pnpm-workspace.yaml` still pins
`protobufjs: ^7.5.8` and the lockfile resolves `7.6.5`, comfortably
above the 7.5.5 floor. The sibling assertion that checks lockfile
resolutions has been passing the whole time — which is the only reason
this was survivable.

But no reader could distinguish this message from a genuinely removed
pin without doing the archaeology, and a guard that is permanently red
while naming a real risk teaches its readers that red means nothing
here. That is worse than no guard: it launders a real removal into
background noise.

## Measured both ways

| change to `pnpm-workspace.yaml` | result |
|---|---|
| pin removed | **fails** — `protobufjs range undefined must include an
explicit semver version` |
| pin lowered to `^7.4.0` | **fails** — `range ^7.4.0 is below required
floor 7.5.5` |
| unchanged | passes |

## Second assertion

`package.json` must declare **no** `pnpm.overrides`. If overrides move
again — or come back — that fails and points at the next reader, instead
of letting the floor go silently unchecked. That is precisely the
failure mode #2220 produced, and nothing would otherwise catch a second
occurrence.

## The other six

Left alone deliberately; each needs its own diagnosis and they are
unrelated to one another (I checked — the "seven files, one failure
each" pattern looked like a common cause and is not):

- `workflow-reliability-release-check` — manifest references
`workflow-definition-store.test.ts`, which no longer exists. **Likely a
real signal**: a release check pointing at a deleted seam file covers
nothing. Best next one to pick up.
- `ci-test-shard-timings` — snapshot references missing test files;
affects shard balancing.
- `verify-fast`, `engine-vitest-gate-policy` — validator/canary list
drift.
- `plugin-authoring-docs` — a TOC anchor for a heading containing `&`.
- `release-prompt-gate` — dry-run no longer exits before the proceed
confirmation.

## Verification

`node --test scripts/__tests__/dependency-security-floor.test.mjs` **4
passed** · `pnpm test:gate` 161 + 13 + 487 + 71 · lint · changesets —
green.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Updated security validation to correctly read protobufjs version
overrides from the workspace configuration.
* Added safeguards to detect outdated override locations and help
prevent future security-check regressions.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-31 01:46:56 -07:00

128 lines
5.6 KiB
JavaScript

import assert from "node:assert/strict";
import { readdir, readFile } from "node:fs/promises";
import path from "node:path";
import test from "node:test";
import YAML from "yaml";
const root = process.cwd();
const dependencyFloors = [
{ name: "protobufjs", minimum: "7.5.5", manifestSections: ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"], lockfileMatcher: /^protobufjs@(.*)$/ },
{ name: "vitest", minimum: "4.1.0", manifestSections: ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"], lockfileMatcher: /^vitest@(.*)$/ },
{ name: "@vitest/coverage-v8", minimum: "4.1.0", manifestSections: ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"], lockfileMatcher: /^@vitest\/coverage-v8@(.*)$/ },
];
const manifestRoots = ["packages", "plugins"];
const ignoredPathParts = new Set(["node_modules", "dist", "build", "coverage", ".turbo"]);
function compareVersions(a, b) {
const pa = a.split(".").map((part) => Number.parseInt(part, 10));
const pb = b.split(".").map((part) => Number.parseInt(part, 10));
for (let i = 0; i < Math.max(pa.length, pb.length); i += 1) {
const ai = Number.isFinite(pa[i]) ? pa[i] : 0;
const bi = Number.isFinite(pb[i]) ? pb[i] : 0;
if (ai !== bi) return ai - bi;
}
return 0;
}
function minVersionFromRange(range) {
const version = String(range).match(/\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?/u)?.[0];
return version?.split(/[+-]/u)[0] ?? null;
}
function assertRangeMeetsFloor({ location, name, range, minimum }) {
const minVersion = minVersionFromRange(range);
assert.ok(minVersion, `${location}: ${name} range ${range} must include an explicit semver version`);
assert.ok(compareVersions(minVersion, minimum) >= 0, `${location}: ${name} range ${range} is below required floor ${minimum}`);
}
async function collectPackageManifests(dir) {
const entries = await readdir(dir, { withFileTypes: true });
const manifests = [];
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (ignoredPathParts.has(entry.name)) continue;
if (entry.isDirectory()) {
manifests.push(...(await collectPackageManifests(fullPath)));
} else if (entry.name === "package.json") {
manifests.push(fullPath);
}
}
return manifests;
}
async function sourceManifestPaths() {
const manifests = [path.join(root, "package.json")];
for (const manifestRoot of manifestRoots) {
manifests.push(...(await collectPackageManifests(path.join(root, manifestRoot))));
}
return manifests.sort();
}
test("source manifests keep vulnerable dependency floors out", async () => {
for (const manifestPath of await sourceManifestPaths()) {
const manifest = JSON.parse(await readFile(manifestPath, "utf8"));
const relativePath = path.relative(root, manifestPath);
for (const floor of dependencyFloors) {
for (const section of floor.manifestSections) {
const range = manifest[section]?.[floor.name];
if (range) assertRangeMeetsFloor({ location: `${relativePath} ${section}`, name: floor.name, range, minimum: floor.minimum });
}
}
}
});
/*
FNXC:DependencySecurity 2026-07-31-11:20:
The override moved; the guard did not follow it.
#2220 relocated pnpm overrides from `package.json` to `pnpm-workspace.yaml` for pnpm 11, and this
assertion kept reading `package.json`. It has been failing ever since, on a non-blocking lane, saying
"protobufjs range undefined" — which reads exactly like the security floor was DELETED. It was not:
`pnpm-workspace.yaml` still pins `^7.5.8` and the lockfile resolves 7.6.5.
That is the expensive kind of stale test. A red guard that names a real risk teaches its readers that
red means nothing here, and the next reader cannot tell this from an actual removed pin without
doing the archaeology. Read the file the overrides actually live in, so removing the pin fails again.
*/
test("pnpm overrides pin transitive protobufjs to a safe floor", async () => {
const workspace = YAML.parse(await readFile(path.join(root, "pnpm-workspace.yaml"), "utf8"));
assertRangeMeetsFloor({
location: "pnpm-workspace.yaml overrides",
name: "protobufjs",
range: workspace?.overrides?.protobufjs,
minimum: "7.5.5",
});
});
test("the protobufjs floor is asserted where overrides actually live", async () => {
/*
The guard above is only meaningful while `pnpm-workspace.yaml` is the overrides home. If they move
again, this fails and points at the next reader rather than letting the floor go unchecked in
silence — the failure mode #2220 produced.
*/
const rootManifest = JSON.parse(await readFile(path.join(root, "package.json"), "utf8"));
assert.equal(
Object.keys(rootManifest.pnpm?.overrides ?? {}).length,
0,
"package.json declares pnpm.overrides again — point the floor assertion above at it, or at both",
);
});
test("lockfile resolutions satisfy dependency security floors", async () => {
const lockfile = YAML.parse(await readFile(path.join(root, "pnpm-lock.yaml"), "utf8"));
const packageKeys = Object.keys(lockfile.packages ?? {});
for (const floor of dependencyFloors) {
const matches = packageKeys
.map((key) => key.replace(/^\//u, ""))
.map((key) => key.match(floor.lockfileMatcher)?.[1])
.filter(Boolean)
.map((suffix) => suffix.split("(")[0]);
assert.ok(matches.length > 0, `pnpm-lock.yaml must include at least one ${floor.name} resolution`);
for (const version of matches) {
assert.ok(compareVersions(version, floor.minimum) >= 0, `pnpm-lock.yaml resolves ${floor.name}@${version}, below required floor ${floor.minimum}`);
}
}
});