Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
28f2168
Bump minimatch from 3.1.2 to 3.1.5 (#1281)
dependabot[bot] Mar 9, 2026
c8813ba
Upgrade @actions dependencies and update licenses (#1303)
Copilot Apr 15, 2026
fa8bde1
Bump @actions/cache to 5.1.0, log cache write denied
jasongin Jun 18, 2026
6f4b74b
Strict equality
jasongin Jun 18, 2026
dc6eab6
Update dist
jasongin Jun 18, 2026
0cb1a84
Add RHEL support and include Linux distro in cache keys (#1323)
v-priyagupta108 Jun 18, 2026
6731c2b
Resolve high-severity audit issues
jasongin Jun 19, 2026
8f639b1
Merge pull request #1324 from jasongin/update-actions-cache-5.1.0
Link- Jun 23, 2026
d2b357a
Update dependency versions and test workflow configuration (#1322)
v-HarithaVattikuti Jun 23, 2026
1d18d7a
Update advanced-usage.md (#811)
Dunky-Z Jun 23, 2026
ece7cb0
Fix pip cache error handling on Windows. (#1040)
v-priyagupta108 Jun 23, 2026
0903b46
Bump certifi from 2020.6.20 to 2024.7.4 in /__tests__/data (#1328)
dependabot[bot] Jul 10, 2026
6849080
remove EOL Python versions and Bumps numpy text fixture (#1333)
v-priya-kinthali Jul 13, 2026
c709277
Annotation code fix (#1335)
v-lmvysakh Jul 13, 2026
54baeea
Validate and retry manifest fetch to prevent silent failures (#1332)
v-priyagupta108 Jul 14, 2026
f8cf429
Migrate to ESM and upgrade dependencies (#1330)
v-priyagupta108 Jul 15, 2026
0f3a009
Remove the pip-install input (#1336)
v-gowridurgad Jul 16, 2026
4ab7e95
Merge pull request #1337 from actions/philip-gai/bump-actions-cache-6…
philip-gai Jul 16, 2026
5fda3b9
Pin SHA commits and update docs with latest versions (#1338)
v-HarithaVattikuti Jul 20, 2026
39c6069
Sync upstream v7 and move to @warpbuilds/cache 2.x
janakiram7755 Jul 30, 2026
a4ce695
Pin brace-expansion to clear the high-severity audit finding
janakiram7755 Jul 30, 2026
87ec11d
Move macOS CI to warp-macos-15 runners
janakiram7755 Aug 3, 2026
8f0c60b
Move to @warpbuilds/cache 2.0.0-1
janakiram7755 Aug 3, 2026
541f7ab
Regenerate license manifests for 2.0.0-1
janakiram7755 Aug 3, 2026
2f77716
Address zizmor findings and drop the redundant brace-expansion override
janakiram7755 Aug 3, 2026
69aad45
Move to @warpbuilds/cache 2.0.0
janakiram7755 Aug 7, 2026
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
Prev Previous commit
Next Next commit
Pin brace-expansion to clear the high-severity audit finding
  • Loading branch information
janakiram7755 committed Jul 30, 2026
commit a4ce695b13ebf296bb1d62c0770621455184c9ea
2 changes: 1 addition & 1 deletion .licenses/npm/brace-expansion.dep.yml

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

222 changes: 142 additions & 80 deletions dist/cache-save/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -69596,6 +69596,17 @@ const closePattern = /\\}/g;
const commaPattern = /\\,/g;
const periodPattern = /\\\./g;
const EXPANSION_MAX = 100_000;
// `EXPANSION_MAX` caps the *number* of expansions, but not their length. An
// input like `'{a,b}'.repeat(1500)` stays under that count - its output is
// truncated to 100k results - while making every result ~1500 characters
// long. The result set, and the intermediate arrays built while combining
// brace sets, then grow large enough to exhaust memory and crash the process
// (CVE-2026-14257). `EXPANSION_MAX_LENGTH` bounds the total number of
// characters the accumulator may hold at any point, so memory stays flat no
// matter how many brace groups are chained. The limit sits well above any
// realistic expansion (100k results hitting `EXPANSION_MAX` measure ~1M
// characters) so legitimate input is unaffected.
const EXPANSION_MAX_LENGTH = 4_000_000;
function numeric(str) {
return !isNaN(str) ? parseInt(str, 10) : str.charCodeAt(0);
}
Expand Down Expand Up @@ -69645,7 +69656,7 @@ function expand(str, options = {}) {
if (!str) {
return [];
}
const { max = EXPANSION_MAX } = options;
const { max = EXPANSION_MAX, maxLength = EXPANSION_MAX_LENGTH } = options;
// I don't know why Bash 4.3 does this, but it does.
// Anything starting with {} will have the first two bytes preserved
// but *only* at the top level, so {},a}b will not expand to anything,
Expand All @@ -69655,7 +69666,7 @@ function expand(str, options = {}) {
if (str.slice(0, 2) === '{}') {
str = '\\{\\}' + str.slice(2);
}
return expand_(escapeBraces(str), max, true).map(unescapeBraces);
return expand_(escapeBraces(str), max, maxLength, true).map(unescapeBraces);
}
function embrace(str) {
return '{' + str + '}';
Expand All @@ -69669,22 +69680,113 @@ function lte(i, y) {
function gte(i, y) {
return i >= y;
}
function expand_(str, max, isTop) {
/** @type {string[]} */
const expansions = [];
const m = balanced('{', '}', str);
if (!m)
return [str];
// no need to expand pre, since it is guaranteed to be free of brace-sets
const pre = m.pre;
const post = m.post.length ? expand_(m.post, max, false) : [''];
if (/\$$/.test(m.pre)) {
for (let k = 0; k < post.length && k < max; k++) {
const expansion = pre + '{' + m.body + '}' + post[k];
expansions.push(expansion);
// Build `{ acc[a] + pre + values[v] }` for every combination, capping the
// number of results at `max` and the total number of characters at `maxLength`.
// This is the one place output grows, so bounding it here keeps the single
// accumulator - and therefore memory - flat regardless of how many brace groups
// are combined (CVE-2026-14257).
function combine(acc, pre, values, max, maxLength, dropEmpties) {
const out = [];
let length = 0;
for (let a = 0; a < acc.length; a++) {
for (let v = 0; v < values.length; v++) {
if (out.length >= max)
return out;
const expansion = acc[a] + pre + values[v];
// Bash drops empty results at the top level. Skip them before they count
// against `max`, so `max` bounds the number of *kept* results.
if (dropEmpties && !expansion)
continue;
if (length + expansion.length > maxLength)
return out;
out.push(expansion);
length += expansion.length;
}
}
else {
return out;
}
// The expansion values of a single numeric (`1..5`) or alphabetic (`a..e..2`)
// sequence body.
function expandSequence(body, isAlphaSequence, max) {
const n = body.split(/\.\./);
const N = [];
// A sequence body always splits into two or three parts, but the compiler
// can't know that.
/* c8 ignore start */
if (n[0] === undefined || n[1] === undefined) {
return N;
}
/* c8 ignore stop */
const x = numeric(n[0]);
const y = numeric(n[1]);
const width = Math.max(n[0].length, n[1].length);
let incr = n.length === 3 && n[2] !== undefined ?
Math.max(Math.abs(numeric(n[2])), 1)
: 1;
let test = lte;
const reverse = y < x;
if (reverse) {
incr *= -1;
test = gte;
}
const pad = n.some(isPadded);
for (let i = x; test(i, y) && N.length < max; i += incr) {
let c;
if (isAlphaSequence) {
c = String.fromCharCode(i);
if (c === '\\') {
c = '';
}
}
else {
c = String(i);
if (pad) {
const need = width - c.length;
if (need > 0) {
const z = new Array(need + 1).join('0');
if (i < 0) {
c = '-' + z + c.slice(1);
}
else {
c = z + c;
}
}
}
}
N.push(c);
}
return N;
}
function expand_(str, max, maxLength, isTop) {
// Consume the string's top-level brace groups left to right, threading a
// running set of combined prefixes (`acc`). Expanding the tail iteratively -
// rather than recursing on `m.post` once per group - keeps the native stack
// depth constant, so deeply chained input (`'{a,b}'.repeat(3000)`) can no
// longer overflow the stack, and leaves a single accumulator whose size
// `maxLength` bounds directly (CVE-2026-14257).
let acc = [''];
// Bash drops empty results, but only when the *first* top-level group is a
// comma set - a sequence like `{a..\}` may legitimately yield ''. The drop
// is on the final strings, so it is applied to whichever `combine` produces
// them (the one with no brace set left in the tail).
let dropEmpties = false;
let firstGroup = true;
for (;;) {
const m = balanced('{', '}', str);
// No brace set left: the rest of the string is literal.
if (!m) {
return combine(acc, str, [''], max, maxLength, dropEmpties);
}
// no need to expand pre, since it is guaranteed to be free of brace-sets
const pre = m.pre;
if (/\$$/.test(pre)) {
acc = combine(acc, pre + '{' + m.body + '}', [''], max, maxLength, dropEmpties && !m.post.length);
firstGroup = false;
if (!m.post.length)
break;
str = m.post;
continue;
}
const isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body);
const isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body);
const isSequence = isNumericSequence || isAlphaSequence;
Expand All @@ -69693,87 +69795,47 @@ function expand_(str, max, isTop) {
// {a},b}
if (m.post.match(/,(?!,).*\}/)) {
str = m.pre + '{' + m.body + escClose + m.post;
return expand_(str, max, true);
isTop = true;
continue;
}
return [str];
// Nothing here expands, so the whole remaining string is literal.
return combine(acc, pre + '{' + m.body + '}' + m.post, [''], max, maxLength, dropEmpties);
}
let n;
if (firstGroup) {
dropEmpties = isTop && !isSequence;
firstGroup = false;
}
let values;
if (isSequence) {
n = m.body.split(/\.\./);
values = expandSequence(m.body, isAlphaSequence, max);
}
else {
n = parseCommaParts(m.body);
let n = parseCommaParts(m.body);
if (n.length === 1 && n[0] !== undefined) {
// x{{a,b}}y ==> x{a}y x{b}y
n = expand_(n[0], max, false).map(embrace);
n = expand_(n[0], max, maxLength, false).map(embrace);
//XXX is this necessary? Can't seem to hit it in tests.
/* c8 ignore start */
if (n.length === 1) {
return post.map(p => m.pre + n[0] + p);
acc = combine(acc, pre + n[0], [''], max, maxLength, dropEmpties && !m.post.length);
if (!m.post.length)
break;
str = m.post;
continue;
}
/* c8 ignore stop */
}
}
// at this point, n is the parts, and we know it's not a comma set
// with a single entry.
let N;
if (isSequence && n[0] !== undefined && n[1] !== undefined) {
const x = numeric(n[0]);
const y = numeric(n[1]);
const width = Math.max(n[0].length, n[1].length);
let incr = n.length === 3 && n[2] !== undefined ?
Math.max(Math.abs(numeric(n[2])), 1)
: 1;
let test = lte;
const reverse = y < x;
if (reverse) {
incr *= -1;
test = gte;
}
const pad = n.some(isPadded);
N = [];
for (let i = x; test(i, y) && N.length < max; i += incr) {
let c;
if (isAlphaSequence) {
c = String.fromCharCode(i);
if (c === '\\') {
c = '';
}
}
else {
c = String(i);
if (pad) {
const need = width - c.length;
if (need > 0) {
const z = new Array(need + 1).join('0');
if (i < 0) {
c = '-' + z + c.slice(1);
}
else {
c = z + c;
}
}
}
}
N.push(c);
}
}
else {
N = [];
values = [];
for (let j = 0; j < n.length; j++) {
N.push.apply(N, expand_(n[j], max, false));
}
}
for (let j = 0; j < N.length; j++) {
for (let k = 0; k < post.length && expansions.length < max; k++) {
const expansion = pre + N[j] + post[k];
if (!isTop || isSequence || expansion) {
expansions.push(expansion);
}
values.push.apply(values, expand_(n[j], max, maxLength, false));
}
}
acc = combine(acc, pre, values, max, maxLength, dropEmpties && !m.post.length);
if (!m.post.length)
break;
str = m.post;
}
return expansions;
return acc;
}
//# sourceMappingURL=index.js.map
;// CONCATENATED MODULE: ./node_modules/minimatch/dist/esm/assert-valid-pattern.js
Expand Down
Loading
Loading