+5
-0
@@ -7,2 +7,4 @@ 'use strict'; | ||
| const DEFAULT_MAX_EXTGLOB_RECURSION = 0; | ||
| /** | ||
@@ -71,2 +73,3 @@ * Posix glob regex | ||
| const POSIX_REGEX_SOURCE = { | ||
| __proto__: null, | ||
| alnum: 'a-zA-Z0-9', | ||
@@ -89,2 +92,3 @@ alpha: 'a-zA-Z', | ||
| module.exports = { | ||
| DEFAULT_MAX_EXTGLOB_RECURSION, | ||
| MAX_LENGTH: 1024 * 64, | ||
@@ -103,2 +107,3 @@ POSIX_REGEX_SOURCE, | ||
| REPLACEMENTS: { | ||
| __proto__: null, | ||
| '***': '*', | ||
@@ -105,0 +110,0 @@ '**/**': '**', |
+301
-0
@@ -48,2 +48,273 @@ 'use strict'; | ||
| const splitTopLevel = input => { | ||
| const parts = []; | ||
| let bracket = 0; | ||
| let paren = 0; | ||
| let quote = 0; | ||
| let value = ''; | ||
| let escaped = false; | ||
| for (const ch of input) { | ||
| if (escaped === true) { | ||
| value += ch; | ||
| escaped = false; | ||
| continue; | ||
| } | ||
| if (ch === '\\') { | ||
| value += ch; | ||
| escaped = true; | ||
| continue; | ||
| } | ||
| if (ch === '"') { | ||
| quote = quote === 1 ? 0 : 1; | ||
| value += ch; | ||
| continue; | ||
| } | ||
| if (quote === 0) { | ||
| if (ch === '[') { | ||
| bracket++; | ||
| } else if (ch === ']' && bracket > 0) { | ||
| bracket--; | ||
| } else if (bracket === 0) { | ||
| if (ch === '(') { | ||
| paren++; | ||
| } else if (ch === ')' && paren > 0) { | ||
| paren--; | ||
| } else if (ch === '|' && paren === 0) { | ||
| parts.push(value); | ||
| value = ''; | ||
| continue; | ||
| } | ||
| } | ||
| } | ||
| value += ch; | ||
| } | ||
| parts.push(value); | ||
| return parts; | ||
| }; | ||
| const isPlainBranch = branch => { | ||
| let escaped = false; | ||
| for (const ch of branch) { | ||
| if (escaped === true) { | ||
| escaped = false; | ||
| continue; | ||
| } | ||
| if (ch === '\\') { | ||
| escaped = true; | ||
| continue; | ||
| } | ||
| if (/[?*+@!()[\]{}]/.test(ch)) { | ||
| return false; | ||
| } | ||
| } | ||
| return true; | ||
| }; | ||
| const normalizeSimpleBranch = branch => { | ||
| let value = branch.trim(); | ||
| let changed = true; | ||
| while (changed === true) { | ||
| changed = false; | ||
| if (/^@\([^\\()[\]{}|]+\)$/.test(value)) { | ||
| value = value.slice(2, -1); | ||
| changed = true; | ||
| } | ||
| } | ||
| if (!isPlainBranch(value)) { | ||
| return; | ||
| } | ||
| return value.replace(/\\(.)/g, '$1'); | ||
| }; | ||
| const hasRepeatedCharPrefixOverlap = branches => { | ||
| const values = branches.map(normalizeSimpleBranch).filter(Boolean); | ||
| for (let i = 0; i < values.length; i++) { | ||
| for (let j = i + 1; j < values.length; j++) { | ||
| const a = values[i]; | ||
| const b = values[j]; | ||
| const char = a[0]; | ||
| if (!char || a !== char.repeat(a.length) || b !== char.repeat(b.length)) { | ||
| continue; | ||
| } | ||
| if (a === b || a.startsWith(b) || b.startsWith(a)) { | ||
| return true; | ||
| } | ||
| } | ||
| } | ||
| return false; | ||
| }; | ||
| const parseRepeatedExtglob = (pattern, requireEnd = true) => { | ||
| if ((pattern[0] !== '+' && pattern[0] !== '*') || pattern[1] !== '(') { | ||
| return; | ||
| } | ||
| let bracket = 0; | ||
| let paren = 0; | ||
| let quote = 0; | ||
| let escaped = false; | ||
| for (let i = 1; i < pattern.length; i++) { | ||
| const ch = pattern[i]; | ||
| if (escaped === true) { | ||
| escaped = false; | ||
| continue; | ||
| } | ||
| if (ch === '\\') { | ||
| escaped = true; | ||
| continue; | ||
| } | ||
| if (ch === '"') { | ||
| quote = quote === 1 ? 0 : 1; | ||
| continue; | ||
| } | ||
| if (quote === 1) { | ||
| continue; | ||
| } | ||
| if (ch === '[') { | ||
| bracket++; | ||
| continue; | ||
| } | ||
| if (ch === ']' && bracket > 0) { | ||
| bracket--; | ||
| continue; | ||
| } | ||
| if (bracket > 0) { | ||
| continue; | ||
| } | ||
| if (ch === '(') { | ||
| paren++; | ||
| continue; | ||
| } | ||
| if (ch === ')') { | ||
| paren--; | ||
| if (paren === 0) { | ||
| if (requireEnd === true && i !== pattern.length - 1) { | ||
| return; | ||
| } | ||
| return { | ||
| type: pattern[0], | ||
| body: pattern.slice(2, i), | ||
| end: i | ||
| }; | ||
| } | ||
| } | ||
| } | ||
| }; | ||
| const getStarExtglobSequenceOutput = pattern => { | ||
| let index = 0; | ||
| const chars = []; | ||
| while (index < pattern.length) { | ||
| const match = parseRepeatedExtglob(pattern.slice(index), false); | ||
| if (!match || match.type !== '*') { | ||
| return; | ||
| } | ||
| const branches = splitTopLevel(match.body).map(branch => branch.trim()); | ||
| if (branches.length !== 1) { | ||
| return; | ||
| } | ||
| const branch = normalizeSimpleBranch(branches[0]); | ||
| if (!branch || branch.length !== 1) { | ||
| return; | ||
| } | ||
| chars.push(branch); | ||
| index += match.end + 1; | ||
| } | ||
| if (chars.length < 1) { | ||
| return; | ||
| } | ||
| const source = chars.length === 1 | ||
| ? utils.escapeRegex(chars[0]) | ||
| : `[${chars.map(ch => utils.escapeRegex(ch)).join('')}]`; | ||
| return `${source}*`; | ||
| }; | ||
| const repeatedExtglobRecursion = pattern => { | ||
| let depth = 0; | ||
| let value = pattern.trim(); | ||
| let match = parseRepeatedExtglob(value); | ||
| while (match) { | ||
| depth++; | ||
| value = match.body.trim(); | ||
| match = parseRepeatedExtglob(value); | ||
| } | ||
| return depth; | ||
| }; | ||
| const analyzeRepeatedExtglob = (body, options) => { | ||
| if (options.maxExtglobRecursion === false) { | ||
| return { risky: false }; | ||
| } | ||
| const max = | ||
| typeof options.maxExtglobRecursion === 'number' | ||
| ? options.maxExtglobRecursion | ||
| : constants.DEFAULT_MAX_EXTGLOB_RECURSION; | ||
| const branches = splitTopLevel(body).map(branch => branch.trim()); | ||
| if (branches.length > 1) { | ||
| if ( | ||
| branches.some(branch => branch === '') || | ||
| branches.some(branch => /^[*?]+$/.test(branch)) || | ||
| hasRepeatedCharPrefixOverlap(branches) | ||
| ) { | ||
| return { risky: true }; | ||
| } | ||
| } | ||
| for (const branch of branches) { | ||
| const safeOutput = getStarExtglobSequenceOutput(branch); | ||
| if (safeOutput) { | ||
| return { risky: true, safeOutput }; | ||
| } | ||
| if (repeatedExtglobRecursion(branch) > max) { | ||
| return { risky: true }; | ||
| } | ||
| } | ||
| return { risky: false }; | ||
| }; | ||
| /** | ||
@@ -230,2 +501,4 @@ * Parse the given input string. | ||
| token.output = state.output; | ||
| token.startIndex = state.index; | ||
| token.tokensIndex = tokens.length; | ||
| const output = (opts.capture ? '(' : '') + token.open; | ||
@@ -240,2 +513,30 @@ | ||
| const extglobClose = token => { | ||
| const literal = input.slice(token.startIndex, state.index + 1); | ||
| const body = input.slice(token.startIndex + 2, state.index); | ||
| const analysis = analyzeRepeatedExtglob(body, opts); | ||
| if ((token.type === 'plus' || token.type === 'star') && analysis.risky) { | ||
| const safeOutput = analysis.safeOutput | ||
| ? (token.output ? '' : ONE_CHAR) + (opts.capture ? `(${analysis.safeOutput})` : analysis.safeOutput) | ||
| : undefined; | ||
| const open = tokens[token.tokensIndex]; | ||
| open.type = 'text'; | ||
| open.value = literal; | ||
| open.output = safeOutput || utils.escapeRegex(literal); | ||
| for (let i = token.tokensIndex + 1; i < tokens.length; i++) { | ||
| tokens[i].value = ''; | ||
| tokens[i].output = ''; | ||
| delete tokens[i].suffix; | ||
| } | ||
| state.output = token.output + open.output; | ||
| state.backtrack = true; | ||
| push({ type: 'paren', extglob: true, value, output: '' }); | ||
| decrement('parens'); | ||
| return; | ||
| } | ||
| let output = token.close + (opts.capture ? ')' : ''); | ||
@@ -242,0 +543,0 @@ let rest; |
+1
-1
| { | ||
| "name": "picomatch", | ||
| "description": "Blazing fast and accurate glob matcher written in JavaScript, with no dependencies and full support for standard and extended Bash glob features, including braces, extglobs, POSIX brackets, and regular expressions.", | ||
| "version": "2.3.1", | ||
| "version": "2.3.2", | ||
| "homepage": "https://github.com/micromatch/picomatch", | ||
@@ -6,0 +6,0 @@ "author": "Jon Schlinkert (https://github.com/jonschlinkert)", |
+9
-1
@@ -321,3 +321,4 @@ <h1 align="center">Picomatch</h1> | ||
| | `matchBase` | `boolean` | `false` | Alias for `basename` | | ||
| | `maxLength` | `boolean` | `65536` | Limit the max length of the input string. An error is thrown if the input string is longer than this value. | | ||
| | `maxLength` | `number` | `65536` | Limit the max length of the input string. An error is thrown if the input string is longer than this value. | | ||
| | `maxExtglobRecursion` | `number\|boolean` | `0` | Limit nested quantified extglobs and other risky repeated extglob forms. When the limit is exceeded, the extglob is treated as a literal string instead of being compiled to regex. Set to `false` to disable this safeguard. | | ||
| | `nobrace` | `boolean` | `false` | Disable brace matching, so that `{a,b}` and `{1..3}` would be treated as literal characters. | | ||
@@ -537,2 +538,9 @@ | `nobracket` | `boolean` | `undefined` | Disable matching with regex brackets. | | ||
| console.log(pm.isMatch('foo.bar', '!(!(foo)).!(!(bar))')); // true | ||
| // risky quantified extglobs are treated literally by default | ||
| console.log(pm.makeRe('+(a|aa)')); | ||
| //=> /^(?:\+\(a\|aa\))$/ | ||
| // increase the limit to allow a small amount of nested quantified extglobs | ||
| console.log(pm.isMatch('aaa', '+(+(a))', { maxExtglobRecursion: 1 })); // true | ||
| ``` | ||
@@ -539,0 +547,0 @@ |
-136
| # Release history | ||
| **All notable changes to this project will be documented in this file.** | ||
| The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) | ||
| and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). | ||
| <details> | ||
| <summary><strong>Guiding Principles</strong></summary> | ||
| - Changelogs are for humans, not machines. | ||
| - There should be an entry for every single version. | ||
| - The same types of changes should be grouped. | ||
| - Versions and sections should be linkable. | ||
| - The latest version comes first. | ||
| - The release date of each versions is displayed. | ||
| - Mention whether you follow Semantic Versioning. | ||
| </details> | ||
| <details> | ||
| <summary><strong>Types of changes</strong></summary> | ||
| Changelog entries are classified using the following labels _(from [keep-a-changelog](http://keepachangelog.com/)_): | ||
| - `Added` for new features. | ||
| - `Changed` for changes in existing functionality. | ||
| - `Deprecated` for soon-to-be removed features. | ||
| - `Removed` for now removed features. | ||
| - `Fixed` for any bug fixes. | ||
| - `Security` in case of vulnerabilities. | ||
| </details> | ||
| ## 2.3.1 (2022-01-02) | ||
| ### Fixed | ||
| * Fixes bug when a pattern containing an expression after the closing parenthesis (`/!(*.d).{ts,tsx}`) was incorrectly converted to regexp ([9f241ef](https://github.com/micromatch/picomatch/commit/9f241ef)). | ||
| ### Changed | ||
| * Some documentation improvements ([f81d236](https://github.com/micromatch/picomatch/commit/f81d236), [421e0e7](https://github.com/micromatch/picomatch/commit/421e0e7)). | ||
| ## 2.3.0 (2021-05-21) | ||
| ### Fixed | ||
| * Fixes bug where file names with two dots were not being matched consistently with negation extglobs containing a star ([56083ef](https://github.com/micromatch/picomatch/commit/56083ef)) | ||
| ## 2.2.3 (2021-04-10) | ||
| ### Fixed | ||
| * Do not skip pattern seperator for square brackets ([fb08a30](https://github.com/micromatch/picomatch/commit/fb08a30)). | ||
| * Set negatedExtGlob also if it does not span the whole pattern ([032e3f5](https://github.com/micromatch/picomatch/commit/032e3f5)). | ||
| ## 2.2.2 (2020-03-21) | ||
| ### Fixed | ||
| * Correctly handle parts of the pattern after parentheses in the `scan` method ([e15b920](https://github.com/micromatch/picomatch/commit/e15b920)). | ||
| ## 2.2.1 (2020-01-04) | ||
| * Fixes [#49](https://github.com/micromatch/picomatch/issues/49), so that braces with no sets or ranges are now propertly treated as literals. | ||
| ## 2.2.0 (2020-01-04) | ||
| * Disable fastpaths mode for the parse method ([5b8d33f](https://github.com/micromatch/picomatch/commit/5b8d33f)) | ||
| * Add `tokens`, `slashes`, and `parts` to the object returned by `picomatch.scan()`. | ||
| ## 2.1.0 (2019-10-31) | ||
| * add benchmarks for scan ([4793b92](https://github.com/micromatch/picomatch/commit/4793b92)) | ||
| * Add eslint object-curly-spacing rule ([707c650](https://github.com/micromatch/picomatch/commit/707c650)) | ||
| * Add prefer-const eslint rule ([5c7501c](https://github.com/micromatch/picomatch/commit/5c7501c)) | ||
| * Add support for nonegate in scan API ([275c9b9](https://github.com/micromatch/picomatch/commit/275c9b9)) | ||
| * Change lets to consts. Move root import up. ([4840625](https://github.com/micromatch/picomatch/commit/4840625)) | ||
| * closes https://github.com/micromatch/picomatch/issues/21 ([766bcb0](https://github.com/micromatch/picomatch/commit/766bcb0)) | ||
| * Fix "Extglobs" table in readme ([eb19da8](https://github.com/micromatch/picomatch/commit/eb19da8)) | ||
| * fixes https://github.com/micromatch/picomatch/issues/20 ([9caca07](https://github.com/micromatch/picomatch/commit/9caca07)) | ||
| * fixes https://github.com/micromatch/picomatch/issues/26 ([fa58f45](https://github.com/micromatch/picomatch/commit/fa58f45)) | ||
| * Lint test ([d433a34](https://github.com/micromatch/picomatch/commit/d433a34)) | ||
| * lint unit tests ([0159b55](https://github.com/micromatch/picomatch/commit/0159b55)) | ||
| * Make scan work with noext ([6c02e03](https://github.com/micromatch/picomatch/commit/6c02e03)) | ||
| * minor linting ([c2a2b87](https://github.com/micromatch/picomatch/commit/c2a2b87)) | ||
| * minor parser improvements ([197671d](https://github.com/micromatch/picomatch/commit/197671d)) | ||
| * remove eslint since it... ([07876fa](https://github.com/micromatch/picomatch/commit/07876fa)) | ||
| * remove funding file ([8ebe96d](https://github.com/micromatch/picomatch/commit/8ebe96d)) | ||
| * Remove unused funks ([cbc6d54](https://github.com/micromatch/picomatch/commit/cbc6d54)) | ||
| * Run eslint during pretest, fix existing eslint findings ([0682367](https://github.com/micromatch/picomatch/commit/0682367)) | ||
| * support `noparen` in scan ([3d37569](https://github.com/micromatch/picomatch/commit/3d37569)) | ||
| * update changelog ([7b34e77](https://github.com/micromatch/picomatch/commit/7b34e77)) | ||
| * update travis ([777f038](https://github.com/micromatch/picomatch/commit/777f038)) | ||
| * Use eslint-disable-next-line instead of eslint-disable ([4e7c1fd](https://github.com/micromatch/picomatch/commit/4e7c1fd)) | ||
| ## 2.0.7 (2019-05-14) | ||
| * 2.0.7 ([9eb9a71](https://github.com/micromatch/picomatch/commit/9eb9a71)) | ||
| * supports lookbehinds ([1f63f7e](https://github.com/micromatch/picomatch/commit/1f63f7e)) | ||
| * update .verb.md file with typo change ([2741279](https://github.com/micromatch/picomatch/commit/2741279)) | ||
| * fix: typo in README ([0753e44](https://github.com/micromatch/picomatch/commit/0753e44)) | ||
| ## 2.0.4 (2019-04-10) | ||
| ### Fixed | ||
| - Readme link [fixed](https://github.com/micromatch/picomatch/pull/13/commits/a96ab3aa2b11b6861c23289964613d85563b05df) by @danez. | ||
| - `options.capture` now works as expected when fastpaths are enabled. See https://github.com/micromatch/picomatch/pull/12/commits/26aefd71f1cfaf95c37f1c1fcab68a693b037304. Thanks to @DrPizza. | ||
| ## 2.0.0 (2019-04-10) | ||
| ### Added | ||
| - Adds support for `options.onIgnore`. See the readme for details | ||
| - Adds support for `options.onResult`. See the readme for details | ||
| ### Breaking changes | ||
| - The unixify option was renamed to `windows` | ||
| - caching and all related options and methods have been removed | ||
| ## 1.0.0 (2018-11-05) | ||
| - adds `.onMatch` option | ||
| - improvements to `.scan` method | ||
| - numerous improvements and optimizations for matching and parsing | ||
| - better windows path handling | ||
| ## 0.1.0 - 2017-04-13 | ||
| First release. | ||
| [keep-a-changelog]: https://github.com/olivierlacan/keep-a-changelog |
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
90605
0.73%1961
14.21%717
1.13%9
-10%