Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions docs/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,10 +173,10 @@ version to the same advisory endpoint. This matters for the private workflow
controller's pinned npm: pnpm otherwise reports npm as one opaque package and
omits the packages npm ships inside itself. The lockfile-bound npm patch bundles
`brace-expansion` 5.0.12, `ip-address` 10.5.0, `tar` 7.5.22, and `undici`
6.28.1. The `ip-address` and `tar` versions mirror the green npm v11 upstream
6.29.1. The `ip-address` and `tar` versions mirror the green npm v11 upstream
fixes in `npm/cli#9842` and `npm/cli#9872`. No npm release yet bundles the
`brace-expansion` and `undici` fixes for GHSA-6j4f-fj2g-mc7p,
GHSA-qhr7-859c-m2p7, and GHSA-rfgv-xxqx-mfg5, so the patch carries those
GHSA-qhr7-859c-m2p7, GHSA-rfgv-xxqx-mfg5, and GHSA-xjj6-f33q-929c, so the patch carries those
registry releases, limited to the files npm's own bundling keeps.
The patch can be removed when an upstream npm release carries those versions;
the operator npm closure digest and advisory inventory both fail if that
Expand All @@ -187,6 +187,11 @@ that no such shim directory is copied. Socket's duplicate obfuscated-code
warnings refer to npm's official bundled/minified distribution; no warning is
suppressed, and registry integrity plus the closure digest cover those bytes.

The controller's generated `.smithers/package.json` is installed separately by
npm, so it also pins the fixed MCP SDK, body-parser, DOMPurify, and Zod versions.
Its fixed resolution cutoff follows those releases. Non-force init upgrades the
exact prior generated manifest to the new pins while preserving project runs.

## Agent process environment

The workflow controller receives only the active agents' configured API-key
Expand Down
2 changes: 1 addition & 1 deletion packages/artifacts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"@ultrafuzz/security": "workspace:*",
"ajv": "^8.20.0",
"ajv-formats": "^3.0.1",
"zod": "^4.4.3"
"zod": "^4.6.6"
},
"homepage": "https://github.com/monad-developers/ultrafuzz#readme",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion packages/config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"@ultrafuzz/security": "workspace:*",
"smol-toml": "^1.9.0",
"yaml": "^2.8.0",
"zod": "^4.4.3"
"zod": "^4.6.6"
},
"homepage": "https://github.com/monad-developers/ultrafuzz#readme",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion packages/evals/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@
"@ultrafuzz/config": "workspace:*",
"@ultrafuzz/runtime": "workspace:*",
"yaml": "^2.8.0",
"zod": "^4.4.3"
"zod": "^4.6.6"
}
}
2 changes: 1 addition & 1 deletion packages/evmbench/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"dependencies": {
"@ultrafuzz/artifacts": "workspace:*",
"yaml": "^2.8.0",
"zod": "^4.4.3"
"zod": "^4.6.6"
},
"homepage": "https://github.com/monad-developers/ultrafuzz#readme",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion packages/modal/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"proper-lockfile": "4.1.2",
"smol-toml": "^1.9.0",
"yaml": "^2.9.0",
"zod": "^4.4.3"
"zod": "^4.6.6"
},
"devDependencies": {
"@types/proper-lockfile": "^4.1.4"
Expand Down
2 changes: 1 addition & 1 deletion packages/runtime/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"proper-lockfile": "4.1.2",
"smthrs": "0.35.0",
"typescript": "6.0.3",
"zod": "4.4.3"
"zod": "4.6.6"
},
"devDependencies": {
"@moonshot-ai/kimi-code": "0.29.1",
Expand Down
2 changes: 1 addition & 1 deletion packages/runtime/src/operator-npm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { createRequire } from "node:module";
import path from "node:path";

const OPERATOR_NPM_VERSION = "11.19.0";
const OPERATOR_NPM_CLOSURE_SHA256 = "4852a49e930ed235c056f19f9cfa6214f5d84dd4a0942f9403714e4603ef13e6";
const OPERATOR_NPM_CLOSURE_SHA256 = "59715f52d005bdb07d45b5e52544af33ac198c0293e9bd9af837d75109b1fa1a";
const OPERATOR_NPM_MAX_FILES = 10_000;
const OPERATOR_NPM_MAX_DIRECTORIES = 2_500;
const OPERATOR_NPM_MAX_BYTES = 64 * 1024 * 1024;
Expand Down
81 changes: 51 additions & 30 deletions packages/runtime/src/smithers-package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,11 @@ export const KIMI_CODE_VERSION = "0.29.1";
// manifest does not preserve the Ultrafuzz run ID or the Smithers run ID the
// durable run is keyed on.
//
// A listed version only migrates when the rest of the manifest still matches the
// current generated document exactly (see `migrateGeneratedSmithersManifest`),
// so the list is fail-safe under drift: if a future bump also moves Kimi, Zod,
// TypeScript or the Effect override family, manifests from that release stop
// matching and are rejected rather than silently rewritten to a shape they were
// never installed against. That makes a stale entry inert, not dangerous --
// but it also means an entry is only useful while its release differed from the
// current one in `smthrs` alone, which is true of 0.34.0 -> 0.35.0.
// A listed version only migrates when the rest of the manifest still matches
// the recorded generated document exactly (see
// `migrateGeneratedSmithersManifest`), so the list is fail-safe under drift.
// The 0.34.0 and pre-security 0.35.0 documents shared the old Zod and Effect
// pins; both are recognized explicitly and migrate to today's security pins.
//
// Only versions this repository actually pinned belong here. `0.34.0` is the
// sole `smthrs` pin ever committed (the release before it pinned
Expand All @@ -53,12 +50,15 @@ const SMITHERS_EFFECT_PACKAGE_NAMES = [
"@effect/sql-sqlite-bun"
] as const;

export const REQUIRED_SMITHERS_OVERRIDES: Readonly<Record<string, string>> = {
const LEGACY_SMITHERS_OVERRIDES = {
effect: SMITHERS_EFFECT_VERSION,
...Object.fromEntries(SMITHERS_EFFECT_PACKAGE_NAMES.map((name) => [name, SMITHERS_EFFECT_VERSION]))
};

const REQUIRED_SMITHERS_DEPENDENCIES = {
// These were the exact generated pins before the October dependency fixes. A
// project can carry this manifest across upgrades, so keep its shape available
// solely for authenticated migration to the current document.
const LEGACY_GENERATED_SMITHERS_DEPENDENCIES = {
dependencies: {
"@moonshot-ai/kimi-code": KIMI_CODE_VERSION,
smthrs: SMITHERS_VERSION,
Expand All @@ -67,6 +67,23 @@ const REQUIRED_SMITHERS_DEPENDENCIES = {
devDependencies: {
typescript: "6.0.3"
},
overrides: LEGACY_SMITHERS_OVERRIDES
} as const;

export const REQUIRED_SMITHERS_OVERRIDES: Readonly<Record<string, string>> = {
...LEGACY_SMITHERS_OVERRIDES,
"@modelcontextprotocol/sdk": "1.32.0",
"body-parser": "2.4.0",
dompurify: "3.4.17",
zod: "4.6.6"
};

const REQUIRED_SMITHERS_DEPENDENCIES = {
...LEGACY_GENERATED_SMITHERS_DEPENDENCIES,
dependencies: {
...LEGACY_GENERATED_SMITHERS_DEPENDENCIES.dependencies,
zod: "4.6.6"
},
overrides: REQUIRED_SMITHERS_OVERRIDES
} as const;

Expand Down Expand Up @@ -95,11 +112,14 @@ const SMITHERS_PIN_PUBLISH_TIMES: Readonly<Record<string, string>> = {
"@effect/platform-bun@4.0.0-beta.105": "2026-08-07T01:13:10.227Z",
"@effect/platform-node-shared@4.0.0-beta.105": "2026-08-07T01:16:00.798Z",
"@effect/sql-sqlite-bun@4.0.0-beta.105": "2026-08-07T01:13:00.629Z",
"@modelcontextprotocol/sdk@1.32.0": "2026-10-02T17:32:51.427Z",
"@moonshot-ai/kimi-code@0.29.1": "2026-07-24T05:27:08.545Z",
"body-parser@2.4.0": "2026-10-22T11:39:38.614Z",
"dompurify@3.4.17": "2026-10-12T12:41:28.880Z",
"effect@4.0.0-beta.105": "2026-08-07T01:37:58.225Z",
"smthrs@0.35.0": "2026-08-17T20:35:37.787Z",
"typescript@6.0.3": "2026-04-16T23:38:27.905Z",
"zod@4.4.3": "2026-05-04T07:06:40.819Z"
"zod@4.6.6": "2026-10-11T16:18:23.878Z"
};

// The pin list above closes the hazard one package at a time, and only for
Expand All @@ -126,11 +146,9 @@ const SMITHERS_PIN_PUBLISH_TIMES: Readonly<Record<string, string>> = {
// runs -- npm does not freeze a future `--before` view. The cutoff also decides
// which transitive releases launch installs, so it is also what moves a security
// fix in an open range into the launched engine; `pnpm-lock.yaml` does not reach
// this install. This instant is about ten hours after `source-map-js@1.2.2`
// (2026-09-30T14:08:09Z), the newest of the releases that fix the October 2026
// advisories in `proxy-addr`, `@vue/server-renderer`, `source-map-js` and
// `prosemirror-view`, and well after every pin.
export const SMITHERS_DEPENDENCY_RESOLUTION_CUTOFF = "2026-10-01T00:00:00Z";
// this install. The current instant is more than two days after the newest
// pinned security release, body-parser 2.4.0, reached the registry.
export const SMITHERS_DEPENDENCY_RESOLUTION_CUTOFF = "2026-10-25T00:00:00Z";

/**
* Fails when a pinned version has no recorded publish instant, or when the
Expand Down Expand Up @@ -223,17 +241,16 @@ export function renderSmithersPackageJson(): string {
* replacement text, or `undefined` when the manifest is not one this runtime is
* willing to rewrite on the project's behalf.
*
* Two shapes migrate, and nothing else does.
* Three shapes migrate, and nothing else does.
*
* 1. The frozen 0.32 shape (`smithers-orchestrator@0.32.0` plus the
* `4.0.0-beta.102` override family). Extra packages in the three dependency
* extension maps are preserved and only the generated pins are rewritten.
* 2. A manifest identical to `renderSmithersPackageJson()` apart from
* `dependencies.smthrs`, which must name a version in
* `MIGRATABLE_PRIOR_SMITHERS_VERSIONS`. This one is strict on purpose: it
* rewrites nothing it did not itself generate, so a hand-edited or corrupted
* manifest is still rejected and surfaces as a launch failure the operator
* resolves deliberately, rather than being quietly overwritten.
* 2. The exact previously generated `smthrs@0.35.0` manifest, before the
* security pins and cutoff moved forward.
* 3. That same generated shape with `dependencies.smthrs` at a version in
* `MIGRATABLE_PRIOR_SMITHERS_VERSIONS`. Both are strict on purpose: hand
* edits or corruption are rejected instead of silently overwritten.
*
* The name is historical -- it predates case 2 -- and is kept so the three call
* sites and the durable behaviour they encode stay put across the pin bump.
Expand All @@ -243,11 +260,11 @@ export function migrateStockSmithers032PackageManifest(value: unknown): string |
}

/**
* Case 2 above. Accepts only the exact generated document for a superseded pin:
* Cases 2 and 3 above. Accepts only the exact old generated document:
* the same six top-level keys, the same `name`/`private`/`type`, and dependency
* sections whose key sets and values match the current ones entry for entry,
* with `smthrs` at the prior version. Any extra dependency, any missing one, any
* other version drift and any override outside `REQUIRED_SMITHERS_OVERRIDES`
* sections whose key sets and values match the recorded legacy ones entry for entry,
* with `smthrs` at the current or recorded prior version. Any extra dependency,
* any missing one, any other version drift and any override outside the legacy set
* rejects. The replacement is the canonical current document, so a manifest that
* only reordered keys is normalised rather than refused.
*/
Expand All @@ -256,7 +273,10 @@ function migrateGeneratedSmithersManifest(value: unknown): string | undefined {
const dependencies = value.dependencies;
if (!isRecord(dependencies)) return undefined;
const pinned = dependencies[SMITHERS_PACKAGE_NAME];
if (typeof pinned !== "string" || !MIGRATABLE_PRIOR_SMITHERS_VERSIONS.some((prior) => prior === pinned)) {
if (
typeof pinned !== "string" ||
(pinned !== SMITHERS_VERSION && !MIGRATABLE_PRIOR_SMITHERS_VERSIONS.some((prior) => prior === pinned))
) {
return undefined;
}
return matchesGeneratedSmithersManifest(value, pinned) ? renderSmithersPackageJson() : undefined;
Expand All @@ -265,9 +285,9 @@ function migrateGeneratedSmithersManifest(value: unknown): string | undefined {
function matchesGeneratedSmithersManifest(value: Record<string, unknown>, priorSmithersVersion: string): boolean {
const expected: Record<string, unknown> = {
...GENERATED_SMITHERS_MANIFEST_IDENTITY,
...REQUIRED_SMITHERS_DEPENDENCIES,
...LEGACY_GENERATED_SMITHERS_DEPENDENCIES,
dependencies: {
...REQUIRED_SMITHERS_DEPENDENCIES.dependencies,
...LEGACY_GENERATED_SMITHERS_DEPENDENCIES.dependencies,
[SMITHERS_PACKAGE_NAME]: priorSmithersVersion
}
};
Expand Down Expand Up @@ -311,6 +331,7 @@ function migrateStock032SmithersManifest(value: unknown): string | undefined {
}
delete dependencies["smithers-orchestrator"];
dependencies[SMITHERS_PACKAGE_NAME] = SMITHERS_VERSION;
dependencies.zod = REQUIRED_SMITHERS_DEPENDENCIES.dependencies.zod;
const overrides = migratedSections.overrides!;
for (const [name, version] of Object.entries(REQUIRED_SMITHERS_OVERRIDES)) overrides[name] = version;
return `${JSON.stringify(
Expand Down
2 changes: 1 addition & 1 deletion packages/runtime/test/fake-npm-installer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export function writeFakeNpmInstaller(
["@smthrs/tool-context", SMITHERS_VERSION],
["react", "19.2.4"],
["smthrs", SMITHERS_VERSION],
["zod", "4.4.3"]
["zod", "4.6.6"]
];
fs.writeFileSync(
npm,
Expand Down
6 changes: 3 additions & 3 deletions packages/runtime/test/operator-npm.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import { bundledOperatorNpmClosureForValidation, resolveOperatorNpmAuthority } f

test("the release-pinned operator npm closure is complete and reproducible", () => {
assert.deepEqual(bundledOperatorNpmClosureForValidation(), {
bytes: 12_230_966,
digest: "4852a49e930ed235c056f19f9cfa6214f5d84dd4a0942f9403714e4603ef13e6",
bytes: 12_239_079,
digest: "59715f52d005bdb07d45b5e52544af33ac198c0293e9bd9af837d75109b1fa1a",
directories: 460,
files: 1_943
});
Expand All @@ -31,7 +31,7 @@ test("the operator npm backports retain their security behavior", () => {
"brace-expansion": "5.0.12",
"ip-address": "10.5.0",
tar: "7.5.22",
undici: "6.28.1"
undici: "6.29.1"
}
);
const braceExpansion = require(path.join(npmRoot, "node_modules", "brace-expansion")) as {
Expand Down
44 changes: 32 additions & 12 deletions packages/runtime/test/runtime.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1808,7 +1808,7 @@ function writeFakeInstalledSmithersDependencies(project: string): void {
["@moonshot-ai/kimi-code", KIMI_CODE_VERSION],
["@smthrs/tool-context", SMITHERS_VERSION],
["react", "19.2.4"],
["zod", "4.4.3"]
["zod", "4.6.6"]
] as const;
for (const [name, version] of dependencies) {
writeFakeInstalledSmithersDependency(project, name, version);
Expand Down Expand Up @@ -3638,10 +3638,7 @@ test("non-force init migrates a superseded generated manifest so the project kee
const project = tempProject();
assert.equal(initProject({ projectRoot: project, force: true }).ok, true);
const manifestPath = path.join(project, ".smithers", "package.json");
const current = JSON.parse(renderSmithersPackageJson()) as {
dependencies: Record<string, string>;
};
const superseded = { ...current, dependencies: { ...current.dependencies, smthrs: "0.34.0" } };
const superseded = legacySmithersPackageManifest("0.34.0");
fs.writeFileSync(manifestPath, `${JSON.stringify(superseded, null, 2)}\n`, "utf8");

// Without `--force`, so the run directory, the Ultrafuzz run ID and the
Expand All @@ -3653,6 +3650,32 @@ test("non-force init migrates a superseded generated manifest so the project kee
assert.doesNotThrow(() => assertSmithersPackageManifest(JSON.parse(fs.readFileSync(manifestPath, "utf8"))));
});

test("non-force init upgrades the prior 0.35 manifest's dependency pins", () => {
const project = tempProject();
assert.equal(initProject({ projectRoot: project, force: true }).ok, true);
const manifestPath = path.join(project, ".smithers", "package.json");
fs.writeFileSync(manifestPath, `${JSON.stringify(legacySmithersPackageManifest(SMITHERS_VERSION), null, 2)}\n`);

const upgraded = initProject({ projectRoot: project });

assert.equal(upgraded.ok, true, JSON.stringify(upgraded.diagnostics));
assert.equal(fs.readFileSync(manifestPath, "utf8"), renderSmithersPackageJson());
});

function legacySmithersPackageManifest(smithersVersion: string): Record<string, unknown> {
const current = JSON.parse(renderSmithersPackageJson()) as {
dependencies: Record<string, string>;
overrides: Record<string, string>;
};
return {
...current,
dependencies: { ...current.dependencies, smthrs: smithersVersion, zod: "4.4.3" },
overrides: Object.fromEntries(
Object.entries(current.overrides).filter(([name]) => name === "effect" || name.startsWith("@effect/"))
)
};
}

test("non-force init preserves a manifest that is not one Ultrafuzz generated", () => {
const project = tempProject();
assert.equal(initProject({ projectRoot: project, force: true }).ok, true);
Expand Down Expand Up @@ -18710,7 +18733,9 @@ test("every runner compatibility patch still anchors in the pinned Smithers rele
);
// Every override must land on one Effect version, or the pinned set is not
// internally consistent and npm reintroduces a second copy.
for (const [name, version] of Object.entries(REQUIRED_SMITHERS_OVERRIDES)) {
for (const [name, version] of Object.entries(REQUIRED_SMITHERS_OVERRIDES).filter(
([name]) => name === "effect" || name.startsWith("@effect/")
)) {
assert.equal(version, SMITHERS_EFFECT_VERSION, `override ${name} must track Effect ${SMITHERS_EFFECT_VERSION}`);
}
});
Expand Down Expand Up @@ -28250,12 +28275,7 @@ test("resume continues under a superseded generated manifest without changing ei

// Roll the project back to what a pre-bump `init` wrote.
const manifestPath = path.join(project, ".smithers", "package.json");
const current = JSON.parse(renderSmithersPackageJson()) as { dependencies: Record<string, string> };
const superseded = `${JSON.stringify(
{ ...current, dependencies: { ...current.dependencies, smthrs: "0.34.0" } },
null,
2
)}\n`;
const superseded = `${JSON.stringify(legacySmithersPackageManifest("0.34.0"), null, 2)}\n`;
fs.writeFileSync(manifestPath, superseded, "utf8");
fs.writeFileSync(env.SMITHERS_FAKE_LOG!, "", "utf8");

Expand Down
Loading
Loading