@@ -26,11 +26,39 @@ var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER ||
26
26
// Max safe segment length for coercion.
27
27
var MAX_SAFE_COMPONENT_LENGTH = 16
28
28
29
+ var MAX_SAFE_BUILD_LENGTH = MAX_LENGTH - 6
30
+
29
31
// The actual regexps go on exports.re
30
32
var re = exports . re = [ ]
33
+ var safeRe = exports . safeRe = [ ]
31
34
var src = exports . src = [ ]
32
35
var R = 0
33
36
37
+ var LETTERDASHNUMBER = '[a-zA-Z0-9-]'
38
+
39
+ // Replace some greedy regex tokens to prevent regex dos issues. These regex are
40
+ // used internally via the safeRe object since all inputs in this library get
41
+ // normalized first to trim and collapse all extra whitespace. The original
42
+ // regexes are exported for userland consumption and lower level usage. A
43
+ // future breaking change could export the safer regex only with a note that
44
+ // all input should have extra whitespace removed.
45
+ var safeRegexReplacements = [
46
+ [ '\\s' , 1 ] ,
47
+ [ '\\d' , MAX_LENGTH ] ,
48
+ [ LETTERDASHNUMBER , MAX_SAFE_BUILD_LENGTH ] ,
49
+ ]
50
+
51
+ function makeSafeRe ( value ) {
52
+ for ( var i = 0 ; i < safeRegexReplacements . length ; i ++ ) {
53
+ var token = safeRegexReplacements [ i ] [ 0 ]
54
+ var max = safeRegexReplacements [ i ] [ 1 ]
55
+ value = value
56
+ . split ( token + '*' ) . join ( token + '{0,' + max + '}' )
57
+ . split ( token + '+' ) . join ( token + '{1,' + max + '}' )
58
+ }
59
+ return value
60
+ }
61
+
34
62
// The following Regular Expressions can be used for tokenizing,
35
63
// validating, and parsing SemVer version strings.
36
64
@@ -40,14 +68,14 @@ var R = 0
40
68
var NUMERICIDENTIFIER = R ++
41
69
src [ NUMERICIDENTIFIER ] = '0|[1-9]\\d*'
42
70
var NUMERICIDENTIFIERLOOSE = R ++
43
- src [ NUMERICIDENTIFIERLOOSE ] = '[0-9] +'
71
+ src [ NUMERICIDENTIFIERLOOSE ] = '\\d +'
44
72
45
73
// ## Non-numeric Identifier
46
74
// Zero or more digits, followed by a letter or hyphen, and then zero or
47
75
// more letters, digits, or hyphens.
48
76
49
77
var NONNUMERICIDENTIFIER = R ++
50
- src [ NONNUMERICIDENTIFIER ] = '\\d*[a-zA-Z-][a-zA-Z0-9-] *'
78
+ src [ NONNUMERICIDENTIFIER ] = '\\d*[a-zA-Z-]' + LETTERDASHNUMBER + ' *'
51
79
52
80
// ## Main Version
53
81
// Three dot-separated numeric identifiers.
@@ -89,7 +117,7 @@ src[PRERELEASELOOSE] = '(?:-?(' + src[PRERELEASEIDENTIFIERLOOSE] +
89
117
// Any combination of digits, letters, or hyphens.
90
118
91
119
var BUILDIDENTIFIER = R ++
92
- src [ BUILDIDENTIFIER ] = '[0-9A-Za-z-] +'
120
+ src [ BUILDIDENTIFIER ] = LETTERDASHNUMBER + ' +'
93
121
94
122
// ## Build Metadata
95
123
// Plus sign, followed by one or more period-separated build metadata
@@ -174,6 +202,7 @@ src[LONETILDE] = '(?:~>?)'
174
202
var TILDETRIM = R ++
175
203
src [ TILDETRIM ] = '(\\s*)' + src [ LONETILDE ] + '\\s+'
176
204
re [ TILDETRIM ] = new RegExp ( src [ TILDETRIM ] , 'g' )
205
+ safeRe [ TILDETRIM ] = new RegExp ( makeSafeRe ( src [ TILDETRIM ] ) , 'g' )
177
206
var tildeTrimReplace = '$1~'
178
207
179
208
var TILDE = R ++
@@ -189,6 +218,7 @@ src[LONECARET] = '(?:\\^)'
189
218
var CARETTRIM = R ++
190
219
src [ CARETTRIM ] = '(\\s*)' + src [ LONECARET ] + '\\s+'
191
220
re [ CARETTRIM ] = new RegExp ( src [ CARETTRIM ] , 'g' )
221
+ safeRe [ CARETTRIM ] = new RegExp ( makeSafeRe ( src [ CARETTRIM ] ) , 'g' )
192
222
var caretTrimReplace = '$1^'
193
223
194
224
var CARET = R ++
@@ -210,6 +240,7 @@ src[COMPARATORTRIM] = '(\\s*)' + src[GTLT] +
210
240
211
241
// this one has to use the /g flag
212
242
re [ COMPARATORTRIM ] = new RegExp ( src [ COMPARATORTRIM ] , 'g' )
243
+ safeRe [ COMPARATORTRIM ] = new RegExp ( makeSafeRe ( src [ COMPARATORTRIM ] ) , 'g' )
213
244
var comparatorTrimReplace = '$1$2$3'
214
245
215
246
// Something like `1.2.3 - 1.2.4`
@@ -238,6 +269,14 @@ for (var i = 0; i < R; i++) {
238
269
debug ( i , src [ i ] )
239
270
if ( ! re [ i ] ) {
240
271
re [ i ] = new RegExp ( src [ i ] )
272
+
273
+ // Replace all greedy whitespace to prevent regex dos issues. These regex are
274
+ // used internally via the safeRe object since all inputs in this library get
275
+ // normalized first to trim and collapse all extra whitespace. The original
276
+ // regexes are exported for userland consumption and lower level usage. A
277
+ // future breaking change could export the safer regex only with a note that
278
+ // all input should have extra whitespace removed.
279
+ safeRe [ i ] = new RegExp ( makeSafeRe ( src [ i ] ) )
241
280
}
242
281
}
243
282
@@ -262,7 +301,7 @@ function parse (version, options) {
262
301
return null
263
302
}
264
303
265
- var r = options . loose ? re [ LOOSE ] : re [ FULL ]
304
+ var r = options . loose ? safeRe [ LOOSE ] : safeRe [ FULL ]
266
305
if ( ! r . test ( version ) ) {
267
306
return null
268
307
}
@@ -317,7 +356,7 @@ function SemVer (version, options) {
317
356
this . options = options
318
357
this . loose = ! ! options . loose
319
358
320
- var m = version . trim ( ) . match ( options . loose ? re [ LOOSE ] : re [ FULL ] )
359
+ var m = version . trim ( ) . match ( options . loose ? safeRe [ LOOSE ] : safeRe [ FULL ] )
321
360
322
361
if ( ! m ) {
323
362
throw new TypeError ( 'Invalid Version: ' + version )
@@ -731,6 +770,7 @@ function Comparator (comp, options) {
731
770
return new Comparator ( comp , options )
732
771
}
733
772
773
+ comp = comp . trim ( ) . split ( / \s + / ) . join ( ' ' )
734
774
debug ( 'comparator' , comp , options )
735
775
this . options = options
736
776
this . loose = ! ! options . loose
@@ -747,7 +787,7 @@ function Comparator (comp, options) {
747
787
748
788
var ANY = { }
749
789
Comparator . prototype . parse = function ( comp ) {
750
- var r = this . options . loose ? re [ COMPARATORLOOSE ] : re [ COMPARATOR ]
790
+ var r = this . options . loose ? safeRe [ COMPARATORLOOSE ] : safeRe [ COMPARATOR ]
751
791
var m = comp . match ( r )
752
792
753
793
if ( ! m ) {
@@ -861,17 +901,24 @@ function Range (range, options) {
861
901
this . loose = ! ! options . loose
862
902
this . includePrerelease = ! ! options . includePrerelease
863
903
864
- // First, split based on boolean or ||
904
+ // First reduce all whitespace as much as possible so we do not have to rely
905
+ // on potentially slow regexes like \s*. This is then stored and used for
906
+ // future error messages as well.
865
907
this . raw = range
866
- this . set = range . split ( / \s * \| \| \s * / ) . map ( function ( range ) {
908
+ . trim ( )
909
+ . split ( / \s + / )
910
+ . join ( ' ' )
911
+
912
+ // First, split based on boolean or ||
913
+ this . set = this . raw . split ( '||' ) . map ( function ( range ) {
867
914
return this . parseRange ( range . trim ( ) )
868
915
} , this ) . filter ( function ( c ) {
869
916
// throw out any that are not relevant for whatever reason
870
917
return c . length
871
918
} )
872
919
873
920
if ( ! this . set . length ) {
874
- throw new TypeError ( 'Invalid SemVer Range: ' + range )
921
+ throw new TypeError ( 'Invalid SemVer Range: ' + this . raw )
875
922
}
876
923
877
924
this . format ( )
@@ -890,28 +937,23 @@ Range.prototype.toString = function () {
890
937
891
938
Range . prototype . parseRange = function ( range ) {
892
939
var loose = this . options . loose
893
- range = range . trim ( )
894
940
// `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4`
895
- var hr = loose ? re [ HYPHENRANGELOOSE ] : re [ HYPHENRANGE ]
941
+ var hr = loose ? safeRe [ HYPHENRANGELOOSE ] : safeRe [ HYPHENRANGE ]
896
942
range = range . replace ( hr , hyphenReplace )
897
943
debug ( 'hyphen replace' , range )
898
944
// `> 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 ] )
945
+ range = range . replace ( safeRe [ COMPARATORTRIM ] , comparatorTrimReplace )
946
+ debug ( 'comparator trim' , range , safeRe [ COMPARATORTRIM ] )
901
947
902
948
// `~ 1.2.3` => `~1.2.3`
903
- range = range . replace ( re [ TILDETRIM ] , tildeTrimReplace )
949
+ range = range . replace ( safeRe [ TILDETRIM ] , tildeTrimReplace )
904
950
905
951
// `^ 1.2.3` => `^1.2.3`
906
- range = range . replace ( re [ CARETTRIM ] , caretTrimReplace )
907
-
908
- // normalize spaces
909
- range = range . split ( / \s + / ) . join ( ' ' )
952
+ range = range . replace ( safeRe [ CARETTRIM ] , caretTrimReplace )
910
953
911
954
// At this point, the range is completely trimmed and
912
955
// ready to be split into comparators.
913
-
914
- var compRe = loose ? re [ COMPARATORLOOSE ] : re [ COMPARATOR ]
956
+ var compRe = loose ? safeRe [ COMPARATORLOOSE ] : safeRe [ COMPARATOR ]
915
957
var set = range . split ( ' ' ) . map ( function ( comp ) {
916
958
return parseComparator ( comp , this . options )
917
959
} , this ) . join ( ' ' ) . split ( / \s + / )
@@ -987,7 +1029,7 @@ function replaceTildes (comp, options) {
987
1029
}
988
1030
989
1031
function replaceTilde ( comp , options ) {
990
- var r = options . loose ? re [ TILDELOOSE ] : re [ TILDE ]
1032
+ var r = options . loose ? safeRe [ TILDELOOSE ] : safeRe [ TILDE ]
991
1033
return comp . replace ( r , function ( _ , M , m , p , pr ) {
992
1034
debug ( 'tilde' , comp , _ , M , m , p , pr )
993
1035
var ret
@@ -1028,7 +1070,7 @@ function replaceCarets (comp, options) {
1028
1070
1029
1071
function replaceCaret ( comp , options ) {
1030
1072
debug ( 'caret' , comp , options )
1031
- var r = options . loose ? re [ CARETLOOSE ] : re [ CARET ]
1073
+ var r = options . loose ? safeRe [ CARETLOOSE ] : safeRe [ CARET ]
1032
1074
return comp . replace ( r , function ( _ , M , m , p , pr ) {
1033
1075
debug ( 'caret' , comp , _ , M , m , p , pr )
1034
1076
var ret
@@ -1087,7 +1129,7 @@ function replaceXRanges (comp, options) {
1087
1129
1088
1130
function replaceXRange ( comp , options ) {
1089
1131
comp = comp . trim ( )
1090
- var r = options . loose ? re [ XRANGELOOSE ] : re [ XRANGE ]
1132
+ var r = options . loose ? safeRe [ XRANGELOOSE ] : safeRe [ XRANGE ]
1091
1133
return comp . replace ( r , function ( ret , gtlt , M , m , p , pr ) {
1092
1134
debug ( 'xRange' , comp , ret , gtlt , M , m , p , pr )
1093
1135
var xM = isX ( M )
@@ -1157,10 +1199,10 @@ function replaceXRange (comp, options) {
1157
1199
function replaceStars ( comp , options ) {
1158
1200
debug ( 'replaceStars' , comp , options )
1159
1201
// Looseness is ignored here. star is always as loose as it gets!
1160
- return comp . trim ( ) . replace ( re [ STAR ] , '' )
1202
+ return comp . trim ( ) . replace ( safeRe [ STAR ] , '' )
1161
1203
}
1162
1204
1163
- // This function is passed to string.replace(re [HYPHENRANGE])
1205
+ // This function is passed to string.replace(safeRe [HYPHENRANGE])
1164
1206
// M, m, patch, prerelease, build
1165
1207
// 1.2 - 3.4.5 => >=1.2.0 <=3.4.5
1166
1208
// 1.2.3 - 3.4 => >=1.2.0 <3.5.0 Any 3.4.x will do
@@ -1471,7 +1513,7 @@ function coerce (version) {
1471
1513
return null
1472
1514
}
1473
1515
1474
- var match = version . match ( re [ COERCE ] )
1516
+ var match = version . match ( safeRe [ COERCE ] )
1475
1517
1476
1518
if ( match == null ) {
1477
1519
return null
0 commit comments