Browse Source

adds configurable masking of array/object properties

thinkingmedia 9 years ago
parent
commit
24c74f905c
2 changed files with 73 additions and 3 deletions
  1. 41 3
      src/Error/Debugger.php
  2. 32 0
      tests/TestCase/Error/DebuggerTest.php

+ 41 - 3
src/Error/Debugger.php

@@ -14,6 +14,7 @@
  */
 namespace Cake\Error;
 
+use Cake\Core\InstanceConfigTrait;
 use Cake\Log\Log;
 use Cake\Utility\Hash;
 use Cake\Utility\Security;
@@ -32,6 +33,16 @@ use ReflectionProperty;
  */
 class Debugger
 {
+    use InstanceConfigTrait;
+
+    /**
+     * Default configuration
+     *
+     * @var array
+     */
+    protected $_defaultConfig = [
+        'mask' => []
+    ];
 
     /**
      * A list of errors generated by the application.
@@ -152,7 +163,7 @@ class Debugger
      * Returns a reference to the Debugger singleton object instance.
      *
      * @param string|null $class Class name.
-     * @return object
+     * @return object|Debugger
      */
     public static function getInstance($class = null)
     {
@@ -170,6 +181,24 @@ class Debugger
     }
 
     /**
+     * Read or write configuration options for the Debugger instance.
+     *
+     * @param string|array|null $key The key to get/set, or a complete array of configs.
+     * @param mixed|null $value The value to set.
+     * @param bool $merge Whether to recursively merge or overwrite existing config, defaults to true.
+     * @return mixed Config value being read, or the object itself on write operations.
+     * @throws \Cake\Core\Exception\Exception When trying to set a key that is invalid.
+     */
+    public static function configInstance($key = null, $value = null, $merge = true)
+    {
+        if (is_array($key) || func_num_args() >= 2) {
+            return static::getInstance()->config($key, $value, $merge);
+        }
+
+        return static::getInstance()->config($key);
+    }
+
+    /**
      * Recursively formats and outputs the contents of the supplied variable.
      *
      * @param mixed $var The variable to dump.
@@ -505,10 +534,13 @@ class Debugger
         $vars = [];
 
         if ($depth >= 0) {
+            $mask = (array)static::configInstance('mask');
             foreach ($var as $key => $val) {
                 // Sniff for globals as !== explodes in < 5.4
                 if ($key === 'GLOBALS' && is_array($val) && isset($val['GLOBALS'])) {
                     $val = '[recursion]';
+                } elseif (array_key_exists($key, $mask)) {
+                    $val = (string)$mask[$key];
                 } elseif ($val !== $var) {
                     $val = static::_export($val, $depth, $indent);
                 }
@@ -555,9 +587,10 @@ class Debugger
         }
 
         if ($depth > 0) {
+            $mask = (array)static::configInstance('mask');
             $objectVars = get_object_vars($var);
             foreach ($objectVars as $key => $value) {
-                $value = static::_export($value, $depth - 1, $indent);
+                $value = array_key_exists($key, $mask) ? $mask[$key] : static::_export($value, $depth - 1, $indent);
                 $props[] = "$key => " . $value;
             }
 
@@ -575,7 +608,12 @@ class Debugger
 
                     $value = static::_export($property, $depth - 1, $indent);
                     $key = $reflectionProperty->name;
-                    $props[] = sprintf('[%s] %s => %s', $visibility, $key, $value);
+                    $props[] = sprintf(
+                        '[%s] %s => %s',
+                        $visibility,
+                        $key,
+                        array_key_exists($key, $mask) ? $mask[$key] : $value
+                    );
                 }
             }
 

+ 32 - 0
tests/TestCase/Error/DebuggerTest.php

@@ -36,6 +36,11 @@ class DebuggableThing
     }
 }
 
+class SecurityThing
+{
+    public $password = 'pass1234';
+}
+
 /**
  * DebuggerTest class
  *
@@ -561,4 +566,31 @@ object(Cake\Test\TestCase\Error\DebuggableThing) {
 eos;
         $this->assertEquals($expected, $result);
     }
+
+    /**
+     * Tests the masking of an array key.
+     *
+     * @return void
+     */
+    public function testMaskArray()
+    {
+        Debugger::configInstance('mask', ['password' => '[**********]']);
+        $result = Debugger::exportVar(['password' => 'pass1234']);
+        $expected = "['password'=>[**********]]";
+        $this->assertEquals($expected, preg_replace('/\s+/', '', $result));
+    }
+
+    /**
+     * Tests the masking of an array key.
+     *
+     * @return void
+     */
+    public function testMaskObject()
+    {
+        Debugger::configInstance('mask', ['password' => '[**********]']);
+        $object = new SecurityThing();
+        $result = Debugger::exportVar($object);
+        $expected = "object(Cake\\Test\\TestCase\\Error\\SecurityThing){password=>[**********]}";
+        $this->assertEquals($expected, preg_replace('/\s+/', '', $result));
+    }
 }