fix: reject Windows view paths that leave the views directory

Normalize both separators before the containment check, and build the regression next to the views directory so it stays on the same drive.
pull/729/head
Ambrose Casanova 4 days ago
parent 63e5bcd613
commit 8093a18fb3

@ -222,17 +222,19 @@ class View
*/
private function relativeStaysInside(string $file): bool
{
$segments = \preg_split('#[\\/]+#', $file, -1, \PREG_SPLIT_NO_EMPTY);
if ($segments === false) {
return false;
}
$segments = \explode('/', \str_replace('\\', '/', $file));
$depth = 0;
foreach ($segments as $segment) {
if ($segment === '.') {
if ($segment === '' || $segment === '.') {
continue;
}
// A drive letter or colon is an absolute jump, not a view name.
if (\strpos($segment, ':') !== false) {
return false;
}
if ($segment === '..') {
if ($depth === 0) {
return false;

@ -138,14 +138,11 @@ class ViewTest extends TestCase
public function testRejectsTemplateThatLeavesViewsDirectory(): void
{
$outside = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'flight-view-outside-' . uniqid();
$outside = dirname($this->view->path) . DIRECTORY_SEPARATOR . 'flight-view-outside-' . uniqid();
mkdir($outside);
$note = $outside . DIRECTORY_SEPARATOR . 'note.php';
file_put_contents($note, '<?php echo "blocked";');
$views = realpath($this->view->path);
$relative = $this->relativePathFrom($views, $note);
$relative = preg_replace('/\.php$/', '', $relative);
$relative = '..' . DIRECTORY_SEPARATOR . basename($outside) . DIRECTORY_SEPARATOR . 'note';
try {
$this->expectException(Exception::class);
@ -157,6 +154,13 @@ class ViewTest extends TestCase
}
}
public function testRejectsEmbeddedDriveLetter(): void
{
$this->expectException(Exception::class);
$this->expectExceptionMessage('Template path is not allowed.');
$this->view->getTemplate('layouts' . DIRECTORY_SEPARATOR . 'C:' . DIRECTORY_SEPARATOR . 'outside');
}
public function testRejectsTemplateThatResolvesOutsideViewsDirectory(): void
{
$root = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'flight-view-root-' . uniqid();
@ -194,20 +198,6 @@ class ViewTest extends TestCase
$view->render('hello');
}
private function relativePathFrom(string $fromDir, string $toFile): string
{
$from = explode(DIRECTORY_SEPARATOR, rtrim($fromDir, DIRECTORY_SEPARATOR));
$to = explode(DIRECTORY_SEPARATOR, $toFile);
$file = array_pop($to);
while ($from !== [] && $to !== [] && $from[0] === $to[0]) {
array_shift($from);
array_shift($to);
}
$up = array_fill(0, count($from), '..');
return implode(DIRECTORY_SEPARATOR, array_merge($up, $to, [$file]));
}
private function removeDir(string $dir): void
{

Loading…
Cancel
Save