summaryrefslogtreecommitdiff
path: root/misc
diff options
context:
space:
mode:
authorDavid Rothstein <drothstein@gmail.com>2015-08-19 17:20:31 -0400
committerDavid Rothstein <drothstein@gmail.com>2015-08-19 17:20:31 -0400
commitbe00a1ced4104d84df2f34b149b35fb0adf91093 (patch)
tree57eb4bdd551ef892671c5d7d653a78fdd3f3d454 /misc
parent5cb79b4b217e9aa315d61284398cce132c28bea4 (diff)
downloadbrdo-be00a1ced4104d84df2f34b149b35fb0adf91093.tar.gz
brdo-be00a1ced4104d84df2f34b149b35fb0adf91093.tar.bz2
Drupal 7.39
Diffstat (limited to 'misc')
-rw-r--r--misc/ajax.js40
-rw-r--r--misc/autocomplete.js7
-rw-r--r--misc/drupal.js73
3 files changed, 110 insertions, 10 deletions
diff --git a/misc/ajax.js b/misc/ajax.js
index 01b894d75..bb4a6e14f 100644
--- a/misc/ajax.js
+++ b/misc/ajax.js
@@ -14,6 +14,8 @@
Drupal.ajax = Drupal.ajax || {};
+Drupal.settings.urlIsAjaxTrusted = Drupal.settings.urlIsAjaxTrusted || {};
+
/**
* Attaches the Ajax behavior to each Ajax form element.
*/
@@ -130,6 +132,11 @@ Drupal.ajax = function (base, element, element_settings) {
// 5. /nojs# - Followed by a fragment.
// E.g.: path/nojs#myfragment
this.url = element_settings.url.replace(/\/nojs(\/|$|\?|&|#)/g, '/ajax$1');
+ // If the 'nojs' version of the URL is trusted, also trust the 'ajax' version.
+ if (Drupal.settings.urlIsAjaxTrusted[element_settings.url]) {
+ Drupal.settings.urlIsAjaxTrusted[this.url] = true;
+ }
+
this.wrapper = '#' + element_settings.wrapper;
// If there isn't a form, jQuery.ajax() will be used instead, allowing us to
@@ -155,18 +162,36 @@ Drupal.ajax = function (base, element, element_settings) {
ajax.ajaxing = true;
return ajax.beforeSend(xmlhttprequest, options);
},
- success: function (response, status) {
+ success: function (response, status, xmlhttprequest) {
// Sanity check for browser support (object expected).
// When using iFrame uploads, responses must be returned as a string.
if (typeof response == 'string') {
response = $.parseJSON(response);
}
+
+ // Prior to invoking the response's commands, verify that they can be
+ // trusted by checking for a response header. See
+ // ajax_set_verification_header() for details.
+ // - Empty responses are harmless so can bypass verification. This avoids
+ // an alert message for server-generated no-op responses that skip Ajax
+ // rendering.
+ // - Ajax objects with trusted URLs (e.g., ones defined server-side via
+ // #ajax) can bypass header verification. This is especially useful for
+ // Ajax with multipart forms. Because IFRAME transport is used, the
+ // response headers cannot be accessed for verification.
+ if (response !== null && !Drupal.settings.urlIsAjaxTrusted[ajax.url]) {
+ if (xmlhttprequest.getResponseHeader('X-Drupal-Ajax-Token') !== '1') {
+ var customMessage = Drupal.t("The response failed verification so will not be processed.");
+ return ajax.error(xmlhttprequest, ajax.url, customMessage);
+ }
+ }
+
return ajax.success(response, status);
},
- complete: function (response, status) {
+ complete: function (xmlhttprequest, status) {
ajax.ajaxing = false;
if (status == 'error' || status == 'parsererror') {
- return ajax.error(response, ajax.url);
+ return ajax.error(xmlhttprequest, ajax.url);
}
},
dataType: 'json',
@@ -175,6 +200,9 @@ Drupal.ajax = function (base, element, element_settings) {
// Bind the ajaxSubmit function to the element event.
$(ajax.element).bind(element_settings.event, function (event) {
+ if (!Drupal.settings.urlIsAjaxTrusted[ajax.url] && !Drupal.urlIsLocal(ajax.url)) {
+ throw new Error(Drupal.t('The callback URL is not local and not trusted: !url', {'!url': ajax.url}));
+ }
return ajax.eventResponse(this, event);
});
@@ -447,8 +475,8 @@ Drupal.ajax.prototype.getEffect = function (response) {
/**
* Handler for the form redirection error.
*/
-Drupal.ajax.prototype.error = function (response, uri) {
- alert(Drupal.ajaxError(response, uri));
+Drupal.ajax.prototype.error = function (xmlhttprequest, uri, customMessage) {
+ alert(Drupal.ajaxError(xmlhttprequest, uri, customMessage));
// Remove the progress element.
if (this.progress.element) {
$(this.progress.element).remove();
@@ -462,7 +490,7 @@ Drupal.ajax.prototype.error = function (response, uri) {
$(this.element).removeClass('progress-disabled').removeAttr('disabled');
// Reattach behaviors, if they were detached in beforeSerialize().
if (this.form) {
- var settings = response.settings || this.settings || Drupal.settings;
+ var settings = this.settings || Drupal.settings;
Drupal.attachBehaviors(this.form, settings);
}
};
diff --git a/misc/autocomplete.js b/misc/autocomplete.js
index 567908170..d71441b6c 100644
--- a/misc/autocomplete.js
+++ b/misc/autocomplete.js
@@ -271,8 +271,11 @@ Drupal.ACDB.prototype.search = function (searchString) {
var db = this;
this.searchString = searchString;
- // See if this string needs to be searched for anyway.
- searchString = searchString.replace(/^\s+|\s+$/, '');
+ // See if this string needs to be searched for anyway. The pattern ../ is
+ // stripped since it may be misinterpreted by the browser.
+ searchString = searchString.replace(/^\s+|\.{2,}\/|\s+$/g, '');
+ // Skip empty search strings, or search strings ending with a comma, since
+ // that is the separator between search terms.
if (searchString.length <= 0 ||
searchString.charAt(searchString.length - 1) == ',') {
return;
diff --git a/misc/drupal.js b/misc/drupal.js
index 643baa1bf..427c4a1e2 100644
--- a/misc/drupal.js
+++ b/misc/drupal.js
@@ -270,6 +270,72 @@ Drupal.formatPlural = function (count, singular, plural, args, options) {
};
/**
+ * Returns the passed in URL as an absolute URL.
+ *
+ * @param url
+ * The URL string to be normalized to an absolute URL.
+ *
+ * @return
+ * The normalized, absolute URL.
+ *
+ * @see https://github.com/angular/angular.js/blob/v1.4.4/src/ng/urlUtils.js
+ * @see https://grack.com/blog/2009/11/17/absolutizing-url-in-javascript
+ * @see https://github.com/jquery/jquery-ui/blob/1.11.4/ui/tabs.js#L53
+ */
+Drupal.absoluteUrl = function (url) {
+ var urlParsingNode = document.createElement('a');
+
+ // Decode the URL first; this is required by IE <= 6. Decoding non-UTF-8
+ // strings may throw an exception.
+ try {
+ url = decodeURIComponent(url);
+ } catch (e) {}
+
+ urlParsingNode.setAttribute('href', url);
+
+ // IE <= 7 normalizes the URL when assigned to the anchor node similar to
+ // the other browsers.
+ return urlParsingNode.cloneNode(false).href;
+};
+
+/**
+ * Returns true if the URL is within Drupal's base path.
+ *
+ * @param url
+ * The URL string to be tested.
+ *
+ * @return
+ * Boolean true if local.
+ *
+ * @see https://github.com/jquery/jquery-ui/blob/1.11.4/ui/tabs.js#L58
+ */
+Drupal.urlIsLocal = function (url) {
+ // Always use browser-derived absolute URLs in the comparison, to avoid
+ // attempts to break out of the base path using directory traversal.
+ var absoluteUrl = Drupal.absoluteUrl(url);
+ var protocol = location.protocol;
+
+ // Consider URLs that match this site's base URL but use HTTPS instead of HTTP
+ // as local as well.
+ if (protocol === 'http:' && absoluteUrl.indexOf('https:') === 0) {
+ protocol = 'https:';
+ }
+ var baseUrl = protocol + '//' + location.host + Drupal.settings.basePath.slice(0, -1);
+
+ // Decoding non-UTF-8 strings may throw an exception.
+ try {
+ absoluteUrl = decodeURIComponent(absoluteUrl);
+ } catch (e) {}
+ try {
+ baseUrl = decodeURIComponent(baseUrl);
+ } catch (e) {}
+
+ // The given URL matches the site's base URL, or has a path under the site's
+ // base URL.
+ return absoluteUrl === baseUrl || absoluteUrl.indexOf(baseUrl + '/') === 0;
+};
+
+/**
* Generate the themed representation of a Drupal object.
*
* All requests for themed output must go through this function. It examines
@@ -350,7 +416,7 @@ Drupal.getSelection = function (element) {
/**
* Build an error message from an Ajax response.
*/
-Drupal.ajaxError = function (xmlhttp, uri) {
+Drupal.ajaxError = function (xmlhttp, uri, customMessage) {
var statusCode, statusText, pathText, responseText, readyStateText, message;
if (xmlhttp.status) {
statusCode = "\n" + Drupal.t("An AJAX HTTP error occurred.") + "\n" + Drupal.t("HTTP Result Code: !status", {'!status': xmlhttp.status});
@@ -383,7 +449,10 @@ Drupal.ajaxError = function (xmlhttp, uri) {
// We don't need readyState except for status == 0.
readyStateText = xmlhttp.status == 0 ? ("\n" + Drupal.t("ReadyState: !readyState", {'!readyState': xmlhttp.readyState})) : "";
- message = statusCode + pathText + statusText + responseText + readyStateText;
+ // Additional message beyond what the xmlhttp object provides.
+ customMessage = customMessage ? ("\n" + Drupal.t("CustomMessage: !customMessage", {'!customMessage': customMessage})) : "";
+
+ message = statusCode + pathText + statusText + customMessage + responseText + readyStateText;
return message;
};