8000 Safe escaping of fragments for eval() · symfony/symfony@5c12bcb · GitHub
[go: up one dir, main page]

Skip to content

Commit 5c12bcb

Browse files
nicolas-grekasfabpot
authored andcommitted
Safe escaping of fragments for eval()
1 parent 0a003b7 commit 5c12bcb

File tree

2 files changed

+33
-33
lines changed

2 files changed

+33
-33
lines changed

src/Symfony/Component/HttpKernel/HttpCache/Esi.php

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@
2929
class Esi
3030
{
3131
private $contentTypes;
32+
private $phpEscapeMap = array(
33+
array('<?', '<%', '<s', '<S'),
34+
array('<?php echo "<?"; ?>', '<?php echo "<%"; ?>', '<?php echo "<s"; ?>', '<?php echo "<S"; ?>'),
35+
);
3236

3337
/**
3438
* Constructor.
@@ -158,10 +162,34 @@ public function process(Request $request, Response $response)
158162

159163
// we don't use a proper XML parser here as we can have ESI tags in a plain text response
160164
$content = $response->getContent();
161-
$content = str_replace(array('<?', '<%'), array('<?php echo "<?"; ?>', '<?php echo "<%"; ?>'), $content);
162-
$content = preg_replace_callback('#<esi\:include\s+(.*?)\s*(?:/|</esi\:include)>#', array($this, 'handleEsiIncludeTag'), $content);
163-
$content = preg_replace('#<esi\:comment[^>]*(?:/|</esi\:comment)>#', '', $content);
164165
$content = preg_replace('#<esi\:remove>.*?</esi\:remove>#', '', $content);
166+
$content = preg_replace('#<esi\:comment[^>]*(?:/|</esi\:comment)>#', '', $content);
167+
168+
$chunks = preg_split('#<esi\:include\s+(.*?)\s*(?:/|</esi\:include)>#', $content, -1, PREG_SPLIT_DELIM_CAPTURE);
169+
$chunks[0] = str_replace($this->phpEscapeMap[0], $this->phpEscapeMap[1], $chunks[0]);
170+
171+
$i = 1;
172+
while (isset($chunks[$i])) {
173+
$options = array();
174+
preg_match_all('/(src|onerror|alt)="([^"]*?)"/', $chunks[$i], $matches, PREG_SET_ORDER);
175+
foreach ($matches as $set) {
176+
$options[$set[1]] = $set[2];
177+
}
178+
179+
if (!isset($options['src'])) {
180+
throw new \RuntimeException('Unable to process an ESI tag without a "src" attribute.');
181+
}
182+
183+
$chunks[$i] = sprintf('<?php echo $this->esi->handle($this, %s, %s, %s) ?>'."\n",
184+
var_export($options['src'], true),
185+
var_export(isset($options['alt']) ? $options['alt'] : '', true),
186+
isset($options['onerror']) && 'continue' == $options['onerror'] ? 'true' : 'false'
187+
);
188+
++$i;
189+
$chunks[$i] = str_replace($this->phpEscapeMap[0], $this->phpEscapeMap[1], $chunks[$i]);
190+
++$i;
191+
}
192+
$content = implode('', $chunks);
165193

166194
$response->setContent($content);
167195
$response->headers->set('X-Body-Eval', 'ESI');
@@ -214,32 +242,4 @@ public function handle(HttpCache $cache, $uri, $alt, $ignoreErrors)
214242
}
215243
}
216244
}
217-
218-
/**
219-
* Handles an ESI include tag (called internally).
220-
*
221-
* @param array $attributes An array containing the attributes.
222-
*
223-
* @return string The response content for the include.
224-
*
225-
* @throws \RuntimeException
226-
*/
227-
private function handleEsiIncludeTag($attributes)
228-
{
229-
$options = array();
230-
preg_match_all('/(src|onerror|alt)="([^"]*?)"/', $attributes[1], $matches, PREG_SET_ORDER);
231-
foreach ($matches as $set) {
232-
$options[$set[1]] = $set[2];
233-
}
234-
235-
if (!isset($options['src'])) {
236-
throw new \RuntimeException('Unable to process an ESI tag without a "src" attribute.');
237-
}
238-
239-
return sprintf('<?php echo $this->esi->handle($this, %s, %s, %s) ?>'."\n",
240-
var_export($options['src'], true),
241-
var_export(isset($options['alt']) ? $options['alt'] : '', true),
242-
isset($options['onerror']) && 'continue' == $options['onerror'] ? 'true' : 'false'
243-
);
244-
}
245245
}

src/Symfony/Component/HttpKernel/Tests/HttpCache/EsiTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,10 +124,10 @@ public function testProcessEscapesPhpTags()
124124
$esi = new Esi();
125125

126126
$request = Request::create('/');
127-
$response = new Response('foo <?php die("foo"); ?><%= "lala" %>');
127+
$response = new Response('<?php <? <% <script language=php>');
128128
$esi->process($request, $response);
129129

130-
$this->assertEquals('foo <?php echo "<?"; ?>php die("foo"); ?><?php echo "<%"; ?>= "lala" %>', $response->getContent());
130+
$this->assertEquals('<?php echo "<?"; ?>php <?php echo "<?"; ?> <?php echo "<%"; ?> <?php echo "<s"; ?>cript language=php>', $response->getContent());
131131
}
132132

133133
/**

0 commit comments

Comments
 (0)
0