Skip to content

Commit 424a281

Browse files
committed
Apply the changes from D6LTS 6.53
1 parent 0775a64 commit 424a281

5 files changed

Lines changed: 257 additions & 2 deletions

File tree

includes/common.inc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2244,6 +2244,7 @@ function drupal_add_js($data = NULL, $type = 'module', $scope = 'header', $defer
22442244
'core' => array(
22452245
'misc/jquery.js' => array('cache' => TRUE, 'defer' => FALSE, 'preprocess' => TRUE),
22462246
'misc/jquery-extend-3.4.0.js' => array('cache' => TRUE, 'defer' => FALSE, 'preprocess' => TRUE),
2247+
'misc/jquery-html-prefilter-3.5.0-backport.js' => array('cache' => TRUE, 'defer' => FALSE, 'preprocess' => TRUE),
22472248
'misc/drupal.js' => array('cache' => TRUE, 'defer' => FALSE, 'preprocess' => TRUE),
22482249
),
22492250
'module' => array(),

includes/session.inc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,8 @@ function sess_destroy_sid($sid) {
180180
unset($_COOKIE[session_name()]);
181181
}
182182
}
183+
184+
return TRUE;
183185
}
184186

185187
/**
@@ -190,6 +192,7 @@ function sess_destroy_sid($sid) {
190192
*/
191193
function sess_destroy_uid($uid) {
192194
db_query('DELETE FROM {sessions} WHERE uid = %d', $uid);
195+
return TRUE;
193196
}
194197

195198
function sess_gc($lifetime) {
Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
/**
2+
* For jQuery versions less than 3.5.0, this replaces the jQuery.htmlPrefilter()
3+
* function with one that fixes these security vulnerabilities while also
4+
* retaining the pre-3.5.0 behavior where it's safe to do so.
5+
* - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-11022
6+
* - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-11023
7+
*
8+
* Additionally, for jQuery versions that do not have a jQuery.htmlPrefilter()
9+
* function (1.x prior to 1.12 and 2.x prior to 2.2), this adds it, and
10+
* extends the functions that need to call it to do so.
11+
*
12+
* Drupal core's jQuery version is 1.4.4, but jQuery Update can provide a
13+
* different version, so this covers all versions between 1.4.4 and 3.4.1.
14+
* The GitHub links in the code comments below link to jQuery 1.5 code, because
15+
* 1.4.4 isn't on GitHub, but the referenced code didn't change from 1.4.4 to
16+
* 1.5.
17+
*/
18+
19+
(function (jQuery) {
20+
21+
// Parts of this backport differ by jQuery version.
22+
var versionParts = jQuery.fn.jquery.split('.');
23+
var majorVersion = parseInt(versionParts[0]);
24+
var minorVersion = parseInt(versionParts[1]);
25+
26+
// No backport is needed if we're already on jQuery 3.5 or higher.
27+
if ( (majorVersion > 3) || (majorVersion === 3 && minorVersion >= 5) ) {
28+
return;
29+
}
30+
31+
// Prior to jQuery 3.5, jQuery converted XHTML-style self-closing tags to
32+
// their XML equivalent: e.g., "<div />" to "<div></div>". This is
33+
// problematic for several reasons, including that it's vulnerable to XSS
34+
// attacks. However, since this was jQuery's behavior for many years, many
35+
// Drupal modules and jQuery plugins may be relying on it. Therefore, we
36+
// preserve that behavior, but for a limited set of tags only, that we believe
37+
// to not be vulnerable. This is the set of HTML tags that satisfy all of the
38+
// following conditions:
39+
// - In DOMPurify's list of HTML tags. If an HTML tag isn't safe enough to
40+
// appear in that list, then we don't want to mess with it here either.
41+
// @see https://github.com/cure53/DOMPurify/blob/2.0.11/dist/purify.js#L128
42+
// - A normal element (not a void, template, text, or foreign element).
43+
// @see https://html.spec.whatwg.org/multipage/syntax.html#elements-2
44+
// - An element that is still defined by the current HTML specification
45+
// (not a deprecated element), because we do not want to rely on how
46+
// browsers parse deprecated elements.
47+
// @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element
48+
// - Not 'html', 'head', or 'body', because this pseudo-XHTML expansion is
49+
// designed for fragments, not entire documents.
50+
// - Not 'colgroup', because due to an idiosyncrasy of jQuery's original
51+
// regular expression, it didn't match on colgroup, and we don't want to
52+
// introduce a behavior change for that.
53+
var selfClosingTagsToReplace = [
54+
'a', 'abbr', 'address', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo',
55+
'blockquote', 'button', 'canvas', 'caption', 'cite', 'code', 'data',
56+
'datalist', 'dd', 'del', 'details', 'dfn', 'div', 'dl', 'dt', 'em',
57+
'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3',
58+
'h4', 'h5', 'h6', 'header', 'hgroup', 'i', 'ins', 'kbd', 'label', 'legend',
59+
'li', 'main', 'map', 'mark', 'menu', 'meter', 'nav', 'ol', 'optgroup',
60+
'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt',
61+
'ruby', 's', 'samp', 'section', 'select', 'small', 'source', 'span',
62+
'strong', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'tfoot', 'th',
63+
'thead', 'time', 'tr', 'u', 'ul', 'var', 'video'
64+
];
65+
66+
// Define regular expressions for <TAG/> and <TAG ATTRIBUTES/>. Doing this as
67+
// two expressions makes it easier to target <a/> without also targeting
68+
// every tag that starts with "a".
69+
var xhtmlRegExpGroup = '(' + selfClosingTagsToReplace.join('|') + ')';
70+
var whitespace = '[\\x20\\t\\r\\n\\f]';
71+
var rxhtmlTagWithoutSpaceOrAttributes = new RegExp('<' + xhtmlRegExpGroup + '\\/>', 'gi');
72+
var rxhtmlTagWithSpaceAndMaybeAttributes = new RegExp('<' + xhtmlRegExpGroup + '(' + whitespace + '[^>]*)\\/>', 'gi');
73+
74+
// jQuery 3.5 also fixed a vulnerability for when </select> appears within
75+
// an <option> or <optgroup>, but it did that in local code that we can't
76+
// backport directly. Instead, we filter such cases out. To do so, we need to
77+
// determine when jQuery would otherwise invoke the vulnerable code, which it
78+
// uses this regular expression to determine. The regular expression changed
79+
// for version 3.0.0 and changed again for 3.4.0.
80+
// @see https://github.com/jquery/jquery/blob/1.5/jquery.js#L4958
81+
// @see https://github.com/jquery/jquery/blob/3.0.0/dist/jquery.js#L4584
82+
// @see https://github.com/jquery/jquery/blob/3.4.0/dist/jquery.js#L4712
83+
var rtagName;
84+
if (majorVersion < 3) {
85+
rtagName = /<([\w:]+)/;
86+
}
87+
else if (minorVersion < 4) {
88+
rtagName = /<([a-z][^\/\0>\x20\t\r\n\f]+)/i;
89+
}
90+
else {
91+
rtagName = /<([a-z][^\/\0>\x20\t\r\n\f]*)/i;
92+
}
93+
94+
// The regular expression that jQuery uses to determine which self-closing
95+
// tags to expand to open and close tags. This is vulnerable, because it
96+
// matches all tag names except the few excluded ones. We only use this
97+
// expression for determining vulnerability. The expression changed for
98+
// version 3, but we only need to check for vulnerability in versions 1 and 2,
99+
// so we use the expression from those versions.
100+
// @see https://github.com/jquery/jquery/blob/1.5/jquery.js#L4957
101+
var rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi;
102+
103+
jQuery.extend({
104+
htmlPrefilter: function (html) {
105+
// This is how jQuery determines the first tag in the HTML.
106+
// @see https://github.com/jquery/jquery/blob/1.5/jquery.js#L5521
107+
var tag = ( rtagName.exec( html ) || [ "", "" ] )[ 1 ].toLowerCase();
108+
109+
// It is not valid HTML for <option> or <optgroup> to have <select> as
110+
// either a descendant or sibling, and attempts to inject one can cause
111+
// XSS on jQuery versions before 3.5. Since this is invalid HTML and a
112+
// possible XSS attack, reject the entire string.
113+
// @see https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-11023
114+
if ((tag === 'option' || tag === 'optgroup') && html.match(/<\/?select/i)) {
115+
html = '';
116+
}
117+
118+
// Retain jQuery's prior to 3.5 conversion of pseudo-XHTML, but for only
119+
// the tags in the `selfClosingTagsToReplace` list defined above.
120+
// @see https://github.com/jquery/jquery/blob/1.5/jquery.js#L5518
121+
// @see https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-11022
122+
html = html.replace(rxhtmlTagWithoutSpaceOrAttributes, "<$1></$1>");
123+
html = html.replace(rxhtmlTagWithSpaceAndMaybeAttributes, "<$1$2></$1>");
124+
125+
// Prior to jQuery 1.12 and 2.2, this function gets called (via code later
126+
// in this file) in addition to, rather than instead of, the unsafe
127+
// expansion of self-closing tags (including ones not in the list above).
128+
// We can't prevent that unsafe expansion from running, so instead we
129+
// check to make sure that it doesn't affect the DOM returned by the
130+
// browser's parsing logic. If it does affect it, then it's vulnerable to
131+
// XSS, so we reject the entire string.
132+
if ( (majorVersion === 1 && minorVersion < 12) || (majorVersion === 2 && minorVersion < 2) ) {
133+
var htmlRisky = html.replace(rxhtmlTag, "<$1></$2>");
134+
if (htmlRisky !== html) {
135+
// Even though htmlRisky and html are different strings, they might
136+
// represent the same HTML structure once parsed, in which case,
137+
// htmlRisky is actually safe. We can ask the browser to parse both
138+
// to find out, but the browser can't parse table fragments (e.g., a
139+
// root-level "<td>"), so we need to wrap them. We just need this
140+
// technique to work on all supported browsers; we don't need to
141+
// copy from the specific jQuery version we're using.
142+
// @see https://github.com/jquery/jquery/blob/3.5.1/dist/jquery.js#L4939
143+
var wrapMap = {
144+
thead: [ 1, "<table>", "</table>" ],
145+
col: [ 2, "<table><colgroup>", "</colgroup></table>" ],
146+
tr: [ 2, "<table><tbody>", "</tbody></table>" ],
147+
td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
148+
};
149+
wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
150+
wrapMap.th = wrapMap.td;
151+
152+
// Function to wrap HTML into something that a browser can parse.
153+
// @see https://github.com/jquery/jquery/blob/3.5.1/dist/jquery.js#L5032
154+
var getWrappedHtml = function (html) {
155+
var wrap = wrapMap[tag];
156+
if (wrap) {
157+
html = wrap[1] + html + wrap[2];
158+
}
159+
return html;
160+
};
161+
162+
// Function to return canonical HTML after parsing it. This parses
163+
// only; it doesn't execute scripts.
164+
// @see https://github.com/jquery/jquery-migrate/blob/3.3.0/src/jquery/manipulation.js#L5
165+
var getParsedHtml = function (html) {
166+
var doc = window.document.implementation.createHTMLDocument( "" );
167+
doc.body.innerHTML = html;
168+
return doc.body ? doc.body.innerHTML : '';
169+
};
170+
171+
// If the browser couldn't parse either one successfully, or if
172+
// htmlRisky parses differently than html, then html is vulnerable,
173+
// so reject it.
174+
var htmlParsed = getParsedHtml(getWrappedHtml(html));
175+
var htmlRiskyParsed = getParsedHtml(getWrappedHtml(htmlRisky));
176+
if (htmlRiskyParsed === '' || htmlParsed === '' || (htmlRiskyParsed !== htmlParsed)) {
177+
html = '';
178+
}
179+
}
180+
}
181+
182+
return html;
183+
}
184+
});
185+
186+
// Prior to jQuery 1.12 and 2.2, jQuery.clean(), jQuery.buildFragment(), and
187+
// jQuery.fn.html() did not call jQuery.htmlPrefilter(), so we add that.
188+
if ( (majorVersion === 1 && minorVersion < 12) || (majorVersion === 2 && minorVersion < 2) ) {
189+
// Filter the HTML coming into jQuery.fn.html().
190+
var fnOriginalHtml = jQuery.fn.html;
191+
jQuery.fn.extend({
192+
// @see https://github.com/jquery/jquery/blob/1.5/jquery.js#L5147
193+
html: function (value) {
194+
if (typeof value === "string") {
195+
value = jQuery.htmlPrefilter(value);
196+
}
197+
// .html() can be called as a setter (with an argument) or as a getter
198+
// (without an argument), so invoke fnOriginalHtml() the same way that
199+
// we were invoked.
200+
return fnOriginalHtml.apply(this, arguments.length ? [value] : []);
201+
}
202+
});
203+
204+
// The regular expression that jQuery uses to determine if a string is HTML.
205+
// Used by both clean() and buildFragment().
206+
// @see https://github.com/jquery/jquery/blob/1.5/jquery.js#L4960
207+
var rhtml = /<|&#?\w+;/;
208+
209+
// Filter HTML coming into:
210+
// - jQuery.clean() for versions prior to 1.9.
211+
// - jQuery.buildFragment() for 1.9 and above.
212+
//
213+
// The looping constructs in the two functions might be essentially
214+
// identical, but they're each expressed here in the way that most closely
215+
// matches their original expression in jQuery, so that we filter all of
216+
// the items and only the items that jQuery will treat as HTML strings.
217+
if (majorVersion === 1 && minorVersion < 9) {
218+
var originalClean = jQuery.clean;
219+
jQuery.extend({
220+
// @see https://github.com/jquery/jquery/blob/1.5/jquery.js#L5493
221+
'clean': function (elems, context, fragment, scripts) {
222+
for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
223+
if ( typeof elem === "string" && rhtml.test( elem ) ) {
224+
elems[i] = elem = jQuery.htmlPrefilter(elem);
225+
}
226+
}
227+
return originalClean.call(this, elems, context, fragment, scripts);
228+
}
229+
});
230+
}
231+
else {
232+
var originalBuildFragment = jQuery.buildFragment;
233+
jQuery.extend({
234+
// @see https://github.com/jquery/jquery/blob/1.9.0/jquery.js#L6419
235+
'buildFragment': function (elems, context, scripts, selection) {
236+
var l = elems.length;
237+
for ( var i = 0; i < l; i++ ) {
238+
var elem = elems[i];
239+
if (elem || elem === 0) {
240+
if ( jQuery.type( elem ) !== "object" && rhtml.test( elem ) ) {
241+
elems[i] = elem = jQuery.htmlPrefilter(elem);
242+
}
243+
}
244+
}
245+
return originalBuildFragment.call(this, elems, context, scripts, selection);
246+
}
247+
});
248+
}
249+
}
250+
251+
})(jQuery);

modules/system/system.module

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
/**
99
* The current system version.
1010
*/
11-
define('VERSION', '6.52');
11+
define('VERSION', '6.53');
1212

1313
/**
1414
* Core API compatibility.

update.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ function update_results_page() {
345345
$output .= '<li class="failure"><strong>Failed:</strong> '. $query['query'] .'</li>';
346346
}
347347
}
348-
if (!count($queries)) {
348+
if (empty($queries)) {
349349
$output .= '<li class="none">No queries</li>';
350350
}
351351
$output .= '</ul>';

0 commit comments

Comments
 (0)