From f019275be7d960060c682c69d462832fd53e02f2 Mon Sep 17 00:00:00 2001 From: David Rothstein Date: Mon, 5 Aug 2013 01:36:56 -0400 Subject: [PATCH] Issue #238250 by markpavlitski | Xano: Fixed cache_clear_all()('*', 'block', TRUE); will TRUNCATE the {block} table without additional checks. --- CHANGELOG.txt | 3 +++ includes/cache.inc | 32 ++++++++++++++++++++++++++++- modules/simpletest/tests/cache.test | 24 ++++++++++++++++++++++ 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index f9f429da983e..633985059c09 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,6 +1,9 @@ Drupal 7.23, xxxx-xx-xx (development version) ----------------------- +- Added protection to cache_clear_all() to ensure that non-cache tables cannot + be truncated (API addition: a new isValidBin() method has been added to the + default database cache implementation). - Changed the default .htaccess file to support HTTP authorization in CGI environments. - Changed the password reset form to pre-fill the username when requested via a diff --git a/includes/cache.inc b/includes/cache.inc index c2a514646ff9..c2f9569f58b0 100644 --- a/includes/cache.inc +++ b/includes/cache.inc @@ -501,7 +501,16 @@ class DrupalDatabaseCache implements DrupalCacheInterface { else { if ($wildcard) { if ($cid == '*') { - db_truncate($this->bin)->execute(); + // Check if $this->bin is a cache table before truncating. Other + // cache_clear_all() operations throw a PDO error in this situation, + // so we don't need to verify them first. This ensures that non-cache + // tables cannot be truncated accidentally. + if ($this->isValidBin()) { + db_truncate($this->bin)->execute(); + } + else { + throw new Exception(t('Invalid or missing cache bin specified: %bin', array('%bin' => $this->bin))); + } } else { db_delete($this->bin) @@ -538,4 +547,25 @@ class DrupalDatabaseCache implements DrupalCacheInterface { ->fetchField(); return empty($result); } + + /** + * Checks if $this->bin represents a valid cache table. + * + * This check is required to ensure that non-cache tables are not truncated + * accidentally when calling cache_clear_all(). + * + * @return boolean + */ + function isValidBin() { + if ($this->bin == 'cache' || substr($this->bin, 0, 6) == 'cache_') { + // Skip schema check for bins with standard table names. + return TRUE; + } + // These fields are required for any cache table. + $fields = array('cid', 'data', 'expire', 'created', 'serialized'); + // Load the table schema. + $schema = drupal_get_schema($this->bin); + // Confirm that all fields are present. + return isset($schema['fields']) && !array_diff($fields, array_keys($schema['fields'])); + } } diff --git a/modules/simpletest/tests/cache.test b/modules/simpletest/tests/cache.test index 997f37aefcac..b42de360b4e9 100644 --- a/modules/simpletest/tests/cache.test +++ b/modules/simpletest/tests/cache.test @@ -340,6 +340,30 @@ class CacheClearCase extends CacheTestCase { } } + /** + * Test DrupalDatabaseCache::isValidBin(). + */ + function testIsValidBin() { + // Retrieve existing cache bins. + $valid_bins = array('cache', 'cache_filter', 'cache_page', 'cache_boostrap', 'cache_path'); + $valid_bins = array_merge(module_invoke_all('flush_caches'), $valid_bins); + foreach ($valid_bins as $id => $bin) { + $cache = _cache_get_object($bin); + if ($cache instanceof DrupalDatabaseCache) { + $this->assertTrue($cache->isValidBin(), format_string('Cache bin @bin is valid.', array('@bin' => $bin))); + } + } + + // Check for non-cache tables and invalid bins. + $invalid_bins = array('block', 'filter', 'missing_table', $this->randomName()); + foreach ($invalid_bins as $id => $bin) { + $cache = _cache_get_object($bin); + if ($cache instanceof DrupalDatabaseCache) { + $this->assertFalse($cache->isValidBin(), format_string('Cache bin @bin is not valid.', array('@bin' => $bin))); + } + } + } + /** * Test minimum cache lifetime. */