Ignore:
Timestamp:
09/19/26 10:30:30 (11 days ago)
Author:
Klimentina Efremova <klimentina08642@…>
Branches:
finki-main, main
Children:
06ebe74
Parents:
62b2964
Message:

Turned database from SQLite to PostgressSQL, updated database changes from Phase 1 and 2

File:
1 edited

Legend:

Unmodified
Added
Removed
  • node_modules/brace-expansion/dist/commonjs/index.js

    r62b2964 r33517cc  
    11"use strict";
    22Object.defineProperty(exports, "__esModule", { value: true });
    3 exports.EXPANSION_MAX = void 0;
     3exports.EXPANSION_MAX_REWRITES = exports.EXPANSION_MAX_DEPTH = exports.EXPANSION_MAX_LENGTH = exports.EXPANSION_MAX = void 0;
    44exports.expand = expand;
    55const balanced_match_1 = require("balanced-match");
    … …  
    1818const closePattern = /\\}/g;
    1919const commaPattern = /\\,/g;
    20 const periodPattern = /\\./g;
     20const periodPattern = /\\\./g;
    2121exports.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.
     32exports.EXPANSION_MAX_LENGTH = 4_000_000;
     33// `expand_` recurses once per level of brace *nesting* - both when expanding a
     34// set's comma members and when re-wrapping a set whose body is a single part.
     35// The CVE-2026-14257 fix made the *tail* iterative (recursion on `m.post`, one
     36// level per chained group), which left nesting depth unbounded: about 3,100
     37// levels of `{{{...a,b...}}}` - only ~6KB of input - exhausted the native stack
     38// and crashed the process. `EXPANSION_MAX_DEPTH` bounds how deep the parser
     39// will follow nesting. It sits far above any realistic pattern and well below
     40// the depth at which the stack runs out.
     41exports.EXPANSION_MAX_DEPTH = 1_000;
     42// Bash keeps a quirk where a brace group followed by a comma set still expands
     43// (`{a},b}`). The parser implements it by rewriting the string and restarting
     44// the scan, absorbing one `}` per pass. `n` trailing braces therefore cost `n`
     45// full passes over a string that itself grows by one `escClose` sentinel each
     46// time - quadratic in `n`, with a ~26x constant from the sentinel's length.
     47// 128KB of `'{a}' + '}'.repeat(n) + ',z}'` blocked the event loop for 27
     48// seconds to produce two results. `EXPANSION_MAX_REWRITES` bounds how many
     49// times the scan may restart. Real `{a},b}` input needs a handful.
     50exports.EXPANSION_MAX_REWRITES = 1_000;
    2251function numeric(str) {
    2352    return !isNaN(str) ? parseInt(str, 10) : str.charCodeAt(0);
    … …  
    3968        .replace(escPeriodPattern, '.');
    4069}
     70// Like `target.push(...items)` but doesn't overflow the stack
     71function pushAll(target, items) {
     72    for (let i = 0; i < items.length; i++) {
     73        target.push(items[i]);
     74    }
     75}
    4176/**
    4277 * Basically just str.split(","), but handling cases
    … …  
    4580 */
    4681function parseCommaParts(str) {
    47     if (!str) {
    48         return [''];
    49     }
    5082    const parts = [];
    51     const m = (0, balanced_match_1.balanced)('{', '}', str);
    52     if (!m) {
    53         return str.split(',');
    54     }
    55     const { pre, body, post } = m;
    56     const p = pre.split(',');
    57     p[p.length - 1] += '{' + body + '}';
    58     const postParts = parseCommaParts(post);
    59     if (post.length) {
    60         ;
    61         p[p.length - 1] += postParts.shift();
    62         p.push.apply(p, postParts);
    63     }
    64     parts.push.apply(parts, p);
    65     return parts;
     83    // Walk the brace groups iteratively. Recursing on `post` once per group let a
     84    // chain of them exhaust the stack - the parsing-side counterpart to
     85    // the `expand_` overflow fixed for CVE-2026-14257, and not something `max` or
     86    // `maxLength` can bound, since it happens before expansion.
     87    //
     88    // The part the next chunk continues
     89    let carry = '';
     90    for (;;) {
     91        const m = (0, balanced_match_1.balanced)('{', '}', str);
     92        if (!m) {
     93            const tail = str.split(',');
     94            tail[0] = carry + tail[0];
     95            pushAll(parts, tail);
     96            return parts;
     97        }
     98        const { pre, body, post } = m;
     99        const p = pre.split(',');
     100        p[0] = carry + p[0];
     101        p[p.length - 1] += '{' + body + '}';
     102        if (!post.length) {
     103            pushAll(parts, p);
     104            return parts;
     105        }
     106        carry = p.pop();
     107        pushAll(parts, p);
     108        str = post;
     109    }
    66110}
    67111function expand(str, options = {}) {
    … …  
    69113        return [];
    70114    }
    71     const { max = exports.EXPANSION_MAX } = options;
     115    const { max = exports.EXPANSION_MAX, maxLength = exports.EXPANSION_MAX_LENGTH, maxDepth = exports.EXPANSION_MAX_DEPTH, maxRewrites = exports.EXPANSION_MAX_REWRITES, } = options;
    72116    // I don't know why Bash 4.3 does this, but it does.
    73117    // Anything starting with {} will have the first two bytes preserved
    … …  
    79123        str = '\\{\\}' + str.slice(2);
    80124    }
    81     return expand_(escapeBraces(str), max, true).map(unescapeBraces);
     125    return expand_(escapeBraces(str), max, maxLength, maxDepth, 0, maxRewrites, true).map(unescapeBraces);
    82126}
    83127function embrace(str) {
    … …  
    93137    return i >= y;
    94138}
    95 function expand_(str, max, isTop) {
    96     /** @type {string[]} */
    97     const expansions = [];
    98     const m = (0, balanced_match_1.balanced)('{', '}', str);
    99     if (!m)
     139// Build `{ acc[a] + pre + values[v] }` for every combination, capping the
     140// number of results at `max` and the total number of characters at `maxLength`.
     141// This is the one place output grows, so bounding it here keeps the single
     142// accumulator - and therefore memory - flat regardless of how many brace groups
     143// are combined (CVE-2026-14257).
     144function combine(acc, pre, values, max, maxLength, dropEmpties) {
     145    const out = [];
     146    let length = 0;
     147    for (let a = 0; a < acc.length; a++) {
     148        for (let v = 0; v < values.length; v++) {
     149            if (out.length >= max)
     150                return out;
     151            const expansion = acc[a] + pre + values[v];
     152            // Bash drops empty results at the top level. Skip them before they count
     153            // against `max`, so `max` bounds the number of *kept* results.
     154            if (dropEmpties && !expansion)
     155                continue;
     156            if (length + expansion.length > maxLength)
     157                return out;
     158            out.push(expansion);
     159            length += expansion.length;
     160        }
     161    }
     162    return out;
     163}
     164// The expansion values of a single numeric (`1..5`) or alphabetic (`a..e..2`)
     165// sequence body.
     166function expandSequence(body, isAlphaSequence, max, maxLength) {
     167    const n = body.split(/\.\./);
     168    const N = [];
     169    // A sequence body always splits into two or three parts, but the compiler
     170    // can't know that.
     171    /* c8 ignore start */
     172    if (n[0] === undefined || n[1] === undefined) {
     173        return N;
     174    }
     175    /* c8 ignore stop */
     176    const x = numeric(n[0]);
     177    const y = numeric(n[1]);
     178    const width = Math.max(n[0].length, n[1].length);
     179    let incr = n.length === 3 && n[2] !== undefined ?
     180        Math.max(Math.abs(numeric(n[2])), 1)
     181        : 1;
     182    let test = lte;
     183    const reverse = y < x;
     184    if (reverse) {
     185        incr *= -1;
     186        test = gte;
     187    }
     188    const pad = n.some(isPadded);
     189    let length = 0;
     190    for (let i = x; test(i, y) && N.length < max; i += incr) {
     191        let c;
     192        if (isAlphaSequence) {
     193            c = String.fromCharCode(i);
     194            if (c === '\\') {
     195                c = '';
     196            }
     197        }
     198        else {
     199            c = String(i);
     200            if (pad) {
     201                const need = width - c.length;
     202                if (need > 0) {
     203                    const z = new Array(need + 1).join('0');
     204                    if (i < 0) {
     205                        c = '-' + z + c.slice(1);
     206                    }
     207                    else {
     208                        c = z + c;
     209                    }
     210                }
     211            }
     212        }
     213        if (length + c.length > maxLength)
     214            break;
     215        N.push(c);
     216        length += c.length;
     217    }
     218    return N;
     219}
     220function expand_(str, max, maxLength, maxDepth, depth, maxRewrites, isTop) {
     221    // Too deeply nested to keep following: treat the rest as literal, the same
     222    // way a group that cannot expand is already handled. Truncating rather than
     223    // throwing keeps `expand` total, matching `max` and `maxLength`.
     224    if (depth > maxDepth) {
    100225        return [str];
    101     // no need to expand pre, since it is guaranteed to be free of brace-sets
    102     const pre = m.pre;
    103     const post = m.post.length ? expand_(m.post, max, false) : [''];
    104     if (/\$$/.test(m.pre)) {
    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     }
    110     else {
     226    }
     227    // Consume the string's top-level brace groups left to right, threading a
     228    // running set of combined prefixes (`acc`). Expanding the tail iteratively -
     229    // rather than recursing on `m.post` once per group - keeps the native stack
     230    // depth constant, so deeply chained input (`'{a,b}'.repeat(3000)`) can no
     231    // longer overflow the stack, and leaves a single accumulator whose size
     232    // `maxLength` bounds directly (CVE-2026-14257).
     233    let acc = [''];
     234    // Bash drops empty results, but only when the *first* top-level group is a
     235    // comma set - a sequence like `{a..\}` may legitimately yield ''. The drop
     236    // is on the final strings, so it is applied to whichever `combine` produces
     237    // them (the one with no brace set left in the tail).
     238    // How many times the `{a},b}` rewrite below has restarted the scan. Each pass
     239    // re-reads the whole string, so leaving this unbounded is quadratic.
     240    let rewrites = 0;
     241    let dropEmpties = false;
     242    let firstGroup = true;
     243    for (;;) {
     244        const m = (0, balanced_match_1.balanced)('{', '}', str);
     245        // No brace set left: the rest of the string is literal.
     246        if (!m) {
     247            return combine(acc, str, [''], max, maxLength, dropEmpties);
     248        }
     249        // no need to expand pre, since it is guaranteed to be free of brace-sets
     250        const pre = m.pre;
     251        if (/\$$/.test(pre)) {
     252            acc = combine(acc, pre + '{' + m.body + '}', [''], max, maxLength, dropEmpties && !m.post.length);
     253            firstGroup = false;
     254            if (!m.post.length)
     255                break;
     256            str = m.post;
     257            continue;
     258        }
    111259        const isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body);
    112260        const isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body);
    … …  
    115263        if (!isSequence && !isOptions) {
    116264            // {a},b}
    117             if (m.post.match(/,(?!,).*\}/)) {
     265            if (rewrites < maxRewrites && m.post.match(/,(?!,).*\}/)) {
     266                rewrites++;
    118267                str = m.pre + '{' + m.body + escClose + m.post;
    119                 return expand_(str, max, true);
    120             }
    121             return [str];
    122         }
    123         let n;
     268                isTop = true;
     269                continue;
     270            }
     271            // Nothing here expands, so the whole remaining string is literal.
     272            return combine(acc, pre + '{' + m.body + '}' + m.post, [''], max, maxLength, dropEmpties);
     273        }
     274        if (firstGroup) {
     275            dropEmpties = isTop && !isSequence;
     276            firstGroup = false;
     277        }
     278        let values;
    124279        if (isSequence) {
    125             n = m.body.split(/\.\./);
     280            values = expandSequence(m.body, isAlphaSequence, max, maxLength);
    126281        }
    127282        else {
    128             n = parseCommaParts(m.body);
     283            let n = parseCommaParts(m.body);
    129284            if (n.length === 1 && n[0] !== undefined) {
    130285                // x{{a,b}}y ==> x{a}y x{b}y
    131                 n = expand_(n[0], max, false).map(embrace);
     286                n = expand_(n[0], max, maxLength, maxDepth, depth + 1, maxRewrites, false).map(embrace);
    132287                //XXX is this necessary? Can't seem to hit it in tests.
    133288                /* c8 ignore start */
    134289                if (n.length === 1) {
    135                     return post.map(p => m.pre + n[0] + p);
     290                    acc = combine(acc, pre + n[0], [''], max, maxLength, dropEmpties && !m.post.length);
     291                    if (!m.post.length)
     292                        break;
     293                    str = m.post;
     294                    continue;
    136295                }
    137296                /* c8 ignore stop */
    138297            }
    139         }
    140         // at this point, n is the parts, and we know it's not a comma set
    141         // with a single entry.
    142         let N;
    143         if (isSequence && n[0] !== undefined && n[1] !== undefined) {
    144             const x = numeric(n[0]);
    145             const y = numeric(n[1]);
    146             const width = Math.max(n[0].length, n[1].length);
    147             let incr = n.length === 3 && n[2] !== undefined ? Math.abs(numeric(n[2])) : 1;
    148             let test = lte;
    149             const reverse = y < x;
    150             if (reverse) {
    151                 incr *= -1;
    152                 test = gte;
    153             }
    154             const pad = n.some(isPadded);
    155             N = [];
    156             for (let i = x; test(i, y); i += incr) {
    157                 let c;
    158                 if (isAlphaSequence) {
    159                     c = String.fromCharCode(i);
    160                     if (c === '\\') {
    161                         c = '';
     298            // Values that `combine` is going to drop as empty produce no result, so
     299            // they must not count against `max` - otherwise `{a,,b}` with `max: 2`
     300            // would stop at `['a', '']` and yield one result instead of two. Skipping
     301            // them outright keeps `values` bounded while leaving `max` a bound on
     302            // *kept* results.
     303            let dropsEmpties = dropEmpties && !m.post.length && !pre;
     304            for (let d = 0; dropsEmpties && d < acc.length; d++) {
     305                if (acc[d]) {
     306                    dropsEmpties = false;
     307                }
     308            }
     309            values = [];
     310            let valuesLength = 0;
     311            outer: for (let j = 0; j < n.length; j++) {
     312                const expanded = expand_(n[j], max, maxLength, maxDepth, depth + 1, maxRewrites, false);
     313                for (let k = 0; k < expanded.length; k++) {
     314                    const v = expanded[k];
     315                    if (dropsEmpties && !v)
     316                        continue;
     317                    if (values.length >= max ||
     318                        valuesLength + v.length > maxLength) {
     319                        break outer;
    162320                    }
     321                    values.push(v);
     322                    valuesLength += v.length;
    163323                }
    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         }
    182         else {
    183             N = [];
    184             for (let j = 0; j < n.length; j++) {
    185                 N.push.apply(N, expand_(n[j], max, false));
    186             }
    187         }
    188         for (let j = 0; j < N.length; j++) {
    189             for (let k = 0; k < post.length && expansions.length < max; k++) {
    190                 const expansion = pre + N[j] + post[k];
    191                 if (!isTop || isSequence || expansion) {
    192                     expansions.push(expansion);
    193                 }
    194             }
    195         }
    196     }
    197     return expansions;
     324            }
     325        }
     326        acc = combine(acc, pre, values, max, maxLength, dropEmpties && !m.post.length);
     327        if (!m.post.length)
     328            break;
     329        str = m.post;
     330    }
     331    return acc;
    198332}
    199333//# sourceMappingURL=index.js.map
Note: See TracChangeset for help on using the changeset viewer.