1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
|
From: Nicolas Grekas <nicolas.grekas@gmail.com>
Date: Mon, 16 Mar 2015 15:12:02 +0100
Subject: Safe escaping of fragments for eval()
https://github.com/symfony/symfony/commit/195c57e1f50765aff33137689b16e126a689056a
---
src/Symfony/Component/HttpKernel/HttpCache/Esi.php | 62 +++++++++++-----------
.../HttpKernel/Tests/HttpCache/EsiTest.php | 4 +-
2 files changed, 33 insertions(+), 33 deletions(-)
diff --git a/src/Symfony/Component/HttpKernel/HttpCache/Esi.php b/src/Symfony/Component/HttpKernel/HttpCache/Esi.php
index 9dd99d6..ad63267 100644
--- a/src/Symfony/Component/HttpKernel/HttpCache/Esi.php
+++ b/src/Symfony/Component/HttpKernel/HttpCache/Esi.php
@@ -29,6 +29,10 @@ use Symfony\Component\HttpKernel\HttpKernelInterface;
class Esi
{
private $contentTypes;
+ private $phpEscapeMap = array(
+ array('<?', '<%', '<s', '<S'),
+ array('<?php echo "<?"; ?>', '<?php echo "<%"; ?>', '<?php echo "<s"; ?>', '<?php echo "<S"; ?>'),
+ );
/**
* Constructor.
@@ -158,10 +162,34 @@ class Esi
// we don't use a proper XML parser here as we can have ESI tags in a plain text response
$content = $response->getContent();
- $content = str_replace(array('<?', '<%'), array('<?php echo "<?"; ?>', '<?php echo "<%"; ?>'), $content);
- $content = preg_replace_callback('#<esi\:include\s+(.*?)\s*(?:/|</esi\:include)>#', array($this, 'handleEsiIncludeTag'), $content);
- $content = preg_replace('#<esi\:comment[^>]*(?:/|</esi\:comment)>#', '', $content);
$content = preg_replace('#<esi\:remove>.*?</esi\:remove>#', '', $content);
+ $content = preg_replace('#<esi\:comment[^>]*(?:/|</esi\:comment)>#', '', $content);
+
+ $chunks = preg_split('#<esi\:include\s+(.*?)\s*(?:/|</esi\:include)>#', $content, -1, PREG_SPLIT_DELIM_CAPTURE);
+ $chunks[0] = str_replace($this->phpEscapeMap[0], $this->phpEscapeMap[1], $chunks[0]);
+
+ $i = 1;
+ while (isset($chunks[$i])) {
+ $options = array();
+ preg_match_all('/(src|onerror|alt)="([^"]*?)"/', $chunks[$i], $matches, PREG_SET_ORDER);
+ foreach ($matches as $set) {
+ $options[$set[1]] = $set[2];
+ }
+
+ if (!isset($options['src'])) {
+ throw new \RuntimeException('Unable to process an ESI tag without a "src" attribute.');
+ }
+
+ $chunks[$i] = sprintf('<?php echo $this->esi->handle($this, %s, %s, %s) ?>'."\n",
+ var_export($options['src'], true),
+ var_export(isset($options['alt']) ? $options['alt'] : '', true),
+ isset($options['onerror']) && 'continue' == $options['onerror'] ? 'true' : 'false'
+ );
+ ++$i;
+ $chunks[$i] = str_replace($this->phpEscapeMap[0], $this->phpEscapeMap[1], $chunks[$i]);
+ ++$i;
+ }
+ $content = implode('', $chunks);
$response->setContent($content);
$response->headers->set('X-Body-Eval', 'ESI');
@@ -214,32 +242,4 @@ class Esi
}
}
}
-
- /**
- * Handles an ESI include tag (called internally).
- *
- * @param array $attributes An array containing the attributes.
- *
- * @return string The response content for the include.
- *
- * @throws \RuntimeException
- */
- private function handleEsiIncludeTag($attributes)
- {
- $options = array();
- preg_match_all('/(src|onerror|alt)="([^"]*?)"/', $attributes[1], $matches, PREG_SET_ORDER);
- foreach ($matches as $set) {
- $options[$set[1]] = $set[2];
- }
-
- if (!isset($options['src'])) {
- throw new \RuntimeException('Unable to process an ESI tag without a "src" attribute.');
- }
-
- return sprintf('<?php echo $this->esi->handle($this, %s, %s, %s) ?>'."\n",
- var_export($options['src'], true),
- var_export(isset($options['alt']) ? $options['alt'] : '', true),
- isset($options['onerror']) && 'continue' == $options['onerror'] ? 'true' : 'false'
- );
- }
}
diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpCache/EsiTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpCache/EsiTest.php
index 23e256e..8ab3d47 100644
--- a/src/Symfony/Component/HttpKernel/Tests/HttpCache/EsiTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/HttpCache/EsiTest.php
@@ -131,10 +131,10 @@ class EsiTest extends \PHPUnit_Framework_TestCase
$esi = new Esi();
$request = Request::create('/');
- $response = new Response('foo <?php die("foo"); ?><%= "lala" %>');
+ $response = new Response('<?php <? <% <script language=php>');
$esi->process($request, $response);
- $this->assertEquals('foo <?php echo "<?"; ?>php die("foo"); ?><?php echo "<%"; ?>= "lala" %>', $response->getContent());
+ $this->assertEquals('<?php echo "<?"; ?>php <?php echo "<?"; ?> <?php echo "<%"; ?> <?php echo "<s"; ?>cript language=php>', $response->getContent());
}
/**
|