@@ -28,9 +28,14 @@ var MAX_SAFE_COMPONENT_LENGTH = 16
28
28
29
29
// The actual regexps go on exports.re
30
30
var re = exports . re = [ ]
31
+ var safeRe = exports . safeRe = [ ]
31
32
var src = exports . src = [ ]
32
33
var R = 0
33
34
35
+ function makeSafeRe ( value ) {
36
+ return value . split ( '\\s*' ) . join ( '\\s{0,1}' ) . split ( '\\s+' ) . join ( '\\s' )
37
+ }
38
+
34
39
// The following Regular Expressions can be used for tokenizing,
35
40
// validating, and parsing SemVer version strings.
36
41
@@ -174,6 +179,7 @@ src[LONETILDE] = '(?:~>?)'
174
179
var TILDETRIM = R ++
175
180
src [ TILDETRIM ] = '(\\s*)' + src [ LONETILDE ] + '\\s+'
176
181
re [ TILDETRIM ] = new RegExp ( src [ TILDETRIM ] , 'g' )
182
+ safeRe [ TILDETRIM ] = new RegExp ( makeSafeRe ( src [ TILDETRIM ] ) , 'g' )
177
183
var tildeTrimReplace = '$1~'
178
184
179
185
var TILDE = R ++
@@ -189,6 +195,7 @@ src[LONECARET] = '(?:\\^)'
189
195
var CARETTRIM = R ++
190
196
src [ CARETTRIM ] = '(\\s*)' + src [ LONECARET ] + '\\s+'
191
197
re [ CARETTRIM ] = new RegExp ( src [ CARETTRIM ] , 'g' )
198
+ safeRe [ CARETTRIM ] = new RegExp ( makeSafeRe ( src [ CARETTRIM ] ) , 'g' )
192
199
var caretTrimReplace = '$1^'
193
200
194
201
var CARET = R ++
@@ -210,6 +217,7 @@ src[COMPARATORTRIM] = '(\\s*)' + src[GTLT] +
210
217
211
218
// this one has to use the /g flag
212
219
re [ COMPARATORTRIM ] = new RegExp ( src [ COMPARATORTRIM ] , 'g' )
220
+ safeRe [ COMPARATORTRIM ] = new RegExp ( makeSafeRe ( src [ COMPARATORTRIM ] ) , 'g' )
213
221
var comparatorTrimReplace = '$1$2$3'
214
222
215
223
// Something like `1.2.3 - 1.2.4`
@@ -238,6 +246,14 @@ for (var i = 0; i < R; i++) {
238
246
debug ( i , src [ i ] )
239
247
if ( ! re [ i ] ) {
240
248
re [ i ] = new RegExp ( src [ i ] )
249
+
250
+ // Replace all greedy whitespace to prevent regex dos issues. These regex are
251
+ // used internally via the safeRe object since all inputs in this library get
252
+ // normalized first to trim and collapse all extra whitespace. The original
253
+ // regexes are exported for userland consumption and lower level usage. A
254
+ // future breaking change could export the safer regex only with a note that
255
+ // all input should have extra whitespace removed.
256
+ safeRe [ i ] = new RegExp ( src [ i ] . split ( '\\s*' ) . join ( '\\s{0,1}' ) . split ( '\\s+' ) . join ( '\\s' ) )
241
257
}
242
258
}
243
259
@@ -262,7 +278,7 @@ function parse (version, options) {
262
278
return null
263
279
}
264
280
265
- var r = options . loose ? re [ LOOSE ] : re [ FULL ]
281
+ var r = options . loose ? safeRe [ LOOSE ] : safeRe [ FULL ]
266
282
if ( ! r . test ( version ) ) {
267
283
return null
268
284
}
@@ -317,7 +333,7 @@ function SemVer (version, options) {
317
333
this . options = options
318
334
this . loose = ! ! options . loose
319
335
320
- var m = version . trim ( ) . match ( options . loose ? re [ LOOSE ] : re [ FULL ] )
336
+ var m = version . trim ( ) . match ( options . loose ? safeRe [ LOOSE ] : safeRe [ FULL ] )
321
337
322
338
if ( ! m ) {
323
339
throw new TypeError ( 'Invalid Version: ' + version )
@@ -731,6 +747,7 @@ function Comparator (comp, options) {
731
747
return new Comparator ( comp , options )
732
748
}
733
749
750
+ comp = comp . trim ( ) . split ( / \s + / ) . join ( ' ' )
734
751
debug ( 'comparator' , comp , options )
735
752
this . options = options
736
753
this . loose = ! ! options . loose
@@ -747,7 +764,7 @@ function Comparator (comp, options) {
747
764
748
765
var ANY = { }
749
766
Comparator . prototype . parse = function ( comp ) {
750
- var r = this . options . loose ? re [ COMPARATORLOOSE ] : re [ COMPARATOR ]
767
+ var r = this . options . loose ? safeRe [ COMPARATORLOOSE ] : safeRe [ COMPARATOR ]
751
768
var m = comp . match ( r )
752
769
753
770
if ( ! m ) {
@@ -861,17 +878,24 @@ function Range (range, options) {
861
878
this . loose = ! ! options . loose
862
879
this . includePrerelease = ! ! options . includePrerelease
863
880
864
- // First, split based on boolean or ||
881
+ // First reduce all whitespace as much as possible so we do not have to rely
882
+ // on potentially slow regexes like \s*. This is then stored and used for
883
+ // future error messages as well.
865
884
this . raw = range
866
- this . set = range . split ( / \s * \| \| \s * / ) . map ( function ( range ) {
885
+ . trim ( )
886
+ . split ( / \s + / )
887
+ . join ( ' ' )
888
+
889
+ // First, split based on boolean or ||
890
+ this . set = this . raw . split ( '||' ) . map ( function ( range ) {
867
891
return this . parseRange ( range . trim ( ) )
868
892
} , this ) . filter ( function ( c ) {
869
893
// throw out any that are not relevant for whatever reason
870
894
return c . length
871
895
} )
872
896
873
897
if ( ! this . set . length ) {
874
- throw new TypeError ( 'Invalid SemVer Range: ' + range )
898
+ throw new TypeError ( 'Invalid SemVer Range: ' + this . raw )
875
899
}
876
900
877
901
this . format ( )
@@ -890,28 +914,23 @@ Range.prototype.toString = function () {
890
914
891
915
Range . prototype . parseRange = function ( range ) {
892
916
var loose = this . options . loose
893
- range = range . trim ( )
894
917
// `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4`
895
- var hr = loose ? re [ HYPHENRANGELOOSE ] : re [ HYPHENRANGE ]
918
+ var hr = loose ? safeRe [ HYPHENRANGELOOSE ] : safeRe [ HYPHENRANGE ]
896
919
range = range . replace ( hr , hyphenReplace )
897
920
debug ( 'hyphen replace' , range )
898
921
// `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5`
899
- range = range . replace ( re [ COMPARATORTRIM ] , comparatorTrimReplace )
900
- debug ( 'comparator trim' , range , re [ COMPARATORTRIM ] )
922
+ range = range . replace ( safeRe [ COMPARATORTRIM ] , comparatorTrimReplace )
923
+ debug ( 'comparator trim' , range , safeRe [ COMPARATORTRIM ] )
901
924
902
925
// `~ 1.2.3` => `~1.2.3`
903
- range = range . replace ( re [ TILDETRIM ] , tildeTrimReplace )
926
+ range = range . replace ( safeRe [ TILDETRIM ] , tildeTrimReplace )
904
927
905
928
// `^ 1.2.3` => `^1.2.3`
906
- range = range . replace ( re [ CARETTRIM ] , caretTrimReplace )
907
-
908
- // normalize spaces
909
- range = range . split ( / \s + / ) . join ( ' ' )
929
+ range = range . replace ( safeRe [ CARETTRIM ] , caretTrimReplace )
910
930
911
931
// At this point, the range is completely trimmed and
912
932
// ready to be split into comparators.
913
-
914
- var compRe = loose ? re [ COMPARATORLOOSE ] : re [ COMPARATOR ]
933
+ var compRe = loose ? safeRe [ COMPARATORLOOSE ] : safeRe [ COMPARATOR ]
915
934
var set = range . split ( ' ' ) . map ( function ( comp ) {
916
935
return parseComparator ( comp , this . options )
917
936
} , this ) . join ( ' ' ) . split ( / \s + / )
@@ -987,7 +1006,7 @@ function replaceTildes (comp, options) {
987
1006
}
988
1007
989
1008
function replaceTilde ( comp , options ) {
990
- var r = options . loose ? re [ TILDELOOSE ] : re [ TILDE ]
1009
+ var r = options . loose ? safeRe [ TILDELOOSE ] : safeRe [ TILDE ]
991
1010
return comp . replace ( r , function ( _ , M , m , p , pr ) {
992
1011
debug ( 'tilde' , comp , _ , M , m , p , pr )
993
1012
var ret
@@ -1028,7 +1047,7 @@ function replaceCarets (comp, options) {
1028
1047
1029
1048
function replaceCaret ( comp , options ) {
1030
1049
debug ( 'caret' , comp , options )
1031
- var r = options . loose ? re [ CARETLOOSE ] : re [ CARET ]
1050
+ var r = options . loose ? safeRe [ CARETLOOSE ] : safeRe [ CARET ]
1032
1051
return comp . replace ( r , function ( _ , M , m , p , pr ) {
1033
1052
debug ( 'caret' , comp , _ , M , m , p , pr )
1034
1053
var ret
@@ -1087,7 +1106,7 @@ function replaceXRanges (comp, options) {
1087
1106
1088
1107
function replaceXRange ( comp , options ) {
1089
1108
comp = comp . trim ( )
1090
- var r = options . loose ? re [ XRANGELOOSE ] : re [ XRANGE ]
1109
+ var r = options . loose ? safeRe [ XRANGELOOSE ] : safeRe [ XRANGE ]
1091
1110
return comp . replace ( r , function ( ret , gtlt , M , m , p , pr ) {
1092
1111
debug ( 'xRange' , comp , ret , gtlt , M , m , p , pr )
1093
1112
var xM = isX ( M )
@@ -1157,10 +1176,10 @@ function replaceXRange (comp, options) {
1157
1176
function replaceStars ( comp , options ) {
1158
1177
debug ( 'replaceStars' , comp , options )
1159
1178
// Looseness is ignored here. star is always as loose as it gets!
1160
- return comp . trim ( ) . replace ( re [ STAR ] , '' )
1179
+ return comp . trim ( ) . replace ( safeRe [ STAR ] , '' )
1161
1180
}
1162
1181
1163
- // This function is passed to string.replace(re [HYPHENRANGE])
1182
+ // This function is passed to string.replace(safeRe [HYPHENRANGE])
1164
1183
// M, m, patch, prerelease, build
1165
1184
// 1.2 - 3.4.5 => >=1.2.0 <=3.4.5
1166
1185
// 1.2.3 - 3.4 => >=1.2.0 <3.5.0 Any 3.4.x will do
@@ -1471,7 +1490,7 @@ function coerce (version) {
1471
1490
return null
1472
1491
}
1473
1492
1474
- var match = version . match ( re [ COERCE ] )
1493
+ var match = version . match ( safeRe [ COERCE ] )
1475
1494
1476
1495
if ( match == null ) {
1477
1496
return null
0 commit comments