Skip to content
Draft
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
10 changes: 5 additions & 5 deletions ATTRIBUTIONS-Node.md
Original file line number Diff line number Diff line change
Expand Up @@ -25777,7 +25777,7 @@ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
```

## body-parser - 2.3.0
## body-parser - 2.4.0

**Repository URL**: https://github.com/expressjs/body-parser
**License Type(s)**: MIT
Expand Down Expand Up @@ -35192,7 +35192,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```

## qs - 6.16.0
## qs - 6.16.1

**Repository URL**: https://github.com/ljharb/qs
**License Type(s)**: BSD-3-Clause
Expand Down Expand Up @@ -37490,7 +37490,7 @@ If the Work includes a "NOTICE" text file as part of its distribution, then any
END OF TERMS AND CONDITIONS
```

## undici - 6.29.0
## undici - 6.29.1

**Repository URL**: https://github.com/nodejs/undici
**License Type(s)**: MIT
Expand Down Expand Up @@ -37552,7 +37552,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```

## undici - 8.11.2
## undici - 8.12.3

**Repository URL**: https://github.com/nodejs/undici
**License Type(s)**: MIT
Expand Down Expand Up @@ -38324,7 +38324,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```

## zod - 4.6.5
## zod - 4.6.6

**Repository URL**: https://github.com/colinhacks/zod
**License Type(s)**: MIT
Expand Down
2 changes: 1 addition & 1 deletion adapters/typescript/common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"@agentclientprotocol/sdk": "1.5.1",
"ajv": "8.20.0",
"nemo-fabric-adapter-contract": "0.6.0",
"zod": "4.1.8"
"zod": "4.6.6"
},
"devDependencies": {
"@types/node": "22.19.19",
Expand Down
13 changes: 13 additions & 0 deletions adapters/typescript/common/test/validation-limits.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

import assert from "node:assert/strict";
import test from "node:test";
import { z } from "zod";

test("rejects oversized constrained arrays before accumulating element errors", () => {
const result = z.array(z.string()).max(1).safeParse([1, 2, 3, 4]);
assert.equal(result.success, false);
assert.equal(result.error.issues.length, 1);
assert.equal(result.error.issues[0].code, "too_big");
});
53 changes: 31 additions & 22 deletions adapters/typescript/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 8 additions & 2 deletions adapters/typescript/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,14 @@
},
"overrides": {
"@opencode/core": {
"@modelcontextprotocol/client": "2.2.0",
"@modelcontextprotocol/core": "2.2.0"
"@modelcontextprotocol/client": {
".": "2.2.0",
"zod": "4.6.6"
},
"@modelcontextprotocol/core": {
".": "2.2.0",
"zod": "4.6.6"
}
},
"@parcel/watcher": "2.6.0"
},
Expand Down
88 changes: 77 additions & 11 deletions adapters/typescript/scripts/audit-dependencies.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,92 @@
// SPDX-License-Identifier: Apache-2.0

import { spawnSync } from "node:child_process";
import { dirname } from "node:path";
import { readFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";

const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));

export function checkAudit(report) {
// Temporary CI exception; removal and exposure assessment: NVIDIA/NeMo-Fabric#485.
// Only this advisory and these installed versions are excepted. Never force Zod 4
// into SDKs that require Zod 3. Compatible copies are patched separately.
const zodAdvisory = "https://github.com/advisories/GHSA-mrhx-c7xc-m3w5";
const allowedPackages = {
"@factory/droid-sdk": { "node_modules/@factory/droid-sdk": "0.9.1" },
"@qwen-code/sdk": { "node_modules/@qwen-code/sdk": "0.1.16" },
"gitlab-ai-provider": { "node_modules/gitlab-ai-provider": "6.18.0" },
zod: {
"node_modules/@factory/droid-sdk/node_modules/zod": "3.25.76",
"node_modules/@qwen-code/sdk/node_modules/zod": "3.25.76",
"node_modules/gitlab-ai-provider/node_modules/zod": "3.25.76",
"node_modules/zod": "4.1.8",
},
};
for (const name of ["core", "plugin", "plugin-browser", "sdk", "server", "simulation"]) {
allowedPackages[`@opencode/${name}`] = { [`node_modules/@opencode/${name}`]: "2.0.23" };
}

export function checkAudit(report, lockfile = {}) {
if (
report.error ||
!report.vulnerabilities ||
!report.metadata?.vulnerabilities
) {
throw new Error("npm audit did not return a complete vulnerability report");
}
const severe = Object.values(report.vulnerabilities).filter(
(finding) => finding.severity === "high" || finding.severity === "critical",
const severe = Object.entries(report.vulnerabilities).filter(
([, finding]) => finding.severity === "high" || finding.severity === "critical",
);
if (
severe.length !== 0 ||
report.metadata.vulnerabilities.high !== 0 ||
report.metadata.vulnerabilities.critical !== 0
report.metadata.vulnerabilities.critical !== 0 ||
report.metadata.vulnerabilities.high !== severe.length ||
severe.some(([, finding]) => finding.severity === "critical")
) {
throw new Error("npm audit reported a high- or critical-severity finding");
throw new Error("Unexpected high/critical audit counts or critical finding");
}
const severeNames = new Set(severe.map(([name]) => name));
// npm reports parent-package metavulnerabilities, including cyclic OpenCode
// dependencies. Each severe package must trace to the approved Zod cause.
function reachesZod(name, visiting = new Set()) {
if (name === "zod") return true;
if (visiting.has(name)) return false;
const next = new Set(visiting).add(name);
return report.vulnerabilities[name].via.some(
(cause) => typeof cause === "string" && severeNames.has(cause) && reachesZod(cause, next),
);
}
for (const [name, finding] of severe) {
const allowedNodes = allowedPackages[name];
if (
finding.name !== name ||
!allowedNodes ||
!Array.isArray(finding.nodes) ||
finding.nodes.length === 0 ||
!Array.isArray(finding.via) ||
finding.via.length === 0 ||
finding.nodes.some((path) => !Object.hasOwn(allowedNodes, path) ||
lockfile.packages?.[path]?.version !== allowedNodes[path])
) {
throw new Error(`Unapproved audit package, path or version: ${name}`);
}
for (const cause of finding.via) {
if (typeof cause === "string") {
if (!report.vulnerabilities[cause] || (name === "zod")) {
throw new Error(`Unrecognized audit dependency: ${name} -> ${cause}`);
}
} else if (
cause.severity === "critical" ||
(cause.severity === "high" && (name !== "zod" || cause.name !== "zod" ||
cause.url !== zodAdvisory || cause.range !== "<4.6.6"))
) {
throw new Error(`Advisory outside the temporary Zod exception: ${name}`);
}
}
if (!reachesZod(name) || (name === "zod" && !finding.via.some(
(cause) => cause.severity === "high" && cause.url === zodAdvisory,
))) {
throw new Error(`High-severity finding has no approved Zod cause: ${name}`);
}
}
return report.metadata.vulnerabilities;
}
Expand All @@ -47,9 +111,11 @@ if (process.argv[1] === fileURLToPath(import.meta.url)) {
} catch {
throw new Error(`npm audit failed without JSON output: ${audit.stderr}`);
}
const counts = checkAudit(report);
const lockfile = JSON.parse(readFileSync(join(packageRoot, "package-lock.json"), "utf8"));
const counts = checkAudit(report, lockfile);
console.log(
`npm audit: ${counts.high} high and ${counts.critical} critical; ` +
`${counts.moderate} moderate and ${counts.low} low findings remain.`,
`npm audit: ${counts.high} high covered by temporary Zod CI exception (#485); ` +
`${counts.critical} critical; ` +
`${counts.moderate} moderate and ${counts.low} low findings remain.`,
);
}
Loading
Loading