10000 [HttpKernel] Don't use eval() to render ESI/SSI · symfony/symfony@ea449ca · GitHub
[go: up one dir, main page]

Skip to content

Commit ea449ca

Browse files
[HttpKernel] Don't use eval() to render ESI/SSI
1 parent 1d52937 commit ea449ca

File tree

5 files changed

+45
-29
lines changed

5 files changed

+45
-29
lines changed

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

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,10 @@ public function process(Request $request, Response $response)
8080
$content = preg_replace('#<esi\:remove>.*?</esi\:remove>#s', '', $content);
8181
$content = preg_replace('#<esi\:comment[^>]+>#s', '', $content);
8282

83+
static $cookie;
84+
$cookie = hash('md5', $cookie ?? $cookie = random_bytes(16), true);
85+
$boundary = base64_encode($cookie);
8386
$chunks = preg_split('#<esi\:include\s+(.*?)\s*(?:/|</esi\:include)>#', $content, -1, \PREG_SPLIT_DELIM_CAPTURE);
84-
$chunks[0] = str_replace($this->phpEscapeMap[0], $this->phpEscapeMap[1], $chunks[0]);
8587

8688
$i = 1;
8789
while (isset($chunks[$i])) {
@@ -95,16 +97,10 @@ public function process(Request $request, Response $response)
9597
throw new \RuntimeException('Unable to process an ESI tag without a "src" attribute.');
9698
}
9799

98-
$chunks[$i] = sprintf('<?php echo $this->surrogate->handle($this, %s, %s, %s) ?>'."\n",
99-
var_export($options['src'], true),
100-
var_export($options['alt'] ?? '', true),
101-
isset($options['onerror']) && 'continue' === $options['onerror'] ? 'true' : 'false'
102-
);
103-
++$i;
104-
$chunks[$i] = str_replace($this->phpEscapeMap[0], $this->phpEscapeMap[1], $chunks[$i]);
105-
++$i;
100+
$chunks[$i] = $boundary.$options['src']."\n".($options['alt'] ?? '')."\n".('continue' === ($options['onerror'] ?? ''))."\n";
101+
$i += 2;
106102
}
107-
$content = implode('', $chunks);
103+
$content = $boundary.implode('', $chunks).$boundary;
108104

109105
$response->setContent($content);
110106
$response->headers->set('X-Body-Eval', 'ESI');

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -636,7 +636,21 @@ private function restoreResponseBody(Request $request, Response $response)
636636
if ($response->headers->has('X-Body-File')) {
637637
include $response->headers->get('X-Body-File');
638638
} else {
639-
eval('; ?>'.$response->getContent().'<?php ;');
639+
$content = $response->getContent();
640+
641+
if (substr($content, -24) === $boundary = substr($content, 0, 24)) {
642+
$j = strpos($content, $boundary, 24);
643+
echo substr($content, 24, $j - 24);
644+
$i = $j + 24;
645+
646+
while (false !== $j = strpos($content, $boundary, $i)) {
647+
[$uri, $alt, $ignoreErrors, $part] = explode("\n", substr($content, $i, $j - $i), 4);
648+
$i = $j + 24;
649+
650+
echo $this->surrogate->handle($this, $uri, $alt, $ignoreErrors);
651+
echo $part;
652+
}
653+
}
640654
}
641655

642656
$response->setContent(ob_get_clean());

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

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,10 @@ public function process(Request $request, Response $response)
6565
// we don't use a proper XML parser here as we can have SSI tags in a plain text response
6666
$content = $response->getContent();
6767

68+
static $cookie;
69+
$cookie = hash('md5', $cookie ?? $cookie = random_bytes(16), true);
70+
$boundary = base64_encode($cookie);
6871
$chunks = preg_split('#<!--\#include\s+(.*?)\s*-->#', $content, -1, \PREG_SPLIT_DELIM_CAPTURE);
69-
$chunks[0] = str_replace($this->phpEscapeMap[0], $this->phpEscapeMap[1], $chunks[0]);
7072

7173
$i = 1;
7274
while (isset($chunks[$i])) {
@@ -80,14 +82,10 @@ public function process(Request $request, Response $response)
8082
throw new \RuntimeException('Unable to process an SSI tag without a "virtual" attribute.');
8183
}
8284

83-
$chunks[$i] = sprintf('<?php echo $this->surrogate->handle($this, %s, \'\', false) ?>'."\n",
84-
var_export($options['virtual'], true)
85-
);
86-
++$i;
87-
$chunks[$i] = str_replace($this->phpEscapeMap[0], $this->phpEscapeMap[1], $chunks[$i]);
88-
++$i;
85+
$chunks[$i] = $boundary.$options['virtual']."\n\n\n";
86+
$i += 2;
8987
}
90-
$content = implode('', $chunks);
88+
$content = $boundary.implode('', $chunks).$boundary;
9189

9290
$response->setContent($content);
9391
$response->headers->set('X-Body-Eval', 'SSI');

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

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ public function testMultilineEsiRemoveTagsAreRemoved()
102102
$response = new Response('<esi:remove> <a href="http://www.example.com">www.example.com</a> </esi:remove> Keep this'."<esi:remove>\n <a>www.example.com</a> </esi:remove> And this");
103103
$this->assertSame($response, $esi->process($request, $response));
104104

105-
$this->assertEquals(' Keep this And this', $response->getContent());
105+
$this->assertEquals(' Keep this And this', substr($response->getContent(), 24, -24));
106106
}
107107

108108
public function testCommentTagsAreRemoved()
@@ -113,7 +113,7 @@ public function testCommentTagsAreRemoved()
113113
$response = new Response('<esi:comment text="some comment &gt;" /> Keep this');
114114
$this->assertSame($response, $esi->process($request, $response));
115115

116-
$this->assertEquals(' Keep this', $response->getContent());
116+
$this->assertEquals(' Keep this', substr($response->getContent(), 24, -24));
117117
}
118118

119119
public function testProcess()
@@ -124,23 +124,27 @@ public function testProcess()
124124
$response = new Response('foo <esi:comment text="some comment" /><esi:include src="..." alt="alt" onerror="continue" />');
125125
$this->assertSame($response, $esi->process($request, $response));
126126

127-
$this->assertEquals('foo <?php echo $this->surrogate->handle($this, \'...\', \'alt\', true) ?>'."\n", $response->getContent());
127+
$content = explode(substr($response->getContent(), 0, 24), $response->getContent());
128+
$this->assertSame(['', 'foo ', "...\nalt\n1\n", ''], $content);
128129
$this->assertEquals('ESI', $response->headers->get('x-body-eval'));
129130

130131
$response = new Response('foo <esi:comment text="some comment" /><esi:include src="foo\'" alt="bar\'" onerror="continue" />');
131132
$this->assertSame($response, $esi->process($request, $response));
132133

133-
$this->assertEquals('foo <?php echo $this->surrogate->handle($this, \'foo\\\'\', \'bar\\\'\', true) ?>'."\n", $response->getContent());
134+
$content = explode(substr($response->getContent(), 0, 24), $response->getContent());
135+
$this->assertSame(['', 'foo ', "foo'\nbar'\n1\n", ''], $content);
134136

135137
$response = new Response('foo <esi:include src="..." />');
136138
$this->assertSame($response, $esi->process($request, $response));
137139

138-
$this->assertEquals('foo <?php echo $this->surrogate->handle($this, \'...\', \'\', false) ?>'."\n", $response->getContent());
140+
$content = explode(substr($response->getContent(), 0, 24), $response->getContent());
141+
$this->assertSame(['', 'foo ', "...\n\n\n", ''], $content);
139142

140143
$response = new Response('foo <esi:include src="..."></esi:include>');
141144
$this->assertSame($response, $esi->process($request, $response));
142145

143-
$this->assertEquals('foo <?php echo $this->surrogate->handle($this, \'...\', \'\', false) ?>'."\n", $response->getContent());
146+
$content = explode(substr(< 10000 span class=pl-c1>$response->getContent(), 0, 24), $response->getContent());
147+
$this->assertSame(['', 'foo ', "...\n\n\n", ''], $content);
144148
}
145149

146150
public function testProcessEscapesPhpTags()
@@ -151,7 +155,8 @@ public function testProcessEscapesPhpTags()
151155
$response = new Response('<?php <? <% <script language=php>');
152156
$this->assertSame($response, $esi->process($request, $response));
153157

154-
$this->assertEquals('<?php echo "<?"; ?>php <?php echo "<?"; ?> <?php echo "<%"; ?> <?php echo "<s"; ?>cript language=php>', $response->getContent());
158+
$content = explode(substr($response->getContent(), 0, 24), $response->getContent());
159+
$this->assertSame(['', '<?php <? <% <script language=php>', ''], $content);
155160
}
156161

157162
public function testProcessWhenNoSrcInAnEsi()

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,15 @@ public function testProcess()
101101
$response = new Response('foo <!--#include virtual="..." -->');
102102
$ssi->process($request, $response);
103103

104-
$this->assertEquals('foo <?php echo $this->surrogate->handle($this, \'...\', \'\', false) ?>'."\n", $response->getContent());
104+
$content = explode(substr($response->getContent(), 0, 24), $response->getContent());
105+
$this->assertSame(['', 'foo ', "...\n\n\n", ''], $content);
105106
$this->assertEquals('SSI', $response->headers->get('x-body-eval'));
106107

107108
$response = new Response('foo <!--#include virtual="foo\'" -->');
108109
$ssi->process($request, $response);
109110

110-
$this->assertEquals("foo <?php echo \$this->surrogate->handle(\$this, 'foo\\'', '', false) ?>\n", $response->getContent());
111+
$content = explode(substr($response->getContent(), 0, 24), $response->getContent());
112+
$this->assertSame(['', 'foo ', "foo'\n\n\n", ''], $content);
111113
}
112114

113115
public function testProcessEscapesPhpTags()
@@ -118,7 +120,8 @@ public function testProcessEscapesPhpTags()
118120
$response = new Response('<?php <? <% <script language=php>');
119121
$ssi->process($request, $response);
120122

121-
$this->assertEquals('<?php echo "<?"; ?>php <?php echo "<?"; ?> <?php echo "<%"; ?> <?php echo "<s"; ?>cript language=php>', $response->getContent());
123+
$content = explode(substr($response->getContent(), 0, 24), $response->getContent());
124+
$this->assertSame(['', '<?php <? <% <script language=php>', ''], $content);
122125
}
123126

124127
public function testProcessWhenNoSrcInAnSsi()

0 commit comments

Comments
 (0)
0