@@ -4923,8 +4923,11 @@ var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER ||
49234923// Max safe segment length for coercion.
49244924var MAX_SAFE_COMPONENT_LENGTH = 16
49254925
4926+ var MAX_SAFE_BUILD_LENGTH = MAX_LENGTH - 6
4927+
49264928// The actual regexps go on exports.re
49274929var re = exports . re = [ ]
4930+ var safeRe = exports . safeRe = [ ]
49284931var src = exports . src = [ ]
49294932var t = exports . tokens = { }
49304933var R = 0
@@ -4933,6 +4936,31 @@ function tok (n) {
49334936 t [ n ] = R ++
49344937}
49354938
4939+ var LETTERDASHNUMBER = '[a-zA-Z0-9-]'
4940+
4941+ // Replace some greedy regex tokens to prevent regex dos issues. These regex are
4942+ // used internally via the safeRe object since all inputs in this library get
4943+ // normalized first to trim and collapse all extra whitespace. The original
4944+ // regexes are exported for userland consumption and lower level usage. A
4945+ // future breaking change could export the safer regex only with a note that
4946+ // all input should have extra whitespace removed.
4947+ var safeRegexReplacements = [
4948+ [ '\\s' , 1 ] ,
4949+ [ '\\d' , MAX_LENGTH ] ,
4950+ [ LETTERDASHNUMBER , MAX_SAFE_BUILD_LENGTH ] ,
4951+ ]
4952+
4953+ function makeSafeRe ( value ) {
4954+ for ( var i = 0 ; i < safeRegexReplacements . length ; i ++ ) {
4955+ var token = safeRegexReplacements [ i ] [ 0 ]
4956+ var max = safeRegexReplacements [ i ] [ 1 ]
4957+ value = value
4958+ . split ( token + '*' ) . join ( token + '{0,' + max + '}' )
4959+ . split ( token + '+' ) . join ( token + '{1,' + max + '}' )
4960+ }
4961+ return value
4962+ }
4963+
49364964// The following Regular Expressions can be used for tokenizing,
49374965// validating, and parsing SemVer version strings.
49384966
@@ -4942,14 +4970,14 @@ function tok (n) {
49424970tok ( 'NUMERICIDENTIFIER' )
49434971src [ t . NUMERICIDENTIFIER ] = '0|[1-9]\\d*'
49444972tok ( 'NUMERICIDENTIFIERLOOSE' )
4945- src [ t . NUMERICIDENTIFIERLOOSE ] = '[0-9] +'
4973+ src [ t . NUMERICIDENTIFIERLOOSE ] = '\\d +'
49464974
49474975// ## Non-numeric Identifier
49484976// Zero or more digits, followed by a letter or hyphen, and then zero or
49494977// more letters, digits, or hyphens.
49504978
49514979tok ( 'NONNUMERICIDENTIFIER' )
4952- src [ t . NONNUMERICIDENTIFIER ] = '\\d*[a-zA-Z-][a-zA-Z0-9-] *'
4980+ src [ t . NONNUMERICIDENTIFIER ] = '\\d*[a-zA-Z-]' + LETTERDASHNUMBER + ' *'
49534981
49544982// ## Main Version
49554983// Three dot-separated numeric identifiers.
@@ -4991,7 +5019,7 @@ src[t.PRERELEASELOOSE] = '(?:-?(' + src[t.PRERELEASEIDENTIFIERLOOSE] +
49915019// Any combination of digits, letters, or hyphens.
49925020
49935021tok ( 'BUILDIDENTIFIER' )
4994- src [ t . BUILDIDENTIFIER ] = '[0-9A-Za-z-] +'
5022+ src [ t . BUILDIDENTIFIER ] = LETTERDASHNUMBER + ' +'
49955023
49965024// ## Build Metadata
49975025// Plus sign, followed by one or more period-separated build metadata
@@ -5071,6 +5099,7 @@ src[t.COERCE] = '(^|[^\\d])' +
50715099 '(?:$|[^\\d])'
50725100tok ( 'COERCERTL' )
50735101re [ t . COERCERTL ] = new RegExp ( src [ t . COERCE ] , 'g' )
5102+ safeRe [ t . COERCERTL ] = new RegExp ( makeSafeRe ( src [ t . COERCE ] ) , 'g' )
50745103
50755104// Tilde ranges.
50765105// Meaning is "reasonably at or greater than"
@@ -5080,6 +5109,7 @@ src[t.LONETILDE] = '(?:~>?)'
50805109tok ( 'TILDETRIM' )
50815110src [ t . TILDETRIM ] = '(\\s*)' + src [ t . LONETILDE ] + '\\s+'
50825111re [ t . TILDETRIM ] = new RegExp ( src [ t . TILDETRIM ] , 'g' )
5112+ safeRe [ t . TILDETRIM ] = new RegExp ( makeSafeRe ( src [ t . TILDETRIM ] ) , 'g' )
50835113var tildeTrimReplace = '$1~'
50845114
50855115tok ( 'TILDE' )
@@ -5095,6 +5125,7 @@ src[t.LONECARET] = '(?:\\^)'
50955125tok ( 'CARETTRIM' )
50965126src [ t . CARETTRIM ] = '(\\s*)' + src [ t . LONECARET ] + '\\s+'
50975127re [ t . CARETTRIM ] = new RegExp ( src [ t . CARETTRIM ] , 'g' )
5128+ safeRe [ t . CARETTRIM ] = new RegExp ( makeSafeRe ( src [ t . CARETTRIM ] ) , 'g' )
50985129var caretTrimReplace = '$1^'
50995130
51005131tok ( 'CARET' )
@@ -5116,6 +5147,7 @@ src[t.COMPARATORTRIM] = '(\\s*)' + src[t.GTLT] +
51165147
51175148// this one has to use the /g flag
51185149re [ t . COMPARATORTRIM ] = new RegExp ( src [ t . COMPARATORTRIM ] , 'g' )
5150+ safeRe [ t . COMPARATORTRIM ] = new RegExp ( makeSafeRe ( src [ t . COMPARATORTRIM ] ) , 'g' )
51195151var comparatorTrimReplace = '$1$2$3'
51205152
51215153// Something like `1.2.3 - 1.2.4`
@@ -5144,6 +5176,14 @@ for (var i = 0; i < R; i++) {
51445176 debug ( i , src [ i ] )
51455177 if ( ! re [ i ] ) {
51465178 re [ i ] = new RegExp ( src [ i ] )
5179+
5180+ // Replace all greedy whitespace to prevent regex dos issues. These regex are
5181+ // used internally via the safeRe object since all inputs in this library get
5182+ // normalized first to trim and collapse all extra whitespace. The original
5183+ // regexes are exported for userland consumption and lower level usage. A
5184+ // future breaking change could export the safer regex only with a note that
5185+ // all input should have extra whitespace removed.
5186+ safeRe [ i ] = new RegExp ( makeSafeRe ( src [ i ] ) )
51475187 }
51485188}
51495189
@@ -5168,7 +5208,7 @@ function parse (version, options) {
51685208 return null
51695209 }
51705210
5171- var r = options . loose ? re [ t . LOOSE ] : re [ t . FULL ]
5211+ var r = options . loose ? safeRe [ t . LOOSE ] : safeRe [ t . FULL ]
51725212 if ( ! r . test ( version ) ) {
51735213 return null
51745214 }
@@ -5223,7 +5263,7 @@ function SemVer (version, options) {
52235263 this . options = options
52245264 this . loose = ! ! options . loose
52255265
5226- var m = version . trim ( ) . match ( options . loose ? re [ t . LOOSE ] : re [ t . FULL ] )
5266+ var m = version . trim ( ) . match ( options . loose ? safeRe [ t . LOOSE ] : safeRe [ t . FULL ] )
52275267
52285268 if ( ! m ) {
52295269 throw new TypeError ( 'Invalid Version: ' + version )
@@ -5668,6 +5708,7 @@ function Comparator (comp, options) {
56685708 return new Comparator ( comp , options )
56695709 }
56705710
5711+ comp = comp . trim ( ) . split ( / \s + / ) . join ( ' ' )
56715712 debug ( 'comparator' , comp , options )
56725713 this . options = options
56735714 this . loose = ! ! options . loose
@@ -5684,7 +5725,7 @@ function Comparator (comp, options) {
56845725
56855726var ANY = { }
56865727Comparator . prototype . parse = function ( comp ) {
5687- var r = this . options . loose ? re [ t . COMPARATORLOOSE ] : re [ t . COMPARATOR ]
5728+ var r = this . options . loose ? safeRe [ t . COMPARATORLOOSE ] : safeRe [ t . COMPARATOR ]
56885729 var m = comp . match ( r )
56895730
56905731 if ( ! m ) {
@@ -5808,17 +5849,24 @@ function Range (range, options) {
58085849 this . loose = ! ! options . loose
58095850 this . includePrerelease = ! ! options . includePrerelease
58105851
5811- // First, split based on boolean or ||
5852+ // First reduce all whitespace as much as possible so we do not have to rely
5853+ // on potentially slow regexes like \s*. This is then stored and used for
5854+ // future error messages as well.
58125855 this . raw = range
5813- this . set = range . split ( / \s * \| \| \s * / ) . map ( function ( range ) {
5856+ . trim ( )
5857+ . split ( / \s + / )
5858+ . join ( ' ' )
5859+
5860+ // First, split based on boolean or ||
5861+ this . set = this . raw . split ( '||' ) . map ( function ( range ) {
58145862 return this . parseRange ( range . trim ( ) )
58155863 } , this ) . filter ( function ( c ) {
58165864 // throw out any that are not relevant for whatever reason
58175865 return c . length
58185866 } )
58195867
58205868 if ( ! this . set . length ) {
5821- throw new TypeError ( 'Invalid SemVer Range: ' + range )
5869+ throw new TypeError ( 'Invalid SemVer Range: ' + this . raw )
58225870 }
58235871
58245872 this . format ( )
@@ -5837,28 +5885,27 @@ Range.prototype.toString = function () {
58375885
58385886Range . prototype . parseRange = function ( range ) {
58395887 var loose = this . options . loose
5840- range = range . trim ( )
58415888 // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4`
5842- var hr = loose ? re [ t . HYPHENRANGELOOSE ] : re [ t . HYPHENRANGE ]
5889+ var hr = loose ? safeRe [ t . HYPHENRANGELOOSE ] : safeRe [ t . HYPHENRANGE ]
58435890 range = range . replace ( hr , hyphenReplace )
58445891 debug ( 'hyphen replace' , range )
58455892 // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5`
5846- range = range . replace ( re [ t . COMPARATORTRIM ] , comparatorTrimReplace )
5847- debug ( 'comparator trim' , range , re [ t . COMPARATORTRIM ] )
5893+ range = range . replace ( safeRe [ t . COMPARATORTRIM ] , comparatorTrimReplace )
5894+ debug ( 'comparator trim' , range , safeRe [ t . COMPARATORTRIM ] )
58485895
58495896 // `~ 1.2.3` => `~1.2.3`
5850- range = range . replace ( re [ t . TILDETRIM ] , tildeTrimReplace )
5897+ range = range . replace ( safeRe [ t . TILDETRIM ] , tildeTrimReplace )
58515898
58525899 // `^ 1.2.3` => `^1.2.3`
5853- range = range . replace ( re [ t . CARETTRIM ] , caretTrimReplace )
5900+ range = range . replace ( safeRe [ t . CARETTRIM ] , caretTrimReplace )
58545901
58555902 // normalize spaces
58565903 range = range . split ( / \s + / ) . join ( ' ' )
58575904
58585905 // At this point, the range is completely trimmed and
58595906 // ready to be split into comparators.
58605907
5861- var compRe = loose ? re [ t . COMPARATORLOOSE ] : re [ t . COMPARATOR ]
5908+ var compRe = loose ? safeRe [ t . COMPARATORLOOSE ] : safeRe [ t . COMPARATOR ]
58625909 var set = range . split ( ' ' ) . map ( function ( comp ) {
58635910 return parseComparator ( comp , this . options )
58645911 } , this ) . join ( ' ' ) . split ( / \s + / )
@@ -5958,7 +6005,7 @@ function replaceTildes (comp, options) {
59586005}
59596006
59606007function replaceTilde ( comp , options ) {
5961- var r = options . loose ? re [ t . TILDELOOSE ] : re [ t . TILDE ]
6008+ var r = options . loose ? safeRe [ t . TILDELOOSE ] : safeRe [ t . TILDE ]
59626009 return comp . replace ( r , function ( _ , M , m , p , pr ) {
59636010 debug ( 'tilde' , comp , _ , M , m , p , pr )
59646011 var ret
@@ -5999,7 +6046,7 @@ function replaceCarets (comp, options) {
59996046
60006047function replaceCaret ( comp , options ) {
60016048 debug ( 'caret' , comp , options )
6002- var r = options . loose ? re [ t . CARETLOOSE ] : re [ t . CARET ]
6049+ var r = options . loose ? safeRe [ t . CARETLOOSE ] : safeRe [ t . CARET ]
60036050 return comp . replace ( r , function ( _ , M , m , p , pr ) {
60046051 debug ( 'caret' , comp , _ , M , m , p , pr )
60056052 var ret
@@ -6058,7 +6105,7 @@ function replaceXRanges (comp, options) {
60586105
60596106function replaceXRange ( comp , options ) {
60606107 comp = comp . trim ( )
6061- var r = options . loose ? re [ t . XRANGELOOSE ] : re [ t . XRANGE ]
6108+ var r = options . loose ? safeRe [ t . XRANGELOOSE ] : safeRe [ t . XRANGE ]
60626109 return comp . replace ( r , function ( ret , gtlt , M , m , p , pr ) {
60636110 debug ( 'xRange' , comp , ret , gtlt , M , m , p , pr )
60646111 var xM = isX ( M )
@@ -6133,7 +6180,7 @@ function replaceXRange (comp, options) {
61336180function replaceStars ( comp , options ) {
61346181 debug ( 'replaceStars' , comp , options )
61356182 // Looseness is ignored here. star is always as loose as it gets!
6136- return comp . trim ( ) . replace ( re [ t . STAR ] , '' )
6183+ return comp . trim ( ) . replace ( safeRe [ t . STAR ] , '' )
61376184}
61386185
61396186// This function is passed to string.replace(re[t.HYPHENRANGE])
@@ -6459,7 +6506,7 @@ function coerce (version, options) {
64596506
64606507 var match = null
64616508 if ( ! options . rtl ) {
6462- match = version . match ( re [ t . COERCE ] )
6509+ match = version . match ( safeRe [ t . COERCE ] )
64636510 } else {
64646511 // Find the right-most coercible string that does not share
64656512 // a terminus with a more left-ward coercible string.
@@ -6470,17 +6517,17 @@ function coerce (version, options) {
64706517 // Stop when we get a match that ends at the string end, since no
64716518 // coercible string can be more right-ward without the same terminus.
64726519 var next
6473- while ( ( next = re [ t . COERCERTL ] . exec ( version ) ) &&
6520+ while ( ( next = safeRe [ t . COERCERTL ] . exec ( version ) ) &&
64746521 ( ! match || match . index + match [ 0 ] . length !== version . length )
64756522 ) {
64766523 if ( ! match ||
64776524 next . index + next [ 0 ] . length !== match . index + match [ 0 ] . length ) {
64786525 match = next
64796526 }
6480- re [ t . COERCERTL ] . lastIndex = next . index + next [ 1 ] . length + next [ 2 ] . length
6527+ safeRe [ t . COERCERTL ] . lastIndex = next . index + next [ 1 ] . length + next [ 2 ] . length
64816528 }
64826529 // leave it in a clean state
6483- re [ t . COERCERTL ] . lastIndex = - 1
6530+ safeRe [ t . COERCERTL ] . lastIndex = - 1
64846531 }
64856532
64866533 if ( match === null ) {
0 commit comments