terser 5.14.1 → 5.14.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/dist/bundle.min.js +30 -14
- package/lib/compress/evaluate.js +14 -14
- package/lib/compress/index.js +8 -0
- package/lib/output.js +2 -0
- package/lib/utils/index.js +9 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## v5.14.2
|
4
|
+
|
5
|
+
- Security fix for RegExps that should not be evaluated (regexp DDOS)
|
6
|
+
- Source maps improvements (#1211)
|
7
|
+
- Performance improvements in long property access evaluation (#1213)
|
8
|
+
|
3
9
|
## v5.14.1
|
4
10
|
- keep_numbers option added to TypeScript defs (#1208)
|
5
11
|
- Fixed parsing of nested template strings (#1204)
|
@@ -206,6 +212,13 @@ Hotfix release, fixes package.json "engines" syntax
|
|
206
212
|
- Module is now distributed as a dual package - You can `import` and `require()` too.
|
207
213
|
- Inline improvements were made
|
208
214
|
|
215
|
+
|
216
|
+
-----
|
217
|
+
|
218
|
+
## v4.8.1 (backport)
|
219
|
+
|
220
|
+
- Security fix for RegExps that should not be evaluated (regexp DDOS)
|
221
|
+
|
209
222
|
## v4.8.0
|
210
223
|
|
211
224
|
- Support for numeric separators (`million = 1_000_000`) was added.
|
package/dist/bundle.min.js
CHANGED
@@ -253,7 +253,15 @@ function regexp_source_fix(source) {
|
|
253
253
|
return (escaped ? "" : "\\") + lineTerminatorEscape[match];
|
254
254
|
});
|
255
255
|
}
|
256
|
-
|
256
|
+
|
257
|
+
// Subset of regexps that is not going to cause regexp based DDOS
|
258
|
+
// https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
|
259
|
+
const re_safe_regexp = /^[\\/|\0\s\w^$.[\]()]*$/;
|
260
|
+
|
261
|
+
/** Check if the regexp is safe for Terser to create without risking a RegExp DOS */
|
262
|
+
const regexp_is_safe = (source) => re_safe_regexp.test(source);
|
263
|
+
|
264
|
+
const all_flags = "dgimsuy";
|
257
265
|
function sort_regexp_flags(flags) {
|
258
266
|
const existing_flags = new Set(flags.split(""));
|
259
267
|
let out = "";
|
@@ -9640,12 +9648,14 @@ function OutputStream(options) {
|
|
9640
9648
|
output.with_indent(output.next_indent(), function() {
|
9641
9649
|
output.append_comments(self, true);
|
9642
9650
|
});
|
9651
|
+
output.add_mapping(self.end);
|
9643
9652
|
output.print("}");
|
9644
9653
|
}
|
9645
9654
|
function print_braced(self, output, allow_directives) {
|
9646
9655
|
if (self.body.length > 0) {
|
9647
9656
|
output.with_block(function() {
|
9648
9657
|
display_body(self.body, false, output, allow_directives);
|
9658
|
+
output.add_mapping(self.end);
|
9649
9659
|
});
|
9650
9660
|
} else print_braced_empty(self, output);
|
9651
9661
|
}
|
@@ -13851,7 +13861,7 @@ def_eval(AST_BigInt, return_this);
|
|
13851
13861
|
|
13852
13862
|
def_eval(AST_RegExp, function (compressor) {
|
13853
13863
|
let evaluated = compressor.evaluated_regexps.get(this.value);
|
13854
|
-
if (evaluated === undefined) {
|
13864
|
+
if (evaluated === undefined && regexp_is_safe(this.value.source)) {
|
13855
13865
|
try {
|
13856
13866
|
const { source, flags } = this.value;
|
13857
13867
|
evaluated = new RegExp(source, flags);
|
@@ -14064,7 +14074,7 @@ const regexp_flags = new Set([
|
|
14064
14074
|
]);
|
14065
14075
|
|
14066
14076
|
def_eval(AST_PropAccess, function (compressor, depth) {
|
14067
|
-
|
14077
|
+
let obj = this.expression._eval(compressor, depth + 1);
|
14068
14078
|
if (obj === nullish || (this.optional && obj == null)) return nullish;
|
14069
14079
|
if (compressor.option("unsafe")) {
|
14070
14080
|
var key = this.property;
|
@@ -14074,7 +14084,6 @@ def_eval(AST_PropAccess, function (compressor, depth) {
|
|
14074
14084
|
return this;
|
14075
14085
|
}
|
14076
14086
|
var exp = this.expression;
|
14077
|
-
var val;
|
14078
14087
|
if (is_undeclared_ref(exp)) {
|
14079
14088
|
|
14080
14089
|
var aa;
|
@@ -14091,29 +14100,29 @@ def_eval(AST_PropAccess, function (compressor, depth) {
|
|
14091
14100
|
}
|
14092
14101
|
if (!is_pure_native_value(exp.name, key))
|
14093
14102
|
return this;
|
14094
|
-
|
14103
|
+
obj = global_objs[exp.name];
|
14095
14104
|
} else {
|
14096
|
-
|
14097
|
-
if (val instanceof RegExp) {
|
14105
|
+
if (obj instanceof RegExp) {
|
14098
14106
|
if (key == "source") {
|
14099
|
-
return regexp_source_fix(
|
14107
|
+
return regexp_source_fix(obj.source);
|
14100
14108
|
} else if (key == "flags" || regexp_flags.has(key)) {
|
14101
|
-
return
|
14109
|
+
return obj[key];
|
14102
14110
|
}
|
14103
14111
|
}
|
14104
|
-
if (!
|
14112
|
+
if (!obj || obj === exp || !HOP(obj, key))
|
14105
14113
|
return this;
|
14106
|
-
|
14114
|
+
|
14115
|
+
if (typeof obj == "function")
|
14107
14116
|
switch (key) {
|
14108
14117
|
case "name":
|
14109
|
-
return
|
14118
|
+
return obj.node.name ? obj.node.name.name : "";
|
14110
14119
|
case "length":
|
14111
|
-
return
|
14120
|
+
return obj.node.length_property();
|
14112
14121
|
default:
|
14113
14122
|
return this;
|
14114
14123
|
}
|
14115
14124
|
}
|
14116
|
-
return
|
14125
|
+
return obj[key];
|
14117
14126
|
}
|
14118
14127
|
return this;
|
14119
14128
|
});
|
@@ -18992,6 +19001,7 @@ def_optimize(AST_Call, function(self, compressor) {
|
|
18992
19001
|
params.push(value);
|
18993
19002
|
return arg !== value;
|
18994
19003
|
})
|
19004
|
+
&& regexp_is_safe(params[0])
|
18995
19005
|
) {
|
18996
19006
|
let [ source, flags ] = params;
|
18997
19007
|
source = regexp_source_fix(new RegExp(source).source);
|
@@ -20651,6 +20661,12 @@ def_optimize(AST_Dot, function(self, compressor) {
|
|
20651
20661
|
const sub = self.flatten_object(self.property, compressor);
|
20652
20662
|
if (sub) return sub.optimize(compressor);
|
20653
20663
|
}
|
20664
|
+
|
20665
|
+
if (self.expression instanceof AST_PropAccess
|
20666
|
+
&& parent instanceof AST_PropAccess) {
|
20667
|
+
return self;
|
20668
|
+
}
|
20669
|
+
|
20654
20670
|
let ev = self.evaluate(compressor);
|
20655
20671
|
if (ev !== self) {
|
20656
20672
|
ev = make_node_from_constant(ev, self).optimize(compressor);
|
package/lib/compress/evaluate.js
CHANGED
@@ -46,7 +46,8 @@ import {
|
|
46
46
|
makePredicate,
|
47
47
|
return_this,
|
48
48
|
string_template,
|
49
|
-
regexp_source_fix
|
49
|
+
regexp_source_fix,
|
50
|
+
regexp_is_safe,
|
50
51
|
} from "../utils/index.js";
|
51
52
|
import {
|
52
53
|
AST_Array,
|
@@ -129,7 +130,7 @@ def_eval(AST_BigInt, return_this);
|
|
129
130
|
|
130
131
|
def_eval(AST_RegExp, function (compressor) {
|
131
132
|
let evaluated = compressor.evaluated_regexps.get(this.value);
|
132
|
-
if (evaluated === undefined) {
|
133
|
+
if (evaluated === undefined && regexp_is_safe(this.value.source)) {
|
133
134
|
try {
|
134
135
|
const { source, flags } = this.value;
|
135
136
|
evaluated = new RegExp(source, flags);
|
@@ -342,7 +343,7 @@ const regexp_flags = new Set([
|
|
342
343
|
]);
|
343
344
|
|
344
345
|
def_eval(AST_PropAccess, function (compressor, depth) {
|
345
|
-
|
346
|
+
let obj = this.expression._eval(compressor, depth + 1);
|
346
347
|
if (obj === nullish || (this.optional && obj == null)) return nullish;
|
347
348
|
if (compressor.option("unsafe")) {
|
348
349
|
var key = this.property;
|
@@ -352,7 +353,6 @@ def_eval(AST_PropAccess, function (compressor, depth) {
|
|
352
353
|
return this;
|
353
354
|
}
|
354
355
|
var exp = this.expression;
|
355
|
-
var val;
|
356
356
|
if (is_undeclared_ref(exp)) {
|
357
357
|
|
358
358
|
var aa;
|
@@ -369,29 +369,29 @@ def_eval(AST_PropAccess, function (compressor, depth) {
|
|
369
369
|
}
|
370
370
|
if (!is_pure_native_value(exp.name, key))
|
371
371
|
return this;
|
372
|
-
|
372
|
+
obj = global_objs[exp.name];
|
373
373
|
} else {
|
374
|
-
|
375
|
-
if (val instanceof RegExp) {
|
374
|
+
if (obj instanceof RegExp) {
|
376
375
|
if (key == "source") {
|
377
|
-
return regexp_source_fix(
|
376
|
+
return regexp_source_fix(obj.source);
|
378
377
|
} else if (key == "flags" || regexp_flags.has(key)) {
|
379
|
-
return
|
378
|
+
return obj[key];
|
380
379
|
}
|
381
380
|
}
|
382
|
-
if (!
|
381
|
+
if (!obj || obj === exp || !HOP(obj, key))
|
383
382
|
return this;
|
384
|
-
|
383
|
+
|
384
|
+
if (typeof obj == "function")
|
385
385
|
switch (key) {
|
386
386
|
case "name":
|
387
|
-
return
|
387
|
+
return obj.node.name ? obj.node.name.name : "";
|
388
388
|
case "length":
|
389
|
-
return
|
389
|
+
return obj.node.length_property();
|
390
390
|
default:
|
391
391
|
return this;
|
392
392
|
}
|
393
393
|
}
|
394
|
-
return
|
394
|
+
return obj[key];
|
395
395
|
}
|
396
396
|
return this;
|
397
397
|
});
|
package/lib/compress/index.js
CHANGED
@@ -158,6 +158,7 @@ import {
|
|
158
158
|
return_true,
|
159
159
|
regexp_source_fix,
|
160
160
|
has_annotation,
|
161
|
+
regexp_is_safe,
|
161
162
|
} from "../utils/index.js";
|
162
163
|
import { first_in_statement } from "../utils/first_in_statement.js";
|
163
164
|
import { equivalent_to } from "../equivalent-to.js";
|
@@ -2140,6 +2141,7 @@ def_optimize(AST_Call, function(self, compressor) {
|
|
2140
2141
|
params.push(value);
|
2141
2142
|
return arg !== value;
|
2142
2143
|
})
|
2144
|
+
&& regexp_is_safe(params[0])
|
2143
2145
|
) {
|
2144
2146
|
let [ source, flags ] = params;
|
2145
2147
|
source = regexp_source_fix(new RegExp(source).source);
|
@@ -3799,6 +3801,12 @@ def_optimize(AST_Dot, function(self, compressor) {
|
|
3799
3801
|
const sub = self.flatten_object(self.property, compressor);
|
3800
3802
|
if (sub) return sub.optimize(compressor);
|
3801
3803
|
}
|
3804
|
+
|
3805
|
+
if (self.expression instanceof AST_PropAccess
|
3806
|
+
&& parent instanceof AST_PropAccess) {
|
3807
|
+
return self;
|
3808
|
+
}
|
3809
|
+
|
3802
3810
|
let ev = self.evaluate(compressor);
|
3803
3811
|
if (ev !== self) {
|
3804
3812
|
ev = make_node_from_constant(ev, self).optimize(compressor);
|
package/lib/output.js
CHANGED
@@ -1247,12 +1247,14 @@ function OutputStream(options) {
|
|
1247
1247
|
output.with_indent(output.next_indent(), function() {
|
1248
1248
|
output.append_comments(self, true);
|
1249
1249
|
});
|
1250
|
+
output.add_mapping(self.end);
|
1250
1251
|
output.print("}");
|
1251
1252
|
}
|
1252
1253
|
function print_braced(self, output, allow_directives) {
|
1253
1254
|
if (self.body.length > 0) {
|
1254
1255
|
output.with_block(function() {
|
1255
1256
|
display_body(self.body, false, output, allow_directives);
|
1257
|
+
output.add_mapping(self.end);
|
1256
1258
|
});
|
1257
1259
|
} else print_braced_empty(self, output);
|
1258
1260
|
}
|
package/lib/utils/index.js
CHANGED
@@ -249,7 +249,15 @@ function regexp_source_fix(source) {
|
|
249
249
|
return (escaped ? "" : "\\") + lineTerminatorEscape[match];
|
250
250
|
});
|
251
251
|
}
|
252
|
-
|
252
|
+
|
253
|
+
// Subset of regexps that is not going to cause regexp based DDOS
|
254
|
+
// https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
|
255
|
+
const re_safe_regexp = /^[\\/|\0\s\w^$.[\]()]*$/;
|
256
|
+
|
257
|
+
/** Check if the regexp is safe for Terser to create without risking a RegExp DOS */
|
258
|
+
export const regexp_is_safe = (source) => re_safe_regexp.test(source);
|
259
|
+
|
260
|
+
const all_flags = "dgimsuy";
|
253
261
|
function sort_regexp_flags(flags) {
|
254
262
|
const existing_flags = new Set(flags.split(""));
|
255
263
|
let out = "";
|