@zuplo/cli 6.73.11 → 6.73.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/__tests__/integration/link.integration.test.js +12 -1
  2. package/dist/__tests__/integration/link.integration.test.js.map +1 -1
  3. package/dist/link/handler.d.ts.map +1 -1
  4. package/dist/link/handler.js +7 -0
  5. package/dist/link/handler.js.map +1 -1
  6. package/dist/link/project-type.d.ts +22 -0
  7. package/dist/link/project-type.d.ts.map +1 -0
  8. package/dist/link/project-type.js +140 -0
  9. package/dist/link/project-type.js.map +1 -0
  10. package/dist/link/project-type.test.d.ts +2 -0
  11. package/dist/link/project-type.test.d.ts.map +1 -0
  12. package/dist/link/project-type.test.js +225 -0
  13. package/dist/link/project-type.test.js.map +1 -0
  14. package/dist/tsconfig.tsbuildinfo +1 -1
  15. package/node_modules/@hono/node-server/dist/serve-static.js +1 -1
  16. package/node_modules/@hono/node-server/dist/serve-static.mjs +1 -1
  17. package/node_modules/@hono/node-server/package.json +1 -1
  18. package/node_modules/@posthog/core/dist/error-tracking/parsers/node.d.ts.map +1 -1
  19. package/node_modules/@posthog/core/dist/error-tracking/parsers/node.js +4 -0
  20. package/node_modules/@posthog/core/dist/error-tracking/parsers/node.mjs +4 -0
  21. package/node_modules/@posthog/core/package.json +1 -1
  22. package/node_modules/@posthog/core/src/error-tracking/parsers/node.spec.ts +19 -0
  23. package/node_modules/@posthog/core/src/error-tracking/parsers/node.ts +8 -0
  24. package/node_modules/@zuplo/core/package.json +1 -1
  25. package/node_modules/@zuplo/graphql/package.json +1 -1
  26. package/node_modules/@zuplo/openapi-tools/package.json +1 -1
  27. package/node_modules/@zuplo/otel/package.json +1 -1
  28. package/node_modules/@zuplo/runtime/package.json +1 -1
  29. package/node_modules/brace-expansion/README.md +12 -0
  30. package/node_modules/brace-expansion/dist/commonjs/index.d.ts +2 -0
  31. package/node_modules/brace-expansion/dist/commonjs/index.d.ts.map +1 -1
  32. package/node_modules/brace-expansion/dist/commonjs/index.js +138 -86
  33. package/node_modules/brace-expansion/dist/commonjs/index.js.map +1 -1
  34. package/node_modules/brace-expansion/dist/esm/index.d.ts +2 -0
  35. package/node_modules/brace-expansion/dist/esm/index.d.ts.map +1 -1
  36. package/node_modules/brace-expansion/dist/esm/index.js +137 -85
  37. package/node_modules/brace-expansion/dist/esm/index.js.map +1 -1
  38. package/node_modules/brace-expansion/package.json +2 -2
  39. package/node_modules/media-typer/README.md +15 -14
  40. package/node_modules/media-typer/index.js +0 -8
  41. package/node_modules/media-typer/package.json +16 -12
  42. package/package.json +6 -6
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.EXPANSION_MAX = void 0;
3
+ exports.EXPANSION_MAX_LENGTH = exports.EXPANSION_MAX = void 0;
4
4
  exports.expand = expand;
5
5
  const balanced_match_1 = require("balanced-match");
6
6
  const escSlash = '\0SLASH' + Math.random() + '\0';
@@ -19,6 +19,17 @@ const closePattern = /\\}/g;
19
19
  const commaPattern = /\\,/g;
20
20
  const periodPattern = /\\\./g;
21
21
  exports.EXPANSION_MAX = 100_000;
22
+ // `EXPANSION_MAX` caps the *number* of expansions, but not their length. An
23
+ // input like `'{a,b}'.repeat(1500)` stays under that count - its output is
24
+ // truncated to 100k results - while making every result ~1500 characters
25
+ // long. The result set, and the intermediate arrays built while combining
26
+ // brace sets, then grow large enough to exhaust memory and crash the process
27
+ // (CVE-2026-14257). `EXPANSION_MAX_LENGTH` bounds the total number of
28
+ // characters the accumulator may hold at any point, so memory stays flat no
29
+ // matter how many brace groups are chained. The limit sits well above any
30
+ // realistic expansion (100k results hitting `EXPANSION_MAX` measure ~1M
31
+ // characters) so legitimate input is unaffected.
32
+ exports.EXPANSION_MAX_LENGTH = 4_000_000;
22
33
  function numeric(str) {
23
34
  return !isNaN(str) ? parseInt(str, 10) : str.charCodeAt(0);
24
35
  }
@@ -68,7 +79,7 @@ function expand(str, options = {}) {
68
79
  if (!str) {
69
80
  return [];
70
81
  }
71
- const { max = exports.EXPANSION_MAX } = options;
82
+ const { max = exports.EXPANSION_MAX, maxLength = exports.EXPANSION_MAX_LENGTH } = options;
72
83
  // I don't know why Bash 4.3 does this, but it does.
73
84
  // Anything starting with {} will have the first two bytes preserved
74
85
  // but *only* at the top level, so {},a}b will not expand to anything,
@@ -78,7 +89,7 @@ function expand(str, options = {}) {
78
89
  if (str.slice(0, 2) === '{}') {
79
90
  str = '\\{\\}' + str.slice(2);
80
91
  }
81
- return expand_(escapeBraces(str), max, true).map(unescapeBraces);
92
+ return expand_(escapeBraces(str), max, maxLength, true).map(unescapeBraces);
82
93
  }
83
94
  function embrace(str) {
84
95
  return '{' + str + '}';
@@ -92,25 +103,112 @@ function lte(i, y) {
92
103
  function gte(i, y) {
93
104
  return i >= y;
94
105
  }
95
- function expand_(str, max, isTop) {
96
- /** @type {string[]} */
97
- const expansions = [];
98
- // The `{a},b}` rewrite below restarts expansion on a rewritten string with
99
- // the same `max` and `isTop = true`. Loop instead of recursing so a long run
100
- // of non-expanding `{}` groups can't exhaust the call stack.
106
+ // Build `{ acc[a] + pre + values[v] }` for every combination, capping the
107
+ // number of results at `max` and the total number of characters at `maxLength`.
108
+ // This is the one place output grows, so bounding it here keeps the single
109
+ // accumulator - and therefore memory - flat regardless of how many brace groups
110
+ // are combined (CVE-2026-14257).
111
+ function combine(acc, pre, values, max, maxLength, dropEmpties) {
112
+ const out = [];
113
+ let length = 0;
114
+ for (let a = 0; a < acc.length; a++) {
115
+ for (let v = 0; v < values.length; v++) {
116
+ if (out.length >= max)
117
+ return out;
118
+ const expansion = acc[a] + pre + values[v];
119
+ // Bash drops empty results at the top level. Skip them before they count
120
+ // against `max`, so `max` bounds the number of *kept* results.
121
+ if (dropEmpties && !expansion)
122
+ continue;
123
+ if (length + expansion.length > maxLength)
124
+ return out;
125
+ out.push(expansion);
126
+ length += expansion.length;
127
+ }
128
+ }
129
+ return out;
130
+ }
131
+ // The expansion values of a single numeric (`1..5`) or alphabetic (`a..e..2`)
132
+ // sequence body.
133
+ function expandSequence(body, isAlphaSequence, max) {
134
+ const n = body.split(/\.\./);
135
+ const N = [];
136
+ // A sequence body always splits into two or three parts, but the compiler
137
+ // can't know that.
138
+ /* c8 ignore start */
139
+ if (n[0] === undefined || n[1] === undefined) {
140
+ return N;
141
+ }
142
+ /* c8 ignore stop */
143
+ const x = numeric(n[0]);
144
+ const y = numeric(n[1]);
145
+ const width = Math.max(n[0].length, n[1].length);
146
+ let incr = n.length === 3 && n[2] !== undefined ?
147
+ Math.max(Math.abs(numeric(n[2])), 1)
148
+ : 1;
149
+ let test = lte;
150
+ const reverse = y < x;
151
+ if (reverse) {
152
+ incr *= -1;
153
+ test = gte;
154
+ }
155
+ const pad = n.some(isPadded);
156
+ for (let i = x; test(i, y) && N.length < max; i += incr) {
157
+ let c;
158
+ if (isAlphaSequence) {
159
+ c = String.fromCharCode(i);
160
+ if (c === '\\') {
161
+ c = '';
162
+ }
163
+ }
164
+ else {
165
+ c = String(i);
166
+ if (pad) {
167
+ const need = width - c.length;
168
+ if (need > 0) {
169
+ const z = new Array(need + 1).join('0');
170
+ if (i < 0) {
171
+ c = '-' + z + c.slice(1);
172
+ }
173
+ else {
174
+ c = z + c;
175
+ }
176
+ }
177
+ }
178
+ }
179
+ N.push(c);
180
+ }
181
+ return N;
182
+ }
183
+ function expand_(str, max, maxLength, isTop) {
184
+ // Consume the string's top-level brace groups left to right, threading a
185
+ // running set of combined prefixes (`acc`). Expanding the tail iteratively -
186
+ // rather than recursing on `m.post` once per group - keeps the native stack
187
+ // depth constant, so deeply chained input (`'{a,b}'.repeat(3000)`) can no
188
+ // longer overflow the stack, and leaves a single accumulator whose size
189
+ // `maxLength` bounds directly (CVE-2026-14257).
190
+ let acc = [''];
191
+ // Bash drops empty results, but only when the *first* top-level group is a
192
+ // comma set - a sequence like `{a..\}` may legitimately yield ''. The drop
193
+ // is on the final strings, so it is applied to whichever `combine` produces
194
+ // them (the one with no brace set left in the tail).
195
+ let dropEmpties = false;
196
+ let firstGroup = true;
101
197
  for (;;) {
102
198
  const m = (0, balanced_match_1.balanced)('{', '}', str);
103
- if (!m)
104
- return [str];
199
+ // No brace set left: the rest of the string is literal.
200
+ if (!m) {
201
+ return combine(acc, str, [''], max, maxLength, dropEmpties);
202
+ }
105
203
  // no need to expand pre, since it is guaranteed to be free of brace-sets
106
204
  const pre = m.pre;
107
- if (/\$$/.test(m.pre)) {
108
- const post = m.post.length ? expand_(m.post, max, false) : [''];
109
- for (let k = 0; k < post.length && k < max; k++) {
110
- const expansion = pre + '{' + m.body + '}' + post[k];
111
- expansions.push(expansion);
112
- }
113
- return expansions;
205
+ if (/\$$/.test(pre)) {
206
+ acc = combine(acc, pre + '{' + m.body + '}', [''], max, maxLength, dropEmpties && !m.post.length);
207
+ firstGroup = false;
208
+ if (!m.post.length)
209
+ break;
210
+ str = m.post;
211
+ continue;
114
212
  }
115
213
  const isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body);
116
214
  const isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body);
@@ -123,89 +221,43 @@ function expand_(str, max, isTop) {
123
221
  isTop = true;
124
222
  continue;
125
223
  }
126
- return [str];
224
+ // Nothing here expands, so the whole remaining string is literal.
225
+ return combine(acc, pre + '{' + m.body + '}' + m.post, [''], max, maxLength, dropEmpties);
127
226
  }
128
- // Only expand post once we know this brace set actually expands. Computing
129
- // it before the early returns above expanded post a second time on every
130
- // non-expanding `{}`, which is what made inputs like `a{},{},{}...` blow up
131
- // exponentially.
132
- const post = m.post.length ? expand_(m.post, max, false) : [''];
133
- let n;
227
+ if (firstGroup) {
228
+ dropEmpties = isTop && !isSequence;
229
+ firstGroup = false;
230
+ }
231
+ let values;
134
232
  if (isSequence) {
135
- n = m.body.split(/\.\./);
233
+ values = expandSequence(m.body, isAlphaSequence, max);
136
234
  }
137
235
  else {
138
- n = parseCommaParts(m.body);
236
+ let n = parseCommaParts(m.body);
139
237
  if (n.length === 1 && n[0] !== undefined) {
140
238
  // x{{a,b}}y ==> x{a}y x{b}y
141
- n = expand_(n[0], max, false).map(embrace);
239
+ n = expand_(n[0], max, maxLength, false).map(embrace);
142
240
  //XXX is this necessary? Can't seem to hit it in tests.
143
241
  /* c8 ignore start */
144
242
  if (n.length === 1) {
145
- return post.map(p => m.pre + n[0] + p);
243
+ acc = combine(acc, pre + n[0], [''], max, maxLength, dropEmpties && !m.post.length);
244
+ if (!m.post.length)
245
+ break;
246
+ str = m.post;
247
+ continue;
146
248
  }
147
249
  /* c8 ignore stop */
148
250
  }
149
- }
150
- // at this point, n is the parts, and we know it's not a comma set
151
- // with a single entry.
152
- let N;
153
- if (isSequence && n[0] !== undefined && n[1] !== undefined) {
154
- const x = numeric(n[0]);
155
- const y = numeric(n[1]);
156
- const width = Math.max(n[0].length, n[1].length);
157
- let incr = n.length === 3 && n[2] !== undefined ?
158
- Math.max(Math.abs(numeric(n[2])), 1)
159
- : 1;
160
- let test = lte;
161
- const reverse = y < x;
162
- if (reverse) {
163
- incr *= -1;
164
- test = gte;
165
- }
166
- const pad = n.some(isPadded);
167
- N = [];
168
- for (let i = x; test(i, y) && N.length < max; i += incr) {
169
- let c;
170
- if (isAlphaSequence) {
171
- c = String.fromCharCode(i);
172
- if (c === '\\') {
173
- c = '';
174
- }
175
- }
176
- else {
177
- c = String(i);
178
- if (pad) {
179
- const need = width - c.length;
180
- if (need > 0) {
181
- const z = new Array(need + 1).join('0');
182
- if (i < 0) {
183
- c = '-' + z + c.slice(1);
184
- }
185
- else {
186
- c = z + c;
187
- }
188
- }
189
- }
190
- }
191
- N.push(c);
192
- }
193
- }
194
- else {
195
- N = [];
251
+ values = [];
196
252
  for (let j = 0; j < n.length; j++) {
197
- N.push.apply(N, expand_(n[j], max, false));
198
- }
199
- }
200
- for (let j = 0; j < N.length; j++) {
201
- for (let k = 0; k < post.length && expansions.length < max; k++) {
202
- const expansion = pre + N[j] + post[k];
203
- if (!isTop || isSequence || expansion) {
204
- expansions.push(expansion);
205
- }
253
+ values.push.apply(values, expand_(n[j], max, maxLength, false));
206
254
  }
207
255
  }
208
- return expansions;
256
+ acc = combine(acc, pre, values, max, maxLength, dropEmpties && !m.post.length);
257
+ if (!m.post.length)
258
+ break;
259
+ str = m.post;
209
260
  }
261
+ return acc;
210
262
  }
211
263
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { balanced } from 'balanced-match'\n\nconst escSlash = '\\0SLASH' + Math.random() + '\\0'\nconst escOpen = '\\0OPEN' + Math.random() + '\\0'\nconst escClose = '\\0CLOSE' + Math.random() + '\\0'\nconst escComma = '\\0COMMA' + Math.random() + '\\0'\nconst escPeriod = '\\0PERIOD' + Math.random() + '\\0'\nconst escSlashPattern = new RegExp(escSlash, 'g')\nconst escOpenPattern = new RegExp(escOpen, 'g')\nconst escClosePattern = new RegExp(escClose, 'g')\nconst escCommaPattern = new RegExp(escComma, 'g')\nconst escPeriodPattern = new RegExp(escPeriod, 'g')\nconst slashPattern = /\\\\\\\\/g\nconst openPattern = /\\\\{/g\nconst closePattern = /\\\\}/g\nconst commaPattern = /\\\\,/g\nconst periodPattern = /\\\\\\./g\n\nexport const EXPANSION_MAX = 100_000\n\nfunction numeric(str: string) {\n return !isNaN(str as any) ? parseInt(str, 10) : str.charCodeAt(0)\n}\n\nfunction escapeBraces(str: string) {\n return str\n .replace(slashPattern, escSlash)\n .replace(openPattern, escOpen)\n .replace(closePattern, escClose)\n .replace(commaPattern, escComma)\n .replace(periodPattern, escPeriod)\n}\n\nfunction unescapeBraces(str: string) {\n return str\n .replace(escSlashPattern, '\\\\')\n .replace(escOpenPattern, '{')\n .replace(escClosePattern, '}')\n .replace(escCommaPattern, ',')\n .replace(escPeriodPattern, '.')\n}\n\n/**\n * Basically just str.split(\",\"), but handling cases\n * where we have nested braced sections, which should be\n * treated as individual members, like {a,{b,c},d}\n */\nfunction parseCommaParts(str: string) {\n if (!str) {\n return ['']\n }\n\n const parts: string[] = []\n const m = balanced('{', '}', str)\n\n if (!m) {\n return str.split(',')\n }\n\n const { pre, body, post } = m\n const p = pre.split(',')\n\n p[p.length - 1] += '{' + body + '}'\n const postParts = parseCommaParts(post)\n if (post.length) {\n ;(p[p.length - 1] as string) += postParts.shift()\n p.push.apply(p, postParts)\n }\n\n parts.push.apply(parts, p)\n\n return parts\n}\n\nexport type BraceExpansionOptions = {\n max?: number\n}\n\nexport function expand(str: string, options: BraceExpansionOptions = {}) {\n if (!str) {\n return []\n }\n\n const { max = EXPANSION_MAX } = options\n\n // I don't know why Bash 4.3 does this, but it does.\n // Anything starting with {} will have the first two bytes preserved\n // but *only* at the top level, so {},a}b will not expand to anything,\n // but a{},b}c will be expanded to [a}c,abc].\n // One could argue that this is a bug in Bash, but since the goal of\n // this module is to match Bash's rules, we escape a leading {}\n if (str.slice(0, 2) === '{}') {\n str = '\\\\{\\\\}' + str.slice(2)\n }\n\n return expand_(escapeBraces(str), max, true).map(unescapeBraces)\n}\n\nfunction embrace(str: string) {\n return '{' + str + '}'\n}\n\nfunction isPadded(el: string) {\n return /^-?0\\d/.test(el)\n}\n\nfunction lte(i: number, y: number) {\n return i <= y\n}\n\nfunction gte(i: number, y: number) {\n return i >= y\n}\n\nfunction expand_(str: string, max: number, isTop: boolean): string[] {\n /** @type {string[]} */\n const expansions: string[] = []\n\n // The `{a},b}` rewrite below restarts expansion on a rewritten string with\n // the same `max` and `isTop = true`. Loop instead of recursing so a long run\n // of non-expanding `{}` groups can't exhaust the call stack.\n for (;;) {\n const m = balanced('{', '}', str)\n if (!m) return [str]\n\n // no need to expand pre, since it is guaranteed to be free of brace-sets\n const pre = m.pre\n\n if (/\\$$/.test(m.pre)) {\n const post: string[] =\n m.post.length ? expand_(m.post, max, false) : ['']\n for (let k = 0; k < post.length && k < max; k++) {\n const expansion = pre + '{' + m.body + '}' + post[k]\n expansions.push(expansion)\n }\n return expansions\n }\n\n const isNumericSequence = /^-?\\d+\\.\\.-?\\d+(?:\\.\\.-?\\d+)?$/.test(m.body)\n const isAlphaSequence = /^[a-zA-Z]\\.\\.[a-zA-Z](?:\\.\\.-?\\d+)?$/.test(\n m.body,\n )\n const isSequence = isNumericSequence || isAlphaSequence\n const isOptions = m.body.indexOf(',') >= 0\n if (!isSequence && !isOptions) {\n // {a},b}\n if (m.post.match(/,(?!,).*\\}/)) {\n str = m.pre + '{' + m.body + escClose + m.post\n isTop = true\n continue\n }\n return [str]\n }\n\n // Only expand post once we know this brace set actually expands. Computing\n // it before the early returns above expanded post a second time on every\n // non-expanding `{}`, which is what made inputs like `a{},{},{}...` blow up\n // exponentially.\n const post: string[] =\n m.post.length ? expand_(m.post, max, false) : ['']\n\n let n: string[]\n if (isSequence) {\n n = m.body.split(/\\.\\./)\n } else {\n n = parseCommaParts(m.body)\n if (n.length === 1 && n[0] !== undefined) {\n // x{{a,b}}y ==> x{a}y x{b}y\n n = expand_(n[0], max, false).map(embrace)\n //XXX is this necessary? Can't seem to hit it in tests.\n /* c8 ignore start */\n if (n.length === 1) {\n return post.map(p => m.pre + n[0] + p)\n }\n /* c8 ignore stop */\n }\n }\n\n // at this point, n is the parts, and we know it's not a comma set\n // with a single entry.\n let N: string[]\n\n if (isSequence && n[0] !== undefined && n[1] !== undefined) {\n const x = numeric(n[0])\n const y = numeric(n[1])\n const width = Math.max(n[0].length, n[1].length)\n let incr =\n n.length === 3 && n[2] !== undefined ?\n Math.max(Math.abs(numeric(n[2])), 1)\n : 1\n let test = lte\n const reverse = y < x\n if (reverse) {\n incr *= -1\n test = gte\n }\n const pad = n.some(isPadded)\n\n N = []\n\n for (let i = x; test(i, y) && N.length < max; i += incr) {\n let c\n if (isAlphaSequence) {\n c = String.fromCharCode(i)\n if (c === '\\\\') {\n c = ''\n }\n } else {\n c = String(i)\n if (pad) {\n const need = width - c.length\n if (need > 0) {\n const z = new Array(need + 1).join('0')\n if (i < 0) {\n c = '-' + z + c.slice(1)\n } else {\n c = z + c\n }\n }\n }\n }\n N.push(c)\n }\n } else {\n N = []\n\n for (let j = 0; j < n.length; j++) {\n N.push.apply(N, expand_(n[j] as string, max, false))\n }\n }\n\n for (let j = 0; j < N.length; j++) {\n for (let k = 0; k < post.length && expansions.length < max; k++) {\n const expansion = pre + N[j] + post[k]\n if (!isTop || isSequence || expansion) {\n expansions.push(expansion)\n }\n }\n }\n\n return expansions\n }\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { balanced } from 'balanced-match'\n\nconst escSlash = '\\0SLASH' + Math.random() + '\\0'\nconst escOpen = '\\0OPEN' + Math.random() + '\\0'\nconst escClose = '\\0CLOSE' + Math.random() + '\\0'\nconst escComma = '\\0COMMA' + Math.random() + '\\0'\nconst escPeriod = '\\0PERIOD' + Math.random() + '\\0'\nconst escSlashPattern = new RegExp(escSlash, 'g')\nconst escOpenPattern = new RegExp(escOpen, 'g')\nconst escClosePattern = new RegExp(escClose, 'g')\nconst escCommaPattern = new RegExp(escComma, 'g')\nconst escPeriodPattern = new RegExp(escPeriod, 'g')\nconst slashPattern = /\\\\\\\\/g\nconst openPattern = /\\\\{/g\nconst closePattern = /\\\\}/g\nconst commaPattern = /\\\\,/g\nconst periodPattern = /\\\\\\./g\n\nexport const EXPANSION_MAX = 100_000\n\n// `EXPANSION_MAX` caps the *number* of expansions, but not their length. An\n// input like `'{a,b}'.repeat(1500)` stays under that count - its output is\n// truncated to 100k results - while making every result ~1500 characters\n// long. The result set, and the intermediate arrays built while combining\n// brace sets, then grow large enough to exhaust memory and crash the process\n// (CVE-2026-14257). `EXPANSION_MAX_LENGTH` bounds the total number of\n// characters the accumulator may hold at any point, so memory stays flat no\n// matter how many brace groups are chained. The limit sits well above any\n// realistic expansion (100k results hitting `EXPANSION_MAX` measure ~1M\n// characters) so legitimate input is unaffected.\nexport const EXPANSION_MAX_LENGTH = 4_000_000\n\nfunction numeric(str: string) {\n return !isNaN(str as any) ? parseInt(str, 10) : str.charCodeAt(0)\n}\n\nfunction escapeBraces(str: string) {\n return str\n .replace(slashPattern, escSlash)\n .replace(openPattern, escOpen)\n .replace(closePattern, escClose)\n .replace(commaPattern, escComma)\n .replace(periodPattern, escPeriod)\n}\n\nfunction unescapeBraces(str: string) {\n return str\n .replace(escSlashPattern, '\\\\')\n .replace(escOpenPattern, '{')\n .replace(escClosePattern, '}')\n .replace(escCommaPattern, ',')\n .replace(escPeriodPattern, '.')\n}\n\n/**\n * Basically just str.split(\",\"), but handling cases\n * where we have nested braced sections, which should be\n * treated as individual members, like {a,{b,c},d}\n */\nfunction parseCommaParts(str: string) {\n if (!str) {\n return ['']\n }\n\n const parts: string[] = []\n const m = balanced('{', '}', str)\n\n if (!m) {\n return str.split(',')\n }\n\n const { pre, body, post } = m\n const p = pre.split(',')\n\n p[p.length - 1] += '{' + body + '}'\n const postParts = parseCommaParts(post)\n if (post.length) {\n ;(p[p.length - 1] as string) += postParts.shift()\n p.push.apply(p, postParts)\n }\n\n parts.push.apply(parts, p)\n\n return parts\n}\n\nexport type BraceExpansionOptions = {\n max?: number\n maxLength?: number\n}\n\nexport function expand(str: string, options: BraceExpansionOptions = {}) {\n if (!str) {\n return []\n }\n\n const { max = EXPANSION_MAX, maxLength = EXPANSION_MAX_LENGTH } = options\n\n // I don't know why Bash 4.3 does this, but it does.\n // Anything starting with {} will have the first two bytes preserved\n // but *only* at the top level, so {},a}b will not expand to anything,\n // but a{},b}c will be expanded to [a}c,abc].\n // One could argue that this is a bug in Bash, but since the goal of\n // this module is to match Bash's rules, we escape a leading {}\n if (str.slice(0, 2) === '{}') {\n str = '\\\\{\\\\}' + str.slice(2)\n }\n\n return expand_(escapeBraces(str), max, maxLength, true).map(unescapeBraces)\n}\n\nfunction embrace(str: string) {\n return '{' + str + '}'\n}\n\nfunction isPadded(el: string) {\n return /^-?0\\d/.test(el)\n}\n\nfunction lte(i: number, y: number) {\n return i <= y\n}\n\nfunction gte(i: number, y: number) {\n return i >= y\n}\n\n// Build `{ acc[a] + pre + values[v] }` for every combination, capping the\n// number of results at `max` and the total number of characters at `maxLength`.\n// This is the one place output grows, so bounding it here keeps the single\n// accumulator - and therefore memory - flat regardless of how many brace groups\n// are combined (CVE-2026-14257).\nfunction combine(\n acc: string[],\n pre: string,\n values: string[],\n max: number,\n maxLength: number,\n dropEmpties: boolean,\n): string[] {\n const out: string[] = []\n let length = 0\n for (let a = 0; a < acc.length; a++) {\n for (let v = 0; v < values.length; v++) {\n if (out.length >= max) return out\n const expansion = (acc[a] as string) + pre + values[v]\n // Bash drops empty results at the top level. Skip them before they count\n // against `max`, so `max` bounds the number of *kept* results.\n if (dropEmpties && !expansion) continue\n if (length + expansion.length > maxLength) return out\n out.push(expansion)\n length += expansion.length\n }\n }\n return out\n}\n\n// The expansion values of a single numeric (`1..5`) or alphabetic (`a..e..2`)\n// sequence body.\nfunction expandSequence(\n body: string,\n isAlphaSequence: boolean,\n max: number,\n): string[] {\n const n = body.split(/\\.\\./)\n const N: string[] = []\n // A sequence body always splits into two or three parts, but the compiler\n // can't know that.\n /* c8 ignore start */\n if (n[0] === undefined || n[1] === undefined) {\n return N\n }\n /* c8 ignore stop */\n const x = numeric(n[0])\n const y = numeric(n[1])\n const width = Math.max(n[0].length, n[1].length)\n let incr =\n n.length === 3 && n[2] !== undefined ?\n Math.max(Math.abs(numeric(n[2])), 1)\n : 1\n let test = lte\n const reverse = y < x\n if (reverse) {\n incr *= -1\n test = gte\n }\n const pad = n.some(isPadded)\n\n for (let i = x; test(i, y) && N.length < max; i += incr) {\n let c\n if (isAlphaSequence) {\n c = String.fromCharCode(i)\n if (c === '\\\\') {\n c = ''\n }\n } else {\n c = String(i)\n if (pad) {\n const need = width - c.length\n if (need > 0) {\n const z = new Array(need + 1).join('0')\n if (i < 0) {\n c = '-' + z + c.slice(1)\n } else {\n c = z + c\n }\n }\n }\n }\n N.push(c)\n }\n return N\n}\n\nfunction expand_(\n str: string,\n max: number,\n maxLength: number,\n isTop: boolean,\n): string[] {\n // Consume the string's top-level brace groups left to right, threading a\n // running set of combined prefixes (`acc`). Expanding the tail iteratively -\n // rather than recursing on `m.post` once per group - keeps the native stack\n // depth constant, so deeply chained input (`'{a,b}'.repeat(3000)`) can no\n // longer overflow the stack, and leaves a single accumulator whose size\n // `maxLength` bounds directly (CVE-2026-14257).\n let acc: string[] = ['']\n\n // Bash drops empty results, but only when the *first* top-level group is a\n // comma set - a sequence like `{a..\\}` may legitimately yield ''. The drop\n // is on the final strings, so it is applied to whichever `combine` produces\n // them (the one with no brace set left in the tail).\n let dropEmpties = false\n let firstGroup = true\n\n for (;;) {\n const m = balanced('{', '}', str)\n\n // No brace set left: the rest of the string is literal.\n if (!m) {\n return combine(acc, str, [''], max, maxLength, dropEmpties)\n }\n\n // no need to expand pre, since it is guaranteed to be free of brace-sets\n const pre = m.pre\n\n if (/\\$$/.test(pre)) {\n acc = combine(\n acc,\n pre + '{' + m.body + '}',\n [''],\n max,\n maxLength,\n dropEmpties && !m.post.length,\n )\n firstGroup = false\n if (!m.post.length) break\n str = m.post\n continue\n }\n\n const isNumericSequence = /^-?\\d+\\.\\.-?\\d+(?:\\.\\.-?\\d+)?$/.test(m.body)\n const isAlphaSequence = /^[a-zA-Z]\\.\\.[a-zA-Z](?:\\.\\.-?\\d+)?$/.test(\n m.body,\n )\n const isSequence = isNumericSequence || isAlphaSequence\n const isOptions = m.body.indexOf(',') >= 0\n if (!isSequence && !isOptions) {\n // {a},b}\n if (m.post.match(/,(?!,).*\\}/)) {\n str = m.pre + '{' + m.body + escClose + m.post\n isTop = true\n continue\n }\n // Nothing here expands, so the whole remaining string is literal.\n return combine(\n acc,\n pre + '{' + m.body + '}' + m.post,\n [''],\n max,\n maxLength,\n dropEmpties,\n )\n }\n\n if (firstGroup) {\n dropEmpties = isTop && !isSequence\n firstGroup = false\n }\n\n let values: string[]\n if (isSequence) {\n values = expandSequence(m.body, isAlphaSequence, max)\n } else {\n let n = parseCommaParts(m.body)\n if (n.length === 1 && n[0] !== undefined) {\n // x{{a,b}}y ==> x{a}y x{b}y\n n = expand_(n[0], max, maxLength, false).map(embrace)\n //XXX is this necessary? Can't seem to hit it in tests.\n /* c8 ignore start */\n if (n.length === 1) {\n acc = combine(\n acc,\n pre + n[0],\n [''],\n max,\n maxLength,\n dropEmpties && !m.post.length,\n )\n if (!m.post.length) break\n str = m.post\n continue\n }\n /* c8 ignore stop */\n }\n\n values = []\n for (let j = 0; j < n.length; j++) {\n values.push.apply(values, expand_(n[j] as string, max, maxLength, false))\n }\n }\n\n acc = combine(acc, pre, values, max, maxLength, dropEmpties && !m.post.length)\n if (!m.post.length) break\n str = m.post\n }\n\n return acc\n}\n"]}
@@ -1,6 +1,8 @@
1
1
  export declare const EXPANSION_MAX = 100000;
2
+ export declare const EXPANSION_MAX_LENGTH = 4000000;
2
3
  export type BraceExpansionOptions = {
3
4
  max?: number;
5
+ maxLength?: number;
4
6
  };
5
7
  export declare function expand(str: string, options?: BraceExpansionOptions): string[];
6
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAkBA,eAAO,MAAM,aAAa,SAAU,CAAA;AAwDpC,MAAM,MAAM,qBAAqB,GAAG;IAClC,GAAG,CAAC,EAAE,MAAM,CAAA;CACb,CAAA;AAED,wBAAgB,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,qBAA0B,YAkBtE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAkBA,eAAO,MAAM,aAAa,SAAU,CAAA;AAYpC,eAAO,MAAM,oBAAoB,UAAY,CAAA;AAwD7C,MAAM,MAAM,qBAAqB,GAAG;IAClC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,wBAAgB,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,qBAA0B,YAkBtE"}
@@ -15,6 +15,17 @@ const closePattern = /\\}/g;
15
15
  const commaPattern = /\\,/g;
16
16
  const periodPattern = /\\\./g;
17
17
  export const EXPANSION_MAX = 100_000;
18
+ // `EXPANSION_MAX` caps the *number* of expansions, but not their length. An
19
+ // input like `'{a,b}'.repeat(1500)` stays under that count - its output is
20
+ // truncated to 100k results - while making every result ~1500 characters
21
+ // long. The result set, and the intermediate arrays built while combining
22
+ // brace sets, then grow large enough to exhaust memory and crash the process
23
+ // (CVE-2026-14257). `EXPANSION_MAX_LENGTH` bounds the total number of
24
+ // characters the accumulator may hold at any point, so memory stays flat no
25
+ // matter how many brace groups are chained. The limit sits well above any
26
+ // realistic expansion (100k results hitting `EXPANSION_MAX` measure ~1M
27
+ // characters) so legitimate input is unaffected.
28
+ export const EXPANSION_MAX_LENGTH = 4_000_000;
18
29
  function numeric(str) {
19
30
  return !isNaN(str) ? parseInt(str, 10) : str.charCodeAt(0);
20
31
  }
@@ -64,7 +75,7 @@ export function expand(str, options = {}) {
64
75
  if (!str) {
65
76
  return [];
66
77
  }
67
- const { max = EXPANSION_MAX } = options;
78
+ const { max = EXPANSION_MAX, maxLength = EXPANSION_MAX_LENGTH } = options;
68
79
  // I don't know why Bash 4.3 does this, but it does.
69
80
  // Anything starting with {} will have the first two bytes preserved
70
81
  // but *only* at the top level, so {},a}b will not expand to anything,
@@ -74,7 +85,7 @@ export function expand(str, options = {}) {
74
85
  if (str.slice(0, 2) === '{}') {
75
86
  str = '\\{\\}' + str.slice(2);
76
87
  }
77
- return expand_(escapeBraces(str), max, true).map(unescapeBraces);
88
+ return expand_(escapeBraces(str), max, maxLength, true).map(unescapeBraces);
78
89
  }
79
90
  function embrace(str) {
80
91
  return '{' + str + '}';
@@ -88,25 +99,112 @@ function lte(i, y) {
88
99
  function gte(i, y) {
89
100
  return i >= y;
90
101
  }
91
- function expand_(str, max, isTop) {
92
- /** @type {string[]} */
93
- const expansions = [];
94
- // The `{a},b}` rewrite below restarts expansion on a rewritten string with
95
- // the same `max` and `isTop = true`. Loop instead of recursing so a long run
96
- // of non-expanding `{}` groups can't exhaust the call stack.
102
+ // Build `{ acc[a] + pre + values[v] }` for every combination, capping the
103
+ // number of results at `max` and the total number of characters at `maxLength`.
104
+ // This is the one place output grows, so bounding it here keeps the single
105
+ // accumulator - and therefore memory - flat regardless of how many brace groups
106
+ // are combined (CVE-2026-14257).
107
+ function combine(acc, pre, values, max, maxLength, dropEmpties) {
108
+ const out = [];
109
+ let length = 0;
110
+ for (let a = 0; a < acc.length; a++) {
111
+ for (let v = 0; v < values.length; v++) {
112
+ if (out.length >= max)
113
+ return out;
114
+ const expansion = acc[a] + pre + values[v];
115
+ // Bash drops empty results at the top level. Skip them before they count
116
+ // against `max`, so `max` bounds the number of *kept* results.
117
+ if (dropEmpties && !expansion)
118
+ continue;
119
+ if (length + expansion.length > maxLength)
120
+ return out;
121
+ out.push(expansion);
122
+ length += expansion.length;
123
+ }
124
+ }
125
+ return out;
126
+ }
127
+ // The expansion values of a single numeric (`1..5`) or alphabetic (`a..e..2`)
128
+ // sequence body.
129
+ function expandSequence(body, isAlphaSequence, max) {
130
+ const n = body.split(/\.\./);
131
+ const N = [];
132
+ // A sequence body always splits into two or three parts, but the compiler
133
+ // can't know that.
134
+ /* c8 ignore start */
135
+ if (n[0] === undefined || n[1] === undefined) {
136
+ return N;
137
+ }
138
+ /* c8 ignore stop */
139
+ const x = numeric(n[0]);
140
+ const y = numeric(n[1]);
141
+ const width = Math.max(n[0].length, n[1].length);
142
+ let incr = n.length === 3 && n[2] !== undefined ?
143
+ Math.max(Math.abs(numeric(n[2])), 1)
144
+ : 1;
145
+ let test = lte;
146
+ const reverse = y < x;
147
+ if (reverse) {
148
+ incr *= -1;
149
+ test = gte;
150
+ }
151
+ const pad = n.some(isPadded);
152
+ for (let i = x; test(i, y) && N.length < max; i += incr) {
153
+ let c;
154
+ if (isAlphaSequence) {
155
+ c = String.fromCharCode(i);
156
+ if (c === '\\') {
157
+ c = '';
158
+ }
159
+ }
160
+ else {
161
+ c = String(i);
162
+ if (pad) {
163
+ const need = width - c.length;
164
+ if (need > 0) {
165
+ const z = new Array(need + 1).join('0');
166
+ if (i < 0) {
167
+ c = '-' + z + c.slice(1);
168
+ }
169
+ else {
170
+ c = z + c;
171
+ }
172
+ }
173
+ }
174
+ }
175
+ N.push(c);
176
+ }
177
+ return N;
178
+ }
179
+ function expand_(str, max, maxLength, isTop) {
180
+ // Consume the string's top-level brace groups left to right, threading a
181
+ // running set of combined prefixes (`acc`). Expanding the tail iteratively -
182
+ // rather than recursing on `m.post` once per group - keeps the native stack
183
+ // depth constant, so deeply chained input (`'{a,b}'.repeat(3000)`) can no
184
+ // longer overflow the stack, and leaves a single accumulator whose size
185
+ // `maxLength` bounds directly (CVE-2026-14257).
186
+ let acc = [''];
187
+ // Bash drops empty results, but only when the *first* top-level group is a
188
+ // comma set - a sequence like `{a..\}` may legitimately yield ''. The drop
189
+ // is on the final strings, so it is applied to whichever `combine` produces
190
+ // them (the one with no brace set left in the tail).
191
+ let dropEmpties = false;
192
+ let firstGroup = true;
97
193
  for (;;) {
98
194
  const m = balanced('{', '}', str);
99
- if (!m)
100
- return [str];
195
+ // No brace set left: the rest of the string is literal.
196
+ if (!m) {
197
+ return combine(acc, str, [''], max, maxLength, dropEmpties);
198
+ }
101
199
  // no need to expand pre, since it is guaranteed to be free of brace-sets
102
200
  const pre = m.pre;
103
- if (/\$$/.test(m.pre)) {
104
- const post = m.post.length ? expand_(m.post, max, false) : [''];
105
- for (let k = 0; k < post.length && k < max; k++) {
106
- const expansion = pre + '{' + m.body + '}' + post[k];
107
- expansions.push(expansion);
108
- }
109
- return expansions;
201
+ if (/\$$/.test(pre)) {
202
+ acc = combine(acc, pre + '{' + m.body + '}', [''], max, maxLength, dropEmpties && !m.post.length);
203
+ firstGroup = false;
204
+ if (!m.post.length)
205
+ break;
206
+ str = m.post;
207
+ continue;
110
208
  }
111
209
  const isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body);
112
210
  const isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body);
@@ -119,89 +217,43 @@ function expand_(str, max, isTop) {
119
217
  isTop = true;
120
218
  continue;
121
219
  }
122
- return [str];
220
+ // Nothing here expands, so the whole remaining string is literal.
221
+ return combine(acc, pre + '{' + m.body + '}' + m.post, [''], max, maxLength, dropEmpties);
123
222
  }
124
- // Only expand post once we know this brace set actually expands. Computing
125
- // it before the early returns above expanded post a second time on every
126
- // non-expanding `{}`, which is what made inputs like `a{},{},{}...` blow up
127
- // exponentially.
128
- const post = m.post.length ? expand_(m.post, max, false) : [''];
129
- let n;
223
+ if (firstGroup) {
224
+ dropEmpties = isTop && !isSequence;
225
+ firstGroup = false;
226
+ }
227
+ let values;
130
228
  if (isSequence) {
131
- n = m.body.split(/\.\./);
229
+ values = expandSequence(m.body, isAlphaSequence, max);
132
230
  }
133
231
  else {
134
- n = parseCommaParts(m.body);
232
+ let n = parseCommaParts(m.body);
135
233
  if (n.length === 1 && n[0] !== undefined) {
136
234
  // x{{a,b}}y ==> x{a}y x{b}y
137
- n = expand_(n[0], max, false).map(embrace);
235
+ n = expand_(n[0], max, maxLength, false).map(embrace);
138
236
  //XXX is this necessary? Can't seem to hit it in tests.
139
237
  /* c8 ignore start */
140
238
  if (n.length === 1) {
141
- return post.map(p => m.pre + n[0] + p);
239
+ acc = combine(acc, pre + n[0], [''], max, maxLength, dropEmpties && !m.post.length);
240
+ if (!m.post.length)
241
+ break;
242
+ str = m.post;
243
+ continue;
142
244
  }
143
245
  /* c8 ignore stop */
144
246
  }
145
- }
146
- // at this point, n is the parts, and we know it's not a comma set
147
- // with a single entry.
148
- let N;
149
- if (isSequence && n[0] !== undefined && n[1] !== undefined) {
150
- const x = numeric(n[0]);
151
- const y = numeric(n[1]);
152
- const width = Math.max(n[0].length, n[1].length);
153
- let incr = n.length === 3 && n[2] !== undefined ?
154
- Math.max(Math.abs(numeric(n[2])), 1)
155
- : 1;
156
- let test = lte;
157
- const reverse = y < x;
158
- if (reverse) {
159
- incr *= -1;
160
- test = gte;
161
- }
162
- const pad = n.some(isPadded);
163
- N = [];
164
- for (let i = x; test(i, y) && N.length < max; i += incr) {
165
- let c;
166
- if (isAlphaSequence) {
167
- c = String.fromCharCode(i);
168
- if (c === '\\') {
169
- c = '';
170
- }
171
- }
172
- else {
173
- c = String(i);
174
- if (pad) {
175
- const need = width - c.length;
176
- if (need > 0) {
177
- const z = new Array(need + 1).join('0');
178
- if (i < 0) {
179
- c = '-' + z + c.slice(1);
180
- }
181
- else {
182
- c = z + c;
183
- }
184
- }
185
- }
186
- }
187
- N.push(c);
188
- }
189
- }
190
- else {
191
- N = [];
247
+ values = [];
192
248
  for (let j = 0; j < n.length; j++) {
193
- N.push.apply(N, expand_(n[j], max, false));
194
- }
195
- }
196
- for (let j = 0; j < N.length; j++) {
197
- for (let k = 0; k < post.length && expansions.length < max; k++) {
198
- const expansion = pre + N[j] + post[k];
199
- if (!isTop || isSequence || expansion) {
200
- expansions.push(expansion);
201
- }
249
+ values.push.apply(values, expand_(n[j], max, maxLength, false));
202
250
  }
203
251
  }
204
- return expansions;
252
+ acc = combine(acc, pre, values, max, maxLength, dropEmpties && !m.post.length);
253
+ if (!m.post.length)
254
+ break;
255
+ str = m.post;
205
256
  }
257
+ return acc;
206
258
  }
207
259
  //# sourceMappingURL=index.js.map