Browse Source

Escape urls to avoid xss

Marc Ypes 8 years ago
parent
commit
0c88f6365f

+ 1 - 1
src/View/Helper/HtmlHelper.php

@@ -826,7 +826,7 @@ class HtmlHelper extends Helper
      * - `fullBase` If true the src attribute will get a full address for the image file.
      * - `plugin` False value will prevent parsing path as a plugin
      *
-     * @param string $path Path to the image file, relative to the app/webroot/img/ directory.
+     * @param string|array $path Path to the image file, relative to the app/webroot/img/ directory.
      * @param array $options Array of HTML attributes. See above for special options.
      * @return string completed img tag
      * @link https://book.cakephp.org/3.0/en/views/helpers/html.html#linking-to-images

+ 1 - 1
src/View/Helper/UrlHelper.php

@@ -144,7 +144,7 @@ class UrlHelper extends Helper
             return $this->build($path, !empty($options['fullBase']));
         }
         if (strpos($path, '://') !== false || preg_match('/^[a-z]+:/i', $path)) {
-            return $path;
+            return ltrim($this->build($path), '/');
         }
         if (!array_key_exists('plugin', $options) || $options['plugin'] !== false) {
             list($plugin, $path) = $this->_View->pluginSplit($path, false);

+ 3 - 0
src/View/StringTemplate.php

@@ -315,6 +315,9 @@ class StringTemplate
         }
         $truthy = [1, '1', true, 'true', $key];
         $isMinimized = isset($this->_compactAttributes[$key]);
+        if (!preg_match('/\A(\w|[.-])+\z/', $key)) {
+            $key = h($key);
+        }
         if ($isMinimized && in_array($value, $truthy, true)) {
             return "$key=\"$key\"";
         }

+ 39 - 0
tests/TestCase/View/Helper/HtmlHelperTest.php

@@ -347,6 +347,11 @@ class HtmlHelperTest extends TestCase
         $result = $this->Html->image('cid:cakephp_logo');
         $expected = ['img' => ['src' => 'cid:cakephp_logo', 'alt' => '']];
         $this->assertHtml($expected, $result);
+
+        $result = $this->Html->image('x:"><script>alert(1)</script>');
+        $expected = ['img' => ['src' => 'x:&quot;&gt;&lt;script&gt;alert(1)&lt;/script&gt;', 'alt' => '']];
+
+        $this->assertHtml($expected, $result);
     }
 
     /**
@@ -562,6 +567,10 @@ class HtmlHelperTest extends TestCase
         $expected['link']['href'] = 'css/screen.css?with=param&amp;other=param';
         $this->assertHtml($expected, $result);
 
+        $result = $this->Html->css('x:"><script>alert(1)</script>');
+        $expected['link']['href'] = 'x:&quot;&gt;&lt;script&gt;alert(1)&lt;/script&gt;';
+        $this->assertHtml($expected, $result);
+
         $result = $this->Html->css('http://whatever.com/screen.css?1234');
         $expected['link']['href'] = 'preg:/http:\/\/.*\/screen\.css\?1234/';
         $this->assertHtml($expected, $result);
@@ -904,6 +913,12 @@ class HtmlHelperTest extends TestCase
         ];
         $this->assertHtml($expected, $result);
 
+        $result = $this->Html->script('x:"><script>alert(1)</script>');
+        $expected = [
+            'script' => ['src' => 'x:&quot;&gt;&lt;script&gt;alert(1)&lt;/script&gt;']
+        ];
+        $this->assertHtml($expected, $result);
+
         $result = $this->Html->script('foo2', ['pathPrefix' => '/my/custom/path/']);
         $expected = [
             'script' => ['src' => '/my/custom/path/foo2.js']
@@ -1716,6 +1731,24 @@ class HtmlHelperTest extends TestCase
         ];
         $this->assertHtml($expected, $result);
 
+        $result = $this->Html->meta('icon', 'x:"><script>alert(1)</script>');
+        $url = 'x:&quot;&gt;&lt;script&gt;alert(1)&lt;/script&gt;';
+        $expected = [
+            'link' => [
+                'href' => $url,
+                'type' => 'image/x-icon',
+                'rel' => 'icon'
+            ],
+            [
+                'link' => [
+                    'href' => $url,
+                    'type' => 'image/x-icon',
+                    'rel' => 'shortcut icon'
+                ]
+            ]
+        ];
+        $this->assertHtml($expected, $result);
+
         $this->Html->request->webroot = '/testing/';
         $result = $this->Html->meta('icon');
         $expected = [
@@ -1956,6 +1989,12 @@ class HtmlHelperTest extends TestCase
         $result = $this->Html->div('class-name', '<text>', ['escape' => true]);
         $expected = ['div' => ['class' => 'class-name'], '&lt;text&gt;', '/div'];
         $this->assertHtml($expected, $result);
+
+        $evilKey = "><script>alert(1)</script>";
+        $options = [$evilKey => 'some value'];
+        $result = $this->Html->div('class-name', '', $options);
+        $expected = '<div &gt;&lt;script&gt;alert(1)&lt;/script&gt;="some value" class="class-name"></div>';
+        $this->assertEquals($expected, $result);
     }
 
     /**

+ 3 - 0
tests/TestCase/View/Helper/UrlHelperTest.php

@@ -198,6 +198,9 @@ class UrlHelperTest extends TestCase
         $result = $this->Helper->assetUrl('foo.jpg?one=two&three=four');
         $this->assertEquals('foo.jpg?one=two&amp;three=four', $result);
 
+        $result = $this->Helper->assetUrl('x:"><script>alert(1)</script>');
+        $this->assertEquals('x:&quot;&gt;&lt;script&gt;alert(1)&lt;/script&gt;', $result);
+
         $result = $this->Helper->assetUrl('dir/big+tall/image', ['ext' => '.jpg']);
         $this->assertEquals('dir/big%2Btall/image.jpg', $result);
     }

+ 9 - 0
tests/TestCase/View/StringTemplateTest.php

@@ -264,6 +264,15 @@ class StringTemplateTest extends TestCase
             ' data-hero="&lt;batman&gt;"',
             $result
         );
+
+        $evilKey = "><script>alert(1)</script>";
+        $attrs = [$evilKey => 'some value'];
+
+        $result = $this->template->formatAttributes($attrs);
+        $this->assertEquals(
+            ' &gt;&lt;script&gt;alert(1)&lt;/script&gt;="some value"',
+            $result
+        );
     }
 
     /**