1+ diff --git a/node_modules/zod/.bun-tag-82bf292cf1bc8e88 b/.bun-tag-82bf292cf1bc8e88
2+ new file mode 100644
3+ index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
14diff --git a/v4/core/schemas.cjs b/v4/core/schemas.cjs
2- index 8444c1fa122b0502f56b141ffa2939906b49baf9..9a085e4beacdbe1ef84c4c6a30b471b4aa9f9625 100644
5+ index 8444c1fa122b0502f56b141ffa2939906b49baf9..b1dc056dfe24e7e17927b5b61cbf528fc3830eb8 100644
36--- a/v4/core/schemas.cjs
47+++ b/v4/core/schemas.cjs
5- @@ -685,6 +685,37 @@ function handleArrayResult(result, final, index) {
8+ @@ -685,6 +685,48 @@ function handleArrayResult(result, final, index) {
69 }
710 final.value[index] = result.value;
811 }
912+ // Backport of colinhacks/zod#6652 (zod 4.6.6, GHSA-mrhx-c7xc-m3w5): a container larger than
10- + // its leading upper size bound skips its elements, so that bound's check (which has a `when`
11- + // guard and always runs) reports it without parsing and collecting an issue per element first.
13+ + // its leading upper size bound fails on that bound before any element is parsed, instead of
14+ + // collecting an issue per element first.
1215+ function sizeCaps(defChecks) {
1316+ const caps = [];
1417+ for (const ch of defChecks ?? []) {
@@ -18,20 +21,31 @@ index 8444c1fa122b0502f56b141ffa2939906b49baf9..9a085e4beacdbe1ef84c4c6a30b471b4
1821+ break;
1922+ const max = d.maximum ?? d.length ?? d.size;
2023+ if (max >= 0)
21- + caps.push(max);
24+ + caps.push([ max, ch] );
2225+ }
2326+ return caps;
2427+ }
2528+ function capSize(inst, size) {
2629+ const caps = sizeCaps(inst._zod.def.checks);
2730+ if (!caps.length)
2831+ return;
29- + const cap = Math.min(...caps);
30- + const parse = inst._zod.parse;
31- + inst._zod.parse = (payload, ctx) => {
32- + if (size(payload.value) > cap && !ctx?.skipChecks && ctx?.direction !== "backward")
33- + return payload;
34- + return parse(payload, ctx);
32+ + const cap = Math.min(...caps.map((c) => c[0]));
33+ + const run = inst._zod.run;
34+ + // gates `run`, not `parse`: 4.3.6 runs `when`-guarded checks even after an abort, so the
35+ + // failing bound reports once here and no later check reads the unparsed elements
36+ + inst._zod.run = (payload, ctx) => {
37+ + const n = size(payload.value);
38+ + if (n > cap && !ctx?.skipChecks && ctx?.direction !== "backward") {
39+ + for (const [max, ch] of caps) {
40+ + if (n > max && ch._zod.def.when(payload)) {
41+ + const start = payload.issues.length;
42+ + ch._zod.check(payload);
43+ + payload.issues[start].continue = false;
44+ + return payload;
45+ + }
46+ + }
47+ + }
48+ + return run(payload, ctx);
3549+ };
3650+ }
3751+ const arraySize = (input) => (Array.isArray(input) ? input.length : -1);
@@ -40,23 +54,23 @@ index 8444c1fa122b0502f56b141ffa2939906b49baf9..9a085e4beacdbe1ef84c4c6a30b471b4
4054 exports.$ZodArray = core.$constructor("$ZodArray", (inst, def) => {
4155 exports.$ZodType.init(inst, def);
4256 inst._zod.parse = (payload, ctx) => {
43- @@ -718,6 +749 ,7 @@ exports.$ZodArray = core.$constructor("$ZodArray", (inst, def) => {
57+ @@ -718,6 +760 ,7 @@ exports.$ZodArray = core.$constructor("$ZodArray", (inst, def) => {
4458 }
4559 return payload; //handleArrayResultsAsync(parseResults, final);
4660 };
4761+ capSize(inst, arraySize);
4862 });
4963 function handlePropertyResult(result, final, key, input, isOptionalOut) {
5064 if (result.issues.length) {
51- @@ -1480,6 +1512 ,7 @@ exports.$ZodMap = core.$constructor("$ZodMap", (inst, def) => {
65+ @@ -1480,6 +1523 ,7 @@ exports.$ZodMap = core.$constructor("$ZodMap", (inst, def) => {
5266 return Promise.all(proms).then(() => payload);
5367 return payload;
5468 };
5569+ capSize(inst, mapSize);
5670 });
5771 function handleMapResult(keyResult, valueResult, final, key, input, inst, ctx) {
5872 if (keyResult.issues.length) {
59- @@ -1540,6 +1573 ,7 @@ exports.$ZodSet = core.$constructor("$ZodSet", (inst, def) => {
73+ @@ -1540,6 +1584 ,7 @@ exports.$ZodSet = core.$constructor("$ZodSet", (inst, def) => {
6074 return Promise.all(proms).then(() => payload);
6175 return payload;
6276 };
@@ -65,16 +79,16 @@ index 8444c1fa122b0502f56b141ffa2939906b49baf9..9a085e4beacdbe1ef84c4c6a30b471b4
6579 function handleSetResult(result, final) {
6680 if (result.issues.length) {
6781diff --git a/v4/core/schemas.js b/v4/core/schemas.js
68- index 29c61bc5fcf2ca90c2bb0121b7f02e16fdf19f11..f87884eea6e301f1c9c46be45953226e850e4795 100644
82+ index 29c61bc5fcf2ca90c2bb0121b7f02e16fdf19f11..1123f503df65ff9993a0e04a4c78dfb3e216b961 100644
6983--- a/v4/core/schemas.js
7084+++ b/v4/core/schemas.js
71- @@ -654,6 +654,37 @@ function handleArrayResult(result, final, index) {
85+ @@ -654,6 +654,48 @@ function handleArrayResult(result, final, index) {
7286 }
7387 final.value[index] = result.value;
7488 }
7589+ // Backport of colinhacks/zod#6652 (zod 4.6.6, GHSA-mrhx-c7xc-m3w5): a container larger than
76- + // its leading upper size bound skips its elements, so that bound's check (which has a `when`
77- + // guard and always runs) reports it without parsing and collecting an issue per element first.
90+ + // its leading upper size bound fails on that bound before any element is parsed, instead of
91+ + // collecting an issue per element first.
7892+ function sizeCaps(defChecks) {
7993+ const caps = [];
8094+ for (const ch of defChecks ?? []) {
@@ -84,20 +98,31 @@ index 29c61bc5fcf2ca90c2bb0121b7f02e16fdf19f11..f87884eea6e301f1c9c46be45953226e
8498+ break;
8599+ const max = d.maximum ?? d.length ?? d.size;
86100+ if (max >= 0)
87- + caps.push(max);
101+ + caps.push([ max, ch] );
88102+ }
89103+ return caps;
90104+ }
91105+ function capSize(inst, size) {
92106+ const caps = sizeCaps(inst._zod.def.checks);
93107+ if (!caps.length)
94108+ return;
95- + const cap = Math.min(...caps);
96- + const parse = inst._zod.parse;
97- + inst._zod.parse = (payload, ctx) => {
98- + if (size(payload.value) > cap && !ctx?.skipChecks && ctx?.direction !== "backward")
99- + return payload;
100- + return parse(payload, ctx);
109+ + const cap = Math.min(...caps.map((c) => c[0]));
110+ + const run = inst._zod.run;
111+ + // gates `run`, not `parse`: 4.3.6 runs `when`-guarded checks even after an abort, so the
112+ + // failing bound reports once here and no later check reads the unparsed elements
113+ + inst._zod.run = (payload, ctx) => {
114+ + const n = size(payload.value);
115+ + if (n > cap && !ctx?.skipChecks && ctx?.direction !== "backward") {
116+ + for (const [max, ch] of caps) {
117+ + if (n > max && ch._zod.def.when(payload)) {
118+ + const start = payload.issues.length;
119+ + ch._zod.check(payload);
120+ + payload.issues[start].continue = false;
121+ + return payload;
122+ + }
123+ + }
124+ + }
125+ + return run(payload, ctx);
101126+ };
102127+ }
103128+ const arraySize = (input) => (Array.isArray(input) ? input.length : -1);
@@ -106,23 +131,23 @@ index 29c61bc5fcf2ca90c2bb0121b7f02e16fdf19f11..f87884eea6e301f1c9c46be45953226e
106131 export const $ZodArray = /*@__PURE__*/ core.$constructor("$ZodArray", (inst, def) => {
107132 $ZodType.init(inst, def);
108133 inst._zod.parse = (payload, ctx) => {
109- @@ -687,6 +718 ,7 @@ export const $ZodArray = /*@__PURE__*/ core.$constructor("$ZodArray", (inst, def
134+ @@ -687,6 +729 ,7 @@ export const $ZodArray = /*@__PURE__*/ core.$constructor("$ZodArray", (inst, def
110135 }
111136 return payload; //handleArrayResultsAsync(parseResults, final);
112137 };
113138+ capSize(inst, arraySize);
114139 });
115140 function handlePropertyResult(result, final, key, input, isOptionalOut) {
116141 if (result.issues.length) {
117- @@ -1449,6 +1481 ,7 @@ export const $ZodMap = /*@__PURE__*/ core.$constructor("$ZodMap", (inst, def) =>
142+ @@ -1449,6 +1492 ,7 @@ export const $ZodMap = /*@__PURE__*/ core.$constructor("$ZodMap", (inst, def) =>
118143 return Promise.all(proms).then(() => payload);
119144 return payload;
120145 };
121146+ capSize(inst, mapSize);
122147 });
123148 function handleMapResult(keyResult, valueResult, final, key, input, inst, ctx) {
124149 if (keyResult.issues.length) {
125- @@ -1509,6 +1542 ,7 @@ export const $ZodSet = /*@__PURE__*/ core.$constructor("$ZodSet", (inst, def) =>
150+ @@ -1509,6 +1553 ,7 @@ export const $ZodSet = /*@__PURE__*/ core.$constructor("$ZodSet", (inst, def) =>
126151 return Promise.all(proms).then(() => payload);
127152 return payload;
128153 };
0 commit comments