Skip to content

Commit bf5a0ed

Browse files
committedMar 14, 2021
Merge branch 'template-variable-parameter'
2 parents 798eafa + 7e3d404 commit bf5a0ed

20 files changed

+345
-243
lines changed
 

‎docs/modules/_setup.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -850,7 +850,7 @@ <h1>_setup.js</h1>
850850

851851
</div>
852852

853-
<div class="content"><div class='highlight'><pre><span class="hljs-keyword">export</span> <span class="hljs-keyword">var</span> VERSION = <span class="hljs-string">'1.12.0'</span>;</pre></div></div>
853+
<div class="content"><div class='highlight'><pre><span class="hljs-keyword">export</span> <span class="hljs-keyword">var</span> VERSION = <span class="hljs-string">'1.12.1'</span>;</pre></div></div>
854854

855855
</li>
856856

‎docs/modules/debounce.html

+34-16
Original file line numberDiff line numberDiff line change
@@ -850,7 +850,7 @@ <h1>debounce.js</h1>
850850
</div>
851851

852852
<div class="content"><div class='highlight'><pre><span class="hljs-keyword">import</span> restArguments <span class="hljs-keyword">from</span> <span class="hljs-string">'./restArguments.js'</span>;
853-
<span class="hljs-keyword">import</span> delay <span class="hljs-keyword">from</span> <span class="hljs-string">'./delay.js'</span>;</pre></div></div>
853+
<span class="hljs-keyword">import</span> now <span class="hljs-keyword">from</span> <span class="hljs-string">'./now.js'</span>;</pre></div></div>
854854

855855
</li>
856856

@@ -869,29 +869,47 @@ <h1>debounce.js</h1>
869869
</div>
870870

871871
<div class="content"><div class='highlight'><pre><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">debounce</span>(<span class="hljs-params">func, wait, immediate</span>) </span>{
872-
<span class="hljs-keyword">var</span> timeout, result;
872+
<span class="hljs-keyword">var</span> timeout, previous, args, result, context;
873873

874-
<span class="hljs-keyword">var</span> later = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">context, args</span>) </span>{
875-
timeout = <span class="hljs-literal">null</span>;
876-
<span class="hljs-keyword">if</span> (args) result = func.apply(context, args);
874+
<span class="hljs-keyword">var</span> later = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
875+
<span class="hljs-keyword">var</span> passed = now() - previous;
876+
<span class="hljs-keyword">if</span> (wait &gt; passed) {
877+
timeout = <span class="hljs-keyword">set</span><span class="hljs-title">Timeout</span>(<span class="hljs-params">later, wait - passed</span>);
878+
} <span class="hljs-title">else</span> {
879+
timeout = <span class="hljs-literal">null</span>;
880+
<span class="hljs-keyword">if</span> (!immediate) result = func.apply(context, args);</pre></div></div>
881+
882+
</li>
883+
884+
885+
<li id="section-3">
886+
<div class="annotation">
887+
888+
<div class="pilwrap ">
889+
<a class="pilcrow" href="#section-3">&#182;</a>
890+
</div>
891+
<p>This check is needed because <code>func</code> can recursively invoke <code>debounced</code>.</p>
892+
893+
</div>
894+
895+
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (!timeout) args = context = <span class="hljs-literal">null</span>;
896+
}
877897
};
878898

879-
<span class="hljs-keyword">var</span> debounced = restArguments(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">args</span>) </span>{
880-
<span class="hljs-keyword">if</span> (timeout) clearTimeout(timeout);
881-
<span class="hljs-keyword">if</span> (immediate) {
882-
<span class="hljs-keyword">var</span> callNow = !timeout;
899+
<span class="hljs-keyword">var</span> debounced = restArguments(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">_args</span>) </span>{
900+
context = <span class="hljs-keyword">this</span>;
901+
args = _args;
902+
previous = now();
903+
<span class="hljs-keyword">if</span> (!timeout) {
883904
timeout = <span class="hljs-keyword">set</span><span class="hljs-title">Timeout</span>(<span class="hljs-params">later, wait</span>);
884-
<span class="hljs-title">if</span> (<span class="hljs-params">callNow</span>) <span class="hljs-title">result</span> = <span class="hljs-title">func</span>.<span class="hljs-title">apply</span>(<span class="hljs-params">this, args</span>);
885-
} <span class="hljs-title">else</span> {
886-
timeout = delay(later, wait, <span class="hljs-keyword">this</span>, args);
905+
<span class="hljs-title">if</span> (<span class="hljs-params">immediate</span>) <span class="hljs-title">result</span> = <span class="hljs-title">func</span>.<span class="hljs-title">apply</span>(<span class="hljs-params">context, args</span>);
887906
}
888-
889-
<span class="hljs-keyword">return</span> result;
907+
<span class="hljs-title">return</span> <span class="hljs-title">result</span>;
890908
});
891909

892-
debounced.cancel = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
910+
<span class="hljs-title">debounced</span>.<span class="hljs-title">cancel</span> = <span class="hljs-title">function</span>() {
893911
clearTimeout(timeout);
894-
timeout = <span class="hljs-literal">null</span>;
912+
timeout = args = context = <span class="hljs-literal">null</span>;
895913
};
896914

897915
<span class="hljs-keyword">return</span> debounced;

‎docs/modules/index.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -865,7 +865,7 @@ <h1 id="named-exports">Named Exports</h1>
865865
<div class="pilwrap ">
866866
<a class="pilcrow" href="#section-3">&#182;</a>
867867
</div>
868-
<pre><code>Underscore.js <span class="hljs-number">1.12</span><span class="hljs-number">.0</span>
868+
<pre><code>Underscore.js <span class="hljs-number">1.12</span><span class="hljs-number">.1</span>
869869
<span class="hljs-attr">https</span>:<span class="hljs-comment">//underscorejs.org</span>
870870
(c) <span class="hljs-number">2009</span><span class="hljs-number">-2020</span> Jeremy Ashkenas, DocumentCloud and Investigative Reporters &amp; Editors
871871
Underscore may be freely distributed under the MIT license.</code></pre>

‎docs/modules/template.html

+14-6
Original file line numberDiff line numberDiff line change
@@ -897,7 +897,9 @@ <h1>template.js</h1>
897897

898898
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">escapeChar</span>(<span class="hljs-params">match</span>) </span>{
899899
<span class="hljs-keyword">return</span> <span class="hljs-string">'\\'</span> + escapes[match];
900-
}</pre></div></div>
900+
}
901+
902+
<span class="hljs-keyword">var</span> bareIdentifier = <span class="hljs-regexp">/^\s*(\w|\$)+\s*$/</span>;</pre></div></div>
901903

902904
</li>
903905

@@ -980,7 +982,12 @@ <h1>template.js</h1>
980982

981983
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">return</span> match;
982984
});
983-
source += <span class="hljs-string">"';\n"</span>;</pre></div></div>
985+
source += <span class="hljs-string">"';\n"</span>;
986+
987+
<span class="hljs-keyword">var</span> argument = settings.variable;
988+
<span class="hljs-keyword">if</span> (argument) {
989+
<span class="hljs-keyword">if</span> (!bareIdentifier.test(argument)) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(argument);
990+
} <span class="hljs-keyword">else</span> {</pre></div></div>
984991

985992
</li>
986993

@@ -995,15 +1002,17 @@ <h1>template.js</h1>
9951002

9961003
</div>
9971004

998-
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (!settings.variable) source = <span class="hljs-string">'with(obj||{}){\n'</span> + source + <span class="hljs-string">'}\n'</span>;
1005+
<div class="content"><div class='highlight'><pre> source = <span class="hljs-string">'with(obj||{}){\n'</span> + source + <span class="hljs-string">'}\n'</span>;
1006+
argument = <span class="hljs-string">'obj'</span>;
1007+
}
9991008

10001009
source = <span class="hljs-string">"var __t,__p='',__j=Array.prototype.join,"</span> +
10011010
<span class="hljs-string">"print=function(){__p+=__j.call(arguments,'');};\n"</span> +
10021011
source + <span class="hljs-string">'return __p;\n'</span>;
10031012

10041013
<span class="hljs-keyword">var</span> render;
10051014
<span class="hljs-keyword">try</span> {
1006-
render = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Function</span>(settings.variable || <span class="hljs-string">'obj'</span>, <span class="hljs-string">'_'</span>, source);
1015+
render = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Function</span>(argument, <span class="hljs-string">'_'</span>, source);
10071016
} <span class="hljs-keyword">catch</span> (e) {
10081017
e.source = source;
10091018
<span class="hljs-keyword">throw</span> e;
@@ -1026,8 +1035,7 @@ <h1>template.js</h1>
10261035

10271036
</div>
10281037

1029-
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> argument = settings.variable || <span class="hljs-string">'obj'</span>;
1030-
template.source = <span class="hljs-string">'function('</span> + argument + <span class="hljs-string">'){\n'</span> + source + <span class="hljs-string">'}'</span>;
1038+
<div class="content"><div class='highlight'><pre> template.source = <span class="hljs-string">'function('</span> + argument + <span class="hljs-string">'){\n'</span> + source + <span class="hljs-string">'}'</span>;
10311039

10321040
<span class="hljs-keyword">return</span> template;
10331041
}</pre></div></div>

‎docs/underscore-esm.html

+193-167
Large diffs are not rendered by default.

‎index.html

+32-11
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@
186186
<div id="sidebar" class="interface">
187187

188188
<a class="toc_title" href="#">
189-
Underscore.js <span class="version">(1.12.0)</span>
189+
Underscore.js <span class="version">(1.12.1)</span>
190190
</a>
191191
<ul class="toc_section">
192192
<li>&raquo; <a href="https://github.com/jashkenas/underscore">GitHub Repository</a></li>
@@ -476,7 +476,7 @@
476476
<i>Underscore is an open-source component of <a href="https://documentcloud.org/">DocumentCloud</a>.</i>
477477
</p>
478478

479-
<h2>v1.12.0 Downloads <i style="padding-left: 12px; font-size:12px;">(Right-click, and use "Save As")</i></h2>
479+
<h2>v1.12.1 Downloads <i style="padding-left: 12px; font-size:12px;">(Right-click, and use "Save As")</i></h2>
480480

481481
<table>
482482
<tr>
@@ -520,32 +520,32 @@ <h2>v1.12.0 Downloads <i style="padding-left: 12px; font-size:12px;">(Right-clic
520520
</tr>
521521
</table>
522522

523-
<h2>v1.12.0 CDN URLs <i style="padding-left: 12px; font-size:12px;">(Use with <tt>&lt;script src="..."&gt;&lt;/script&gt;</tt>)</i></h2>
523+
<h2>v1.12.1 CDN URLs <i style="padding-left: 12px; font-size:12px;">(Use with <tt>&lt;script src="..."&gt;&lt;/script&gt;</tt>)</i></h2>
524524

525525
<ul>
526526
<li>
527-
<tt>https://cdn.jsdelivr.net/npm/underscore@1.12.0/underscore-min.js</tt>
527+
<tt>https://cdn.jsdelivr.net/npm/underscore@1.12.1/underscore-min.js</tt>
528528
</li>
529529
<li>
530-
<tt>https://cdn.jsdelivr.net/npm/underscore@1.12.0/underscore-esm-min.js</tt>
530+
<tt>https://cdn.jsdelivr.net/npm/underscore@1.12.1/underscore-esm-min.js</tt>
531531
</li>
532532
<li>
533-
<tt>https://unpkg.com/underscore@1.12.0/underscore-min.js</tt>
533+
<tt>https://unpkg.com/underscore@1.12.1/underscore-min.js</tt>
534534
</li>
535535
<li>
536-
<tt>https://unpkg.com/underscore@1.12.0/underscore-esm-min.js</tt>
536+
<tt>https://unpkg.com/underscore@1.12.1/underscore-esm-min.js</tt>
537537
</li>
538538
<li>
539-
<tt>https://pagecdn.io/lib/underscore/1.12.0/underscore-min.js</tt>
539+
<tt>https://pagecdn.io/lib/underscore/1.12.1/underscore-min.js</tt>
540540
</li>
541541
<li>
542-
<tt>https://pagecdn.io/lib/underscore/1.12.0/underscore-esm-min.js</tt>
542+
<tt>https://pagecdn.io/lib/underscore/1.12.1/underscore-esm-min.js</tt>
543543
</li>
544544
<li>
545-
<tt>https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.12.0/underscore-min.js</tt>
545+
<tt>https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.12.1/underscore-min.js</tt>
546546
</li>
547547
<li>
548-
<tt>https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.12.0/underscore-esm-min.js</tt>
548+
<tt>https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.12.1/underscore-esm-min.js</tt>
549549
</li>
550550
</ul>
551551

@@ -2705,6 +2705,27 @@ <h2 id="notes">Notes</h2>
27052705

27062706
<h2 id="changelog">Change Log</h2>
27072707

2708+
<p id="1.12.1">
2709+
<b class="header">1.12.1</b> &mdash; <small><i>March 15, 2021</i></small><br />
2710+
<ul>
2711+
<li>
2712+
Fixes a security issue in <tt>_.template</tt> that could enable a
2713+
third party to inject code in compiled templates. This issue
2714+
affects all versions of Underscore between 1.3.2 and 1.12.0,
2715+
inclusive, as well as preview releases 1.13.0-0 and 1.13.0-1. The
2716+
fix in this release is also included in the parallel preview
2717+
release 1.13.0-2.
2718+
</li>
2719+
<li>
2720+
Restores an optimization in <tt>_.debounce</tt> that was
2721+
unintentionally lost in version 1.9.0.
2722+
</li>
2723+
<li>
2724+
Various test and documentation enhancements.
2725+
</li>
2726+
</ul>
2727+
</p>
2728+
27082729
<p id="1.12.0">
27092730
<b class="header">1.12.0</b> &mdash; <small><i>November 24, 2020</i></small> &mdash; <a href="https://github.com/jashkenas/underscore/compare/1.11.0...1.12.0">Diff</a> &mdash; <a href="https://cdn.statically.io/gh/jashkenas/underscore/1.12.0/index.html">Docs</a><br />
27102731
<ul>

‎modules/_setup.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Current version.
2-
export var VERSION = '1.12.0';
2+
export var VERSION = '1.12.1';
33

44
// Establish the root object, `window` (`self`) in the browser, `global`
55
// on the server, or `this` in some virtual machines. We use `self`

‎modules/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Named Exports
22
// =============
33

4-
// Underscore.js 1.12.0
4+
// Underscore.js 1.12.1
55
// https://underscorejs.org
66
// (c) 2009-2020 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
77
// Underscore may be freely distributed under the MIT license.

‎modules/template.js

+11-4
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ function escapeChar(match) {
2424
return '\\' + escapes[match];
2525
}
2626

27+
var bareIdentifier = /^\s*(\w|\$)+\s*$/;
28+
2729
// JavaScript micro-templating, similar to John Resig's implementation.
2830
// Underscore templating handles arbitrary delimiters, preserves whitespace,
2931
// and correctly escapes quotes within interpolated code.
@@ -59,16 +61,22 @@ export default function template(text, settings, oldSettings) {
5961
});
6062
source += "';\n";
6163

62-
// If a variable is not specified, place data values in local scope.
63-
if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
64+
var argument = settings.variable;
65+
if (argument) {
66+
if (!bareIdentifier.test(argument)) throw new Error(argument);
67+
} else {
68+
// If a variable is not specified, place data values in local scope.
69+
source = 'with(obj||{}){\n' + source + '}\n';
70+
argument = 'obj';
71+
}
6472

6573
source = "var __t,__p='',__j=Array.prototype.join," +
6674
"print=function(){__p+=__j.call(arguments,'');};\n" +
6775
source + 'return __p;\n';
6876

6977
var render;
7078
try {
71-
render = new Function(settings.variable || 'obj', '_', source);
79+
render = new Function(argument, '_', source);
7280
} catch (e) {
7381
e.source = source;
7482
throw e;
@@ -79,7 +87,6 @@ export default function template(text, settings, oldSettings) {
7987
};
8088

8189
// Provide the compiled source as a convenience for precompilation.
82-
var argument = settings.variable || 'obj';
8390
template.source = 'function(' + argument + '){\n' + source + '}';
8491

8592
return template;

‎package-lock.json

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
},
1717
"main": "underscore.js",
1818
"module": "modules/index-all.js",
19-
"version": "1.12.0",
19+
"version": "1.12.1",
2020
"devDependencies": {
2121
"coveralls": "^2.11.2",
2222
"docco": "^0.8.0",

‎test/utility.js

+21-13
Original file line numberDiff line numberDiff line change
@@ -465,19 +465,27 @@
465465
assert.strictEqual(template(), '<<\nx\n>>');
466466
});
467467

468-
QUnit.test('#2911 - _.template must not trigger CVE-2021-23337.', function(assert) {
469-
QUnit.holyProperty = 'holy';
470-
var invalidVariableNames = [
471-
'){delete QUnit.holyProperty}; with(obj',
472-
'(x = QUnit.holyProperty = "evil"), obj',
473-
'document.write("got you!")'
474-
];
475-
_.each(invalidVariableNames, function(name) {
476-
assert.throws(function() { _.template('', { variable: name })(); });
477-
});
478-
var holy = QUnit.holyProperty;
479-
delete QUnit.holyProperty;
480-
assert.strictEqual(holy, 'holy');
468+
QUnit.test('#2911 - _.templateSettings.variable must not allow third parties to inject code.', function(assert) {
469+
QUnit.holyProperty = 'holy';
470+
var invalidVariableNames = [
471+
'){delete QUnit.holyProperty}; with(obj',
472+
'(x = QUnit.holyProperty = "evil"), obj',
473+
'document.write("got you!")',
474+
'a = (function() { delete QUnit.holyProperty; }())',
475+
'a = (QUnit.holyProperty = "evil")',
476+
'a = document.write("got you!")'
477+
];
478+
_.each(invalidVariableNames, function(name) {
479+
_.templateSettings.variable = name;
480+
assert.throws(function() {
481+
_.template('')();
482+
}, 'code injection through _.templateSettings.variable: ' + name);
483+
delete _.templateSettings.variable;
484+
});
485+
var holy = QUnit.holyProperty;
486+
delete QUnit.holyProperty;
487+
assert.strictEqual(holy, 'holy', '_.template variable cannot touch global state');
488+
assert.ok(_.isUndefined(_.templateSettings.variable), 'cleanup');
481489
});
482490

483491
}());

‎underscore-esm-min.js

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎underscore-esm-min.js.map

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎underscore-esm.js

+13-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎underscore-esm.js.map

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎underscore-min.js

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎underscore-min.js.map

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎underscore.js

+13-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎underscore.js.map

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)
Please sign in to comment.