@cypress/github-action 7.4.5 → 7.4.6

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 (2) hide show
  1. package/dist/index.js +78 -22
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -14465,6 +14465,26 @@ var EXPANSION_MAX = 100000
14465
14465
  // characters) so legitimate input is unaffected.
14466
14466
  var EXPANSION_MAX_LENGTH = 4000000
14467
14467
 
14468
+ // `expand` recurses once per level of brace *nesting* - both when expanding a
14469
+ // set's comma members and when re-wrapping a set whose body is a single part.
14470
+ // The CVE-2026-14257 fix made the *tail* iterative (recursion on `m.post`, one
14471
+ // level per chained group), which left nesting depth unbounded: about 3,100
14472
+ // levels of `{{{...a,b...}}}` - only ~6KB of input - exhausted the native stack
14473
+ // and crashed the process. `EXPANSION_MAX_DEPTH` bounds how deep the parser
14474
+ // will follow nesting. It sits far above any realistic pattern and well below
14475
+ // the depth at which the stack runs out.
14476
+ var EXPANSION_MAX_DEPTH = 1000
14477
+
14478
+ // Bash keeps a quirk where a brace group followed by a comma set still expands
14479
+ // (`{a},b}`). The parser implements it by rewriting the string and restarting
14480
+ // the scan, absorbing one `}` per pass. `n` trailing braces therefore cost `n`
14481
+ // full passes over a string that itself grows by one `escClose` sentinel each
14482
+ // time - quadratic in `n`, with a ~26x constant from the sentinel's length.
14483
+ // 128KB of `'{a}' + '}'.repeat(n) + ',z}'` blocked the event loop for 27
14484
+ // seconds to produce two results. `EXPANSION_MAX_REWRITES` bounds how many
14485
+ // times the scan may restart. Real `{a},b}` input needs a handful.
14486
+ var EXPANSION_MAX_REWRITES = 1000
14487
+
14468
14488
  function numeric(str) {
14469
14489
  return parseInt(str, 10) == str
14470
14490
  ? parseInt(str, 10)
@@ -14488,34 +14508,54 @@ function unescapeBraces(str) {
14488
14508
  }
14489
14509
 
14490
14510
 
14511
+ // Like `target.push(...items)` but doesn't overflow the stack
14512
+ function pushAll(target, items) {
14513
+ for (var i = 0; i < items.length; i++) {
14514
+ target.push(items[i]);
14515
+ }
14516
+ }
14517
+
14491
14518
  // Basically just str.split(","), but handling cases
14492
14519
  // where we have nested braced sections, which should be
14493
14520
  // treated as individual members, like {a,{b,c},d}
14494
14521
  function parseCommaParts(str) {
14495
- if (!str)
14496
- return [''];
14497
-
14498
14522
  var parts = [];
14499
- var m = balanced('{', '}', str);
14500
14523
 
14501
- if (!m)
14502
- return str.split(',');
14524
+ // Walk the brace groups iteratively. Recursing on `post` once per group let a
14525
+ // chain of them exhaust the stack - the parsing-side counterpart to
14526
+ // the `expand` overflow fixed for CVE-2026-14257, and not something `max` or
14527
+ // `maxLength` can bound, since it happens before expansion.
14528
+ //
14529
+ // The part the next chunk continues
14530
+ var carry = '';
14503
14531
 
14504
- var pre = m.pre;
14505
- var body = m.body;
14506
- var post = m.post;
14507
- var p = pre.split(',');
14532
+ for (;;) {
14533
+ var m = balanced('{', '}', str);
14508
14534
 
14509
- p[p.length-1] += '{' + body + '}';
14510
- var postParts = parseCommaParts(post);
14511
- if (post.length) {
14512
- p[p.length-1] += postParts.shift();
14513
- p.push.apply(p, postParts);
14514
- }
14535
+ if (!m) {
14536
+ var tail = str.split(',');
14537
+ tail[0] = carry + tail[0];
14538
+ pushAll(parts, tail);
14539
+ return parts;
14540
+ }
14515
14541
 
14516
- parts.push.apply(parts, p);
14542
+ var pre = m.pre;
14543
+ var body = m.body;
14544
+ var post = m.post;
14545
+ var p = pre.split(',');
14517
14546
 
14518
- return parts;
14547
+ p[0] = carry + p[0];
14548
+ p[p.length-1] += '{' + body + '}';
14549
+
14550
+ if (!post.length) {
14551
+ pushAll(parts, p);
14552
+ return parts;
14553
+ }
14554
+
14555
+ carry = p.pop();
14556
+ pushAll(parts, p);
14557
+ str = post;
14558
+ }
14519
14559
  }
14520
14560
 
14521
14561
  function expandTop(str, options) {
@@ -14525,6 +14565,8 @@ function expandTop(str, options) {
14525
14565
  options = options || {};
14526
14566
  var max = options.max == null ? EXPANSION_MAX : options.max;
14527
14567
  var maxLength = options.maxLength == null ? EXPANSION_MAX_LENGTH : options.maxLength;
14568
+ var maxDepth = options.maxDepth == null ? EXPANSION_MAX_DEPTH : options.maxDepth;
14569
+ var maxRewrites = options.maxRewrites == null ? EXPANSION_MAX_REWRITES : options.maxRewrites;
14528
14570
 
14529
14571
  // I don't know why Bash 4.3 does this, but it does.
14530
14572
  // Anything starting with {} will have the first two bytes preserved
@@ -14536,7 +14578,7 @@ function expandTop(str, options) {
14536
14578
  str = '\\{\\}' + str.substr(2);
14537
14579
  }
14538
14580
 
14539
- return expand(escapeBraces(str), max, maxLength, true).map(unescapeBraces);
14581
+ return expand(escapeBraces(str), max, maxLength, maxDepth, 0, maxRewrites, true).map(unescapeBraces);
14540
14582
  }
14541
14583
 
14542
14584
  function identity(e) {
@@ -14660,8 +14702,18 @@ function expand(
14660
14702
  str,
14661
14703
  max,
14662
14704
  maxLength,
14705
+ maxDepth,
14706
+ depth,
14707
+ maxRewrites,
14663
14708
  isTop
14664
14709
  ) {
14710
+ // Too deeply nested to keep following: treat the rest as literal, the same
14711
+ // way a group that cannot expand is already handled. Truncating rather than
14712
+ // throwing keeps expansion total, matching `max` and `maxLength`.
14713
+ if (depth > maxDepth) {
14714
+ return [str];
14715
+ }
14716
+
14665
14717
  // Consume the string's top-level brace groups left to right, threading a
14666
14718
  // running set of combined prefixes (`acc`). Expanding the tail iteratively -
14667
14719
  // rather than recursing on `m.post` once per group - keeps the native stack
@@ -14682,6 +14734,9 @@ function expand(
14682
14734
  // `accBase[a]` records how much of `acc[a]` predates the current run;
14683
14735
  // `combine` treats an expansion as empty when it adds nothing past that.
14684
14736
  var accBase = [0]
14737
+ // How many times the `{a},b}` rewrite below has restarted the scan. Each pass
14738
+ // re-reads the whole string, so leaving this unbounded is quadratic.
14739
+ var rewrites = 0
14685
14740
  var dropEmpties = false
14686
14741
  var firstGroup = true
14687
14742
  var nextBase
@@ -14713,7 +14768,8 @@ function expand(
14713
14768
  var isOptions = m.body.indexOf(',') >= 0;
14714
14769
  if (!isSequence && !isOptions) {
14715
14770
  // {a},b}
14716
- if (m.post.match(/,(?!,).*\}/)) {
14771
+ if (rewrites < maxRewrites && m.post.match(/,(?!,).*\}/)) {
14772
+ rewrites++;
14717
14773
  str = m.pre + '{' + m.body + escClose + m.post;
14718
14774
  // The rewritten string is expanded as if it were a fresh top-level one,
14719
14775
  // so start a new empty-drop run: anchor the baseline at what `acc`
@@ -14752,7 +14808,7 @@ function expand(
14752
14808
  var n = parseCommaParts(m.body);
14753
14809
  if (n.length === 1 && n[0] !== undefined) {
14754
14810
  // x{{a,b}}y ==> x{a}y x{b}y
14755
- n = expand(n[0], max, maxLength, false).map(embrace);
14811
+ n = expand(n[0], max, maxLength, maxDepth, depth + 1, maxRewrites, false).map(embrace);
14756
14812
  //XXX is this necessary? Can't seem to hit it in tests.
14757
14813
  /* c8 ignore start */
14758
14814
  if (n.length === 1) {
@@ -14791,7 +14847,7 @@ function expand(
14791
14847
  values = []
14792
14848
  var valuesLength = 0
14793
14849
  outer: for (var j = 0; j < n.length; j++) {
14794
- var expanded = expand(n[j], max, maxLength, false)
14850
+ var expanded = expand(n[j], max, maxLength, maxDepth, depth + 1, maxRewrites, false)
14795
14851
  for (var k = 0; k < expanded.length; k++) {
14796
14852
  var v = expanded[k]
14797
14853
  if (dropsEmpties && !v) continue
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cypress/github-action",
3
- "version": "7.4.5",
3
+ "version": "7.4.6",
4
4
  "description": "GitHub Action for running Cypress end-to-end and component tests",
5
5
  "private": false,
6
6
  "main": "dist/index.js",
@@ -62,7 +62,7 @@
62
62
  "globals": "17.11.0",
63
63
  "husky": "9.1.7",
64
64
  "markdown-link-check": "3.15.0",
65
- "prettier": "3.9.8"
65
+ "prettier": "3.9.9"
66
66
  },
67
67
  "publishConfig": {
68
68
  "access": "public"