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

@@ -98,7 +98,7 @@
"@types/node": "^22.0.0",
"@types/react": "^19.2.0",
"@types/ws": "^8.5.0",
"@vitest/coverage-v8": "^3.1.0",
"@vitest/coverage-v8": "^4.1.0",
"cross-env": "^7.0.0",
"esbuild": "^0.25.12",
"ink-testing-library": "^4.0.0",
@@ -106,7 +106,7 @@
"tsx": "^4.19.0",
"typebox": "^1.0.0",
"typescript": "^5.7.0",
"vitest": "^3.1.0",
"vitest": "^4.1.0",
"yaml": "^2.8.3"
},
"repository": {

View File

@@ -52,7 +52,7 @@ export default defineConfig({
globalSetup: [resolve(__dirname, "../core/src/__test-utils__/vitest-teardown.ts")],
pool: "forks",
maxWorkers,
poolOptions: { forks: { minForks: 1, maxForks: maxWorkers } },
minWorkers: 1,
fileParallelism: true,
coverage: {
enabled: false,

View File

@@ -40,9 +40,9 @@
"devDependencies": {
"@types/dockerode": "^3.3.41",
"@types/node": "^25.5.0",
"@vitest/coverage-v8": "^3.1.0",
"@vitest/coverage-v8": "^4.1.0",
"typescript": "^5.7.0",
"vitest": "^3.1.0"
"vitest": "^4.1.0"
},
"engines": {
"node": ">=22.5.0"

View File

@@ -28,7 +28,7 @@ export default defineConfig({
// and cannot safely share mutable process state under worker_threads.
pool: "forks",
maxWorkers,
poolOptions: { forks: { minForks: 1, maxForks: maxWorkers } },
minWorkers: 1,
fileParallelism: true,
// Core runs a large SQLite-heavy suite while other workspace packages test concurrently.
// Use a slightly higher timeout to reduce nondeterministic slow-machine flakes.

View File

@@ -24,7 +24,7 @@ function getFallback(name: string): AnyFn {
return fallbackFns.get(name)!;
}
export const dashboardApiMocks = {
export const dashboardApiMocks: Record<string, AnyFn> = {
fetchTasks: vi.fn(async () => []),
fetchSettings: vi.fn(async () => ({})),
updateSettings: vi.fn(async () => ({})),
@@ -34,7 +34,7 @@ export const dashboardApiMocks = {
fetchTaskDetail: vi.fn(),
fetchTaskReview: vi.fn(),
fetchUnreadCount: vi.fn(async () => ({ unreadCount: 0 })),
} satisfies Record<string, AnyFn>;
};
export async function createDashboardApiMock(
importActual: () => Promise<AnyModule>,

View File

@@ -147,12 +147,12 @@
"@types/react-dom": "^19.0.0",
"@types/ws": "^8.5.0",
"@vitejs/plugin-react": "^4.3.0",
"@vitest/coverage-v8": "^3.1.0",
"@vitest/coverage-v8": "^4.1.0",
"jsdom": "^29.0.1",
"tinyrainbow": "^2.0.0",
"typescript": "^5.7.0",
"vite": "^6.0.0",
"vitest": "^3.1.0"
"vitest": "^4.1.0"
},
"private": true
}

View File

@@ -316,7 +316,7 @@ export default defineConfig({
// React suites; forks duplicated the entire renderer per worker (~500MB).
pool: "threads",
maxWorkers,
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers } },
minWorkers: 1,
fileParallelism: true,
isolate: true,
// Dashboard route and integration-heavy suites can exceed the Vitest

View File

@@ -46,7 +46,7 @@
"@types/node": "^22.0.0",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@vitest/coverage-v8": "^3.1.0",
"@vitest/coverage-v8": "^4.1.0",
"electron": "^35.0.0",
"electron-builder": "^26.0.0",
"esbuild": "^0.25.12",
@@ -57,6 +57,6 @@
"tsx": "^4.19.0",
"typescript": "^5.7.0",
"vite": "^6.0.0",
"vitest": "^3.1.0"
"vitest": "^4.1.0"
}
}

View File

@@ -20,7 +20,7 @@ export default defineConfig({
hookTimeout: 30_000,
pool: "threads",
maxWorkers,
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers } },
minWorkers: 1,
fileParallelism: true,
passWithNoTests: true,
projects: [

View File

@@ -31,7 +31,7 @@
"devDependencies": {
"@types/node": "^22.0.0",
"typescript": "^5.7.0",
"vitest": "^3.0.0"
"vitest": "^4.1.0"
},
"scripts": {
"test": "vitest run --reporter=dot",

View File

@@ -14,7 +14,7 @@ export default defineConfig({
globalSetup: [resolve(__dirname, "../core/src/__test-utils__/vitest-teardown.ts")],
pool: "forks",
maxWorkers,
poolOptions: { forks: { minForks: 1, maxForks: maxWorkers } },
minWorkers: 1,
fileParallelism: true,
coverage: {
provider: "v8",

View File

@@ -52,9 +52,9 @@
"@modelcontextprotocol/sdk": "^1.0.0",
"@types/node": "^25.5.0",
"@types/proper-lockfile": "^4.1.4",
"@vitest/coverage-v8": "^3.1.0",
"@vitest/coverage-v8": "^4.1.0",
"typescript": "^5.7.0",
"vitest": "^3.1.0"
"vitest": "^4.1.0"
},
"private": true
}

View File

@@ -1,4 +1,5 @@
import { vi } from "vitest";
import type { Mock } from "vitest";
import { installTaskWorktreeIdentityGuard } from "../worktree-hooks.js";
// Mock external dependencies
@@ -220,16 +221,18 @@ vi.mock("node:fs", () => ({
realpathSync: vi.fn((path: string) => path),
}));
export const mockExecuteAll = vi.fn().mockResolvedValue([]);
export const mockTerminateAllSessions = vi.fn().mockResolvedValue(undefined);
export const mockCleanup = vi.fn().mockResolvedValue(undefined);
export const mockExecuteAll: Mock<() => Promise<unknown[]>> = vi.fn().mockResolvedValue([]);
export const mockTerminateAllSessions: Mock<() => Promise<void>> = vi.fn().mockResolvedValue(undefined);
export const mockCleanup: Mock<() => Promise<void>> = vi.fn().mockResolvedValue(undefined);
vi.mock("../step-session-executor.js", () => ({
StepSessionExecutor: vi.fn().mockImplementation(() => ({
executeAll: mockExecuteAll,
terminateAllSessions: mockTerminateAllSessions,
cleanup: mockCleanup,
})),
StepSessionExecutor: vi.fn().mockImplementation(function () {
return {
executeAll: mockExecuteAll,
terminateAllSessions: mockTerminateAllSessions,
cleanup: mockCleanup,
};
}),
}));
vi.mock("../rate-limit-retry.js", () => ({
@@ -257,10 +260,12 @@ vi.mock("@earendil-works/pi-coding-agent", () => {
open: vi.fn().mockReturnValue(mockSessionManager),
inMemory: vi.fn().mockReturnValue(mockSessionManager),
},
ModelRegistry: vi.fn().mockImplementation(() => ({
find: vi.fn(),
refresh: vi.fn(),
})),
ModelRegistry: vi.fn().mockImplementation(function () {
return {
find: vi.fn(),
refresh: vi.fn(),
};
}),
AuthStorage: {
create: vi.fn().mockReturnValue({}),
},

View File

@@ -1,6 +1,7 @@
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import type { Settings, Task, TaskStore } from "@fusion/core";
import { GridlockDetector } from "../gridlock-detector.js";
import type { GridlockEvent } from "../gridlock-detector.js";
function createTask(id: string, overrides: Partial<Task> = {}): Task {
return {
@@ -34,8 +35,8 @@ describe("GridlockDetector", () => {
let tasks: Task[];
let settings: Settings;
let scopes: Record<string, string[]>;
let onGridlock: ReturnType<typeof vi.fn>;
let onGridlockCleared: ReturnType<typeof vi.fn>;
let onGridlock: ReturnType<typeof vi.fn<(event: GridlockEvent) => void>>;
let onGridlockCleared: ReturnType<typeof vi.fn<() => void>>;
let store: TaskStore;
let detector: GridlockDetector;

View File

@@ -4,6 +4,7 @@ import {
HeartbeatMonitor,
HeartbeatTriggerScheduler,
} from "../agent-heartbeat.js";
import type { TriggerCallback } from "../agent-heartbeat.js";
import type { AgentStore, TaskStore, Agent } from "@fusion/core";
import { createBudgetStatus } from "./heartbeat-test-helpers.js";
vi.mock("../logger.js", async () => {
@@ -18,7 +19,7 @@ import { heartbeatLog } from "../logger.js";
describe("HeartbeatTriggerScheduler", () => {
let store: AgentStore;
let callback: ReturnType<typeof vi.fn>;
let callback: ReturnType<typeof vi.fn<TriggerCallback>>;
let scheduler: import("../agent-heartbeat.js").HeartbeatTriggerScheduler;
beforeEach(() => {

View File

@@ -52,21 +52,27 @@ vi.mock("../logger.js", () => ({
}));
vi.mock("../stale-task-reporter.js", () => ({
StaleTaskReporter: vi.fn().mockImplementation(() => ({
report: staleReporterReportMock,
})),
StaleTaskReporter: vi.fn().mockImplementation(function () {
return {
report: staleReporterReportMock,
};
}),
}));
vi.mock("../backlog-pressure-reporter.js", () => ({
BacklogPressureReporter: vi.fn().mockImplementation(() => ({
report: backlogPressureReporterReportMock,
})),
BacklogPressureReporter: vi.fn().mockImplementation(function () {
return {
report: backlogPressureReporterReportMock,
};
}),
}));
vi.mock("../unlinked-missions-advisory-reporter.js", () => ({
UnlinkedMissionsAdvisoryReporter: vi.fn().mockImplementation(() => ({
report: unlinkedMissionsAdvisoryReporterReportMock,
})),
UnlinkedMissionsAdvisoryReporter: vi.fn().mockImplementation(function () {
return {
report: unlinkedMissionsAdvisoryReporterReportMock,
};
}),
}));
// Helper to create mock tasks

View File

@@ -7891,6 +7891,11 @@ describe("SelfHealingManager reclaimSelfOwnedBranchConflicts", () => {
mockedIsUsableTaskWorktree.mockResolvedValue(true);
});
afterEach(() => {
manager.stop();
vi.restoreAllMocks();
});
it("reclaims fully-subsumed branch conflicts and emits subsumed audit metadata", async () => {
store = createMockStore({
getSettings: vi.fn().mockResolvedValue({ globalPause: false, enginePaused: false } as any),

View File

@@ -9,5 +9,6 @@
"node-pty": ["./src/types/node-pty/index.d.ts"]
}
},
"include": ["src/**/*"]
"include": ["src/**/*"],
"exclude": ["src/__tests__/**/*"]
}

View File

@@ -21,7 +21,7 @@ export default defineConfig({
globalSetup: [resolve(__dirname, "../core/src/__test-utils__/vitest-teardown.ts")],
pool: "threads",
maxWorkers,
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers } },
minWorkers: 1,
fileParallelism: true,
// Enable isolate to allow parallel execution of tests with conflicting mocks
isolate: true,
@@ -117,7 +117,9 @@ export default defineConfig({
// SQLite rowid interleaving (e.g. FN-5521 hit
// `expected 24 to be less than 19` in merge-reuse-task-worktree).
// Serialize at the file level; within-file order is already linear.
poolOptions: { threads: { singleThread: true } },
minWorkers: 1,
maxWorkers: 1,
fileParallelism: false,
},
},
{
@@ -130,7 +132,9 @@ export default defineConfig({
// and inflating wall time further. Excluded from the default
// `pnpm test` lane; run via `pnpm test:slow` / `pnpm test:all`.
include: ["src/**/*.slow.test.ts"],
poolOptions: { threads: { singleThread: true } },
minWorkers: 1,
maxWorkers: 1,
fileParallelism: false,
},
},
],

View File

@@ -28,7 +28,7 @@
"devDependencies": {
"@types/node": "^25.5.0",
"typescript": "^5.7.0",
"vitest": "^3.1.0"
"vitest": "^4.1.0"
},
"engines": {
"node": ">=22.5.0"

View File

@@ -1,5 +1,8 @@
import { resolve } from "node:path";
import { defineConfig } from "vitest/config";
import { computeMaxWorkers } from "../core/src/__test-utils__/vitest-workers";
const maxWorkers = computeMaxWorkers();
export default defineConfig({
resolve: {
@@ -10,5 +13,9 @@ export default defineConfig({
},
test: {
include: ["src/**/*.test.ts"],
setupFiles: ["../core/src/__test-utils__/vitest-setup.ts"],
globalSetup: ["../core/src/__test-utils__/vitest-teardown.ts"],
maxWorkers,
minWorkers: 1,
},
});

View File

@@ -36,6 +36,6 @@
"@types/node": "^22.0.0",
"tsx": "^4.19.0",
"typescript": "^5.7.0",
"vitest": "^3.1.0"
"vitest": "^4.1.0"
}
}

View File

@@ -11,7 +11,7 @@ export default defineConfig({
globalSetup: [resolve(__dirname, "../core/src/__test-utils__/vitest-teardown.ts")],
pool: "forks",
maxWorkers,
poolOptions: { forks: { minForks: 1, maxForks: maxWorkers } },
minWorkers: 1,
fileParallelism: true,
},
});

View File

@@ -26,7 +26,7 @@
"devDependencies": {
"@types/node": "^22.0.0",
"typescript": "^5.7.0",
"vitest": "^3.0.0"
"vitest": "^4.1.0"
},
"scripts": {
"test": "vitest run --reporter=dot",

View File

@@ -14,7 +14,7 @@ export default defineConfig({
globalSetup: [resolve(__dirname, "../core/src/__test-utils__/vitest-teardown.ts")],
pool: "forks",
maxWorkers,
poolOptions: { forks: { minForks: 1, maxForks: maxWorkers } },
minWorkers: 1,
fileParallelism: true,
coverage: {
provider: "v8",

View File

@@ -27,7 +27,7 @@
"devDependencies": {
"@types/node": "^22.0.0",
"typescript": "^5.7.0",
"vitest": "^3.0.0"
"vitest": "^4.1.0"
},
"scripts": {
"test": "vitest run --reporter=dot",

View File

@@ -11,7 +11,7 @@ export default defineConfig({
globalSetup: [resolve(__dirname, "../core/src/__test-utils__/vitest-teardown.ts")],
pool: "forks",
maxWorkers,
poolOptions: { forks: { minForks: 1, maxForks: maxWorkers } },
minWorkers: 1,
fileParallelism: true,
},
});

View File

@@ -32,7 +32,7 @@
"devDependencies": {
"@types/node": "^25.5.0",
"typescript": "^5.7.0",
"vitest": "^3.1.0"
"vitest": "^4.1.0"
},
"dependencies": {
"@fusion/core": "workspace:*"

View File

@@ -17,6 +17,6 @@ export default defineConfig({
globalSetup: [resolve(__dirname, "../core/src/__test-utils__/vitest-teardown.ts")],
pool: "threads",
maxWorkers,
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers } },
minWorkers: 1,
},
});