From f14076dc3e19524c12a6a2c803684546170ce511 Mon Sep 17 00:00:00 2001 From: "Edward Z. Yang" Date: Sun, 27 Mar 2016 14:53:31 -0700 Subject: [PATCH] Fix #49; prevent readdir infinite loop when cache directory not listable. Signed-off-by: Edward Z. Yang --- NEWS | 2 ++ library/HTMLPurifier/DefinitionCache.php | 2 +- library/HTMLPurifier/DefinitionCache/Serializer.php | 12 ++++++++++++ tests/HTMLPurifier/DefinitionCache/SerializerTest.php | 14 ++++++++++++++ 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index fd2282ce..6efe38a3 100644 --- a/NEWS +++ b/NEWS @@ -22,6 +22,8 @@ NEWS ( CHANGELOG and HISTORY ) HTMLPurifier - Support non-/tmp temporary directories for data:// validation - Give a better error message when a user attempts to allow ul/ol without allowing li. +- On some versions of PHP, the Serializer DefinitionCache could + infinite loop when the directory exists but is not listable. (#49) 4.7.0, released 2015-08-04 # opacity is now considered a "tricky" CSS property rather than a diff --git a/library/HTMLPurifier/DefinitionCache.php b/library/HTMLPurifier/DefinitionCache.php index 67bb5b1e..9aa8ff35 100644 --- a/library/HTMLPurifier/DefinitionCache.php +++ b/library/HTMLPurifier/DefinitionCache.php @@ -118,7 +118,7 @@ abstract class HTMLPurifier_DefinitionCache /** * Clears all expired (older version or revision) objects from cache - * @note Be carefuly implementing this method as flush. Flush must + * @note Be careful implementing this method as flush. Flush must * not interfere with other Definition types, and cleanup() * should not be repeatedly called by userland code. * @param HTMLPurifier_Config $config diff --git a/library/HTMLPurifier/DefinitionCache/Serializer.php b/library/HTMLPurifier/DefinitionCache/Serializer.php index ce268d91..4686fcff 100644 --- a/library/HTMLPurifier/DefinitionCache/Serializer.php +++ b/library/HTMLPurifier/DefinitionCache/Serializer.php @@ -97,6 +97,12 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac } $dir = $this->generateDirectoryPath($config); $dh = opendir($dir); + // Apparently, on some versions of PHP, readdir will return + // an empty string if you pass an invalid argument to readdir. + // So you need this test. See #49. + if (false === $dh) { + return false; + } while (false !== ($filename = readdir($dh))) { if (empty($filename)) { continue; @@ -106,6 +112,7 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac } unlink($dir . '/' . $filename); } + return true; } /** @@ -119,6 +126,10 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac } $dir = $this->generateDirectoryPath($config); $dh = opendir($dir); + // See #49 (and above). + if (false === $dh) { + return false; + } while (false !== ($filename = readdir($dh))) { if (empty($filename)) { continue; @@ -131,6 +142,7 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac unlink($dir . '/' . $filename); } } + return true; } /** diff --git a/tests/HTMLPurifier/DefinitionCache/SerializerTest.php b/tests/HTMLPurifier/DefinitionCache/SerializerTest.php index 9ad74e63..57c2c3e2 100644 --- a/tests/HTMLPurifier/DefinitionCache/SerializerTest.php +++ b/tests/HTMLPurifier/DefinitionCache/SerializerTest.php @@ -224,6 +224,20 @@ class HTMLPurifier_DefinitionCache_SerializerTest extends HTMLPurifier_Definitio rmdir( $dir . '/Test'); } + + public function testNoInfiniteLoop() + { + $cache = new HTMLPurifier_DefinitionCache_Serializer('Test'); + + $config = $this->generateConfigMock('serial'); + $config->version = '1.0.0'; + $config->returns('get', 1, array('Test.DefinitionRev')); + $dir = dirname(__FILE__) . '/SerializerTest'; + $config->returns('get', $dir, array('Cache.SerializerPath')); + $config->returns('get', 0400, array('Cache.SerializerPermissions')); + + $cache->cleanup($config); + } } // vim: et sw=4 sts=4 -- 2.11.4.GIT