diff --git a/CHANGELOG.txt b/CHANGELOG.txt index ec1d2f91e6a80d99aa0d9a352d61ed74b893cb79..fdafbaab39b6bb225d813000025f2cb7a85eb65c 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,5 +1,5 @@ -Drupal 7.19, xxxx-xx-xx (development version) +Drupal 7.20, xxxx-xx-xx (development version) ----------------------- - Fixed entity argument not being passed to implementations of hook_file_download_access_alter(). The fix adds an additional context @@ -23,6 +23,10 @@ Drupal 7.19, xxxx-xx-xx (development version) sites which use HTTPS and redirect between "www" and non-"www" versions of the page. +Drupal 7.19, 2013-01-16 +----------------------- +- Fixed security issues (multiple vulnerabilities). See SA-CORE-2013-001. + Drupal 7.18, 2012-12-19 ----------------------- - Fixed security issues (multiple vulnerabilities). See SA-CORE-2012-004. diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc index 7fa42d049d36416677af86fda3c6b2f93c862884..1fd497de2c56c6607c5430f47e5d2e3ae07ccbb1 100644 --- a/includes/bootstrap.inc +++ b/includes/bootstrap.inc @@ -8,7 +8,7 @@ /** * The current system version. */ -define('VERSION', '7.19-dev'); +define('VERSION', '7.20-dev'); /** * Core API compatibility. diff --git a/misc/collapse.js b/misc/collapse.js index bd51ce5326e74d354e78a0cfb3109a07b2f8c825..512ff2f9f1d2632072b90c6c2b35a54169a749fe 100644 --- a/misc/collapse.js +++ b/misc/collapse.js @@ -58,9 +58,9 @@ Drupal.behaviors.collapse = { $('fieldset.collapsible', context).once('collapse', function () { var $fieldset = $(this); // Expand fieldset if there are errors inside, or if it contains an - // element that is targeted by the URI fragment identifier. + // element that is targeted by the URI fragment identifier. var anchor = location.hash && location.hash != '#' ? ', ' + location.hash : ''; - if ($('.error' + anchor, $fieldset).length) { + if ($fieldset.find('.error' + anchor).length) { $fieldset.removeClass('collapsed'); } diff --git a/misc/drupal.js b/misc/drupal.js index 83b0884280845291c4c494d147b76f0e64ffe910..643baa1bf0b270be3d5b283996617bd40d170e94 100644 --- a/misc/drupal.js +++ b/misc/drupal.js @@ -6,6 +6,27 @@ jQuery.noConflict(); (function ($) { +/** + * Override jQuery.fn.init to guard against XSS attacks. + * + * See http://bugs.jquery.com/ticket/9521 + */ +var jquery_init = $.fn.init; +$.fn.init = function (selector, context, rootjQuery) { + // If the string contains a "#" before a "<", treat it as invalid HTML. + if (selector && typeof selector === 'string') { + var hash_position = selector.indexOf('#'); + if (hash_position >= 0) { + var bracket_position = selector.indexOf('<'); + if (bracket_position > hash_position) { + throw 'Syntax error, unrecognized expression: ' + selector; + } + } + } + return jquery_init.call(this, selector, context, rootjQuery); +}; +$.fn.init.prototype = jquery_init.prototype; + /** * Attach all registered behaviors to a page element. * diff --git a/misc/vertical-tabs.js b/misc/vertical-tabs.js index 14d06607bff6f7fe9a6c3ea5a7fe7c6b720bed51..ebfaa4f7f70c08af5f6aa6cca5b1321e990e57c4 100644 --- a/misc/vertical-tabs.js +++ b/misc/vertical-tabs.js @@ -50,8 +50,8 @@ Drupal.behaviors.verticalTabs = { if (!tab_focus) { // If the current URL has a fragment and one of the tabs contains an // element that matches the URL fragment, activate that tab. - if (window.location.hash && $(window.location.hash, this).length) { - tab_focus = $(window.location.hash, this).closest('.vertical-tabs-pane'); + if (window.location.hash && $(this).find(window.location.hash).length) { + tab_focus = $(this).find(window.location.hash).closest('.vertical-tabs-pane'); } else { tab_focus = $('> .vertical-tabs-pane:first', this); diff --git a/modules/book/book.pages.inc b/modules/book/book.pages.inc index 19f61158c8da437241b2229cfbb8bd29ac6dbd60..e5a04c5a2c0a296a9e87007ada5608b9e4812963 100644 --- a/modules/book/book.pages.inc +++ b/modules/book/book.pages.inc @@ -45,6 +45,15 @@ function book_render() { * @see book_menu() */ function book_export($type, $nid) { + // Check that the node exists and that the current user has access to it. + $node = node_load($nid); + if (!$node) { + return MENU_NOT_FOUND; + } + if (!node_access('view', $node)) { + return MENU_ACCESS_DENIED; + } + $type = drupal_strtolower($type); $export_function = 'book_export_' . $type; diff --git a/modules/book/book.test b/modules/book/book.test index 71dc6fe61cc03ec7605ecaf8d151575a1e218496..81f4524ac92079bf173268ea8dc3532421d731e8 100644 --- a/modules/book/book.test +++ b/modules/book/book.test @@ -289,6 +289,13 @@ class BookTestCase extends DrupalWebTestCase { // Try getting the URL directly, and verify it fails. $this->drupalGet('book/export/html/' . $this->book->nid); $this->assertResponse('403', 'Anonymous user properly forbidden.'); + + // Now grant anonymous users permission to view the printer-friendly + // version and verify that node access restrictions still prevent them from + // seeing it. + user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, array('access printer-friendly version')); + $this->drupalGet('book/export/html/' . $this->book->nid); + $this->assertResponse('403', 'Anonymous user properly forbidden from seeing the printer-friendly version when denied by node access.'); } /** diff --git a/modules/image/image.module b/modules/image/image.module index ff50452d598085dd4dac64bfa24409852af7b3fe..07f4892339009031bbc437e7bb0abbb4347399f7 100644 --- a/modules/image/image.module +++ b/modules/image/image.module @@ -292,7 +292,8 @@ function image_file_download($uri) { if ($info = image_get_info($uri)) { // Check the permissions of the original to grant access to this image. $headers = module_invoke_all('file_download', $original_uri); - if (!in_array(-1, $headers)) { + // Confirm there's at least one module granting access and none denying access. + if (!empty($headers) && !in_array(-1, $headers)) { return array( // Send headers describing the image's size, and MIME-type... 'Content-Type' => $info['mime_type'],