FN-6042: raise dependency security floors

Harden dependency floors and update the Vitest toolchain to patched releases.

- upgrade workspace vitest and @vitest/coverage-v8 dependencies to the 4.1 line across packages and plugins
- pin transitive protobufjs via pnpm overrides and lockfile updates to patched versions
- adapt Vitest configs, engine test helpers, and security-floor coverage for the new dependency baselines
- add the published CLI changeset and related workspace/package metadata updates included in the task branch

Files changed:
 .changeset/fn-6042-security-dependencies.md        |   5 +
 AGENTS.md                                          |   4 +
 Dockerfile                                         |   3 +
 docs/PLUGIN_AUTHORING.md                           |   2 -
 package.json                                       |   3 +-
 packages/cli/package.json                          |   4 +-
 packages/cli/vitest.config.ts                      |   2 +-
 packages/core/package.json                         |   4 +-
 packages/core/vitest.config.ts                     |   2 +-
 packages/dashboard/app/test/mockApi.ts             |   4 +-
 packages/dashboard/package.json                    |   4 +-
 packages/dashboard/vitest.config.ts                |   2 +-
 packages/desktop/package.json                      |   4 +-
 packages/desktop/vitest.config.ts                  |   2 +-
 packages/droid-cli/package.json                    |   2 +-
 packages/droid-cli/vitest.config.ts                |   2 +-
 packages/engine/package.json                       |   4 +-
 .../engine/src/__tests__/executor-test-helpers.ts  |  29 +-
 .../engine/src/__tests__/gridlock-detector.test.ts |   5 +-
 .../src/__tests__/heartbeat-scheduler.test.ts      |   3 +-
 packages/engine/src/__tests__/scheduler.test.ts    |  24 +-
 packages/engine/src/__tests__/self-healing.test.ts |   5 +
 packages/engine/tsconfig.json                      |   3 +-
 packages/engine/vitest.config.ts                   |  10 +-
 packages/i18n/package.json                         |   2 +-
 packages/i18n/vitest.config.ts                     |   7 +
 packages/mobile/package.json                       |   2 +-
 packages/mobile/vitest.config.ts                   |   2 +-
 packages/pi-claude-cli/package.json                |   2 +-
 packages/pi-claude-cli/vitest.config.ts            |   2 +-
 packages/pi-llama-cpp/package.json                 |   2 +-
 packages/pi-llama-cpp/vitest.config.ts             |   2 +-
 packages/plugin-sdk/package.json                   |   2 +-
 packages/plugin-sdk/vitest.config.ts               |   2 +-
 .../examples/fusion-plugin-auto-label/package.json |   2 +-
 .../fusion-plugin-auto-label/vitest.config.ts      |   2 +-
 .../examples/fusion-plugin-ci-status/package.json  |   2 +-
 .../fusion-plugin-ci-status/vitest.config.ts       |   2 +-
 .../fusion-plugin-notification/package.json        |   2 +-
 .../fusion-plugin-notification/vitest.config.ts    |   2 +-
 .../fusion-plugin-settings-demo/package.json       |   2 +-
 .../fusion-plugin-settings-demo/vitest.config.ts   |   2 +-
 plugins/fusion-plugin-acp-runtime/package.json     |   2 +-
 plugins/fusion-plugin-acp-runtime/vitest.config.ts |   2 +-
 plugins/fusion-plugin-agent-browser/package.json   |   2 +-
 .../fusion-plugin-agent-browser/vitest.config.ts   |   2 +-
 .../fusion-plugin-cli-printing-press/package.json  |   2 +-
 .../vitest.config.ts                               |   2 +-
 .../package.json                                   |   2 +-
 .../src/__tests__/orchestrator-live-output.test.ts |   4 +-
 .../vitest.config.ts                               |   2 +-
 plugins/fusion-plugin-cursor-runtime/package.json  |   4 +-
 .../fusion-plugin-dependency-graph/package.json    |   4 +-
 .../vitest.config.ts                               |   2 +-
 plugins/fusion-plugin-droid-runtime/package.json   |   2 +-
 .../fusion-plugin-droid-runtime/vitest.config.ts   |   2 +-
 plugins/fusion-plugin-even-cards/package.json      |   2 +-
 plugins/fusion-plugin-even-cards/vitest.config.ts  |   2 +-
 .../package.json                                   |   2 +-
 .../vitest.config.ts                               |   2 +-
 plugins/fusion-plugin-hermes-runtime/package.json  |   2 +-
 .../fusion-plugin-hermes-runtime/vitest.config.ts  |   2 +-
 .../fusion-plugin-openclaw-runtime/package.json    |   2 +-
 .../vitest.config.ts                               |   2 +-
 .../fusion-plugin-paperclip-runtime/package.json   |   2 +-
 .../vitest.config.ts                               |   2 +-
 plugins/fusion-plugin-reports/package.json         |   2 +-
 .../src/__tests__/review-panel.test.ts             |   6 +-
 plugins/fusion-plugin-reports/vitest.config.ts     |   2 +-
 plugins/fusion-plugin-roadmap/package.json         |   2 +-
 plugins/fusion-plugin-roadmap/vitest.config.ts     |   2 +-
 plugins/fusion-plugin-whatsapp-chat/package.json   |   2 +-
 .../fusion-plugin-whatsapp-chat/vitest.config.ts   |   2 +-
 pnpm-lock.yaml                                     | 626 ++++++++-------------
 .../__tests__/dependency-security-floor.test.mjs   |  95 ++++
 75 files changed, 475 insertions(+), 491 deletions(-)

Fusion-Task-Id: FN-6042

Fusion-Task-Lineage: fff6a1cb-8937-435c-9a91-b7c7a59cc80e
This commit is contained in:
gsxdsm
2026-06-08 15:19:27 -07:00
parent 558720b4e5
commit 8bc3d7b0a5
75 changed files with 475 additions and 491 deletions

View File

@@ -0,0 +1,95 @@
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 });
}
}
}
});
test("pnpm overrides pin transitive protobufjs to a safe floor", async () => {
const manifest = JSON.parse(await readFile(path.join(root, "package.json"), "utf8"));
assertRangeMeetsFloor({ location: "package.json pnpm.overrides", name: "protobufjs", range: manifest.pnpm?.overrides?.protobufjs, minimum: "7.5.5" });
});
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}`);
}
}
});