1- const { MAX_SAFE_COMPONENT_LENGTH } = require ( './constants' )
1+ const {
2+ MAX_SAFE_COMPONENT_LENGTH ,
3+ MAX_SAFE_BUILD_LENGTH ,
4+ MAX_LENGTH ,
5+ } = require ( './constants' )
26const debug = require ( './debug' )
37exports = module . exports = { }
48
@@ -9,16 +13,31 @@ const src = exports.src = []
913const t = exports . t = { }
1014let R = 0
1115
16+ const LETTERDASHNUMBER = '[a-zA-Z0-9-]'
17+
18+ // Replace some greedy regex tokens to prevent regex dos issues. These regex are
19+ // used internally via the safeRe object since all inputs in this library get
20+ // normalized first to trim and collapse all extra whitespace. The original
21+ // regexes are exported for userland consumption and lower level usage. A
22+ // future breaking change could export the safer regex only with a note that
23+ // all input should have extra whitespace removed.
24+ const safeRegexReplacements = [
25+ [ '\\s' , 1 ] ,
26+ [ '\\d' , MAX_LENGTH ] ,
27+ [ LETTERDASHNUMBER , MAX_SAFE_BUILD_LENGTH ] ,
28+ ]
29+
30+ const makeSafeRegex = ( value ) => {
31+ for ( const [ token , max ] of safeRegexReplacements ) {
32+ value = value
33+ . split ( `${ token } *` ) . join ( `${ token } {0,${ max } }` )
34+ . split ( `${ token } +` ) . join ( `${ token } {1,${ max } }` )
35+ }
36+ return value
37+ }
38+
1239const createToken = ( name , value , isGlobal ) => {
13- // Replace all greedy whitespace to prevent regex dos issues. These regex are
14- // used internally via the safeRe object since all inputs in this library get
15- // normalized first to trim and collapse all extra whitespace. The original
16- // regexes are exported for userland consumption and lower level usage. A
17- // future breaking change could export the safer regex only with a note that
18- // all input should have extra whitespace removed.
19- const safe = value
20- . split ( '\\s*' ) . join ( '\\s{0,1}' )
21- . split ( '\\s+' ) . join ( '\\s' )
40+ const safe = makeSafeRegex ( value )
2241 const index = R ++
2342 debug ( name , index , value )
2443 t [ name ] = index
@@ -34,13 +53,13 @@ const createToken = (name, value, isGlobal) => {
3453// A single `0`, or a non-zero digit followed by zero or more digits.
3554
3655createToken ( 'NUMERICIDENTIFIER' , '0|[1-9]\\d*' )
37- createToken ( 'NUMERICIDENTIFIERLOOSE' , '[0-9] +' )
56+ createToken ( 'NUMERICIDENTIFIERLOOSE' , '\\d +' )
3857
3958// ## Non-numeric Identifier
4059// Zero or more digits, followed by a letter or hyphen, and then zero or
4160// more letters, digits, or hyphens.
4261
43- createToken ( 'NONNUMERICIDENTIFIER' , ' \\d*[a-zA-Z-][a-zA-Z0-9-]*' )
62+ createToken ( 'NONNUMERICIDENTIFIER' , ` \\d*[a-zA-Z-]${ LETTERDASHNUMBER } *` )
4463
4564// ## Main Version
4665// Three dot-separated numeric identifiers.
@@ -75,7 +94,7 @@ createToken('PRERELEASELOOSE', `(?:-?(${src[t.PRERELEASEIDENTIFIERLOOSE]
7594// ## Build Metadata Identifier
7695// Any combination of digits, letters, or hyphens.
7796
78- createToken ( 'BUILDIDENTIFIER' , '[0-9A-Za-z-]+' )
97+ createToken ( 'BUILDIDENTIFIER' , ` ${ LETTERDASHNUMBER } +` )
7998
8099// ## Build Metadata
81100// Plus sign, followed by one or more period-separated build metadata
0 commit comments