Skip to content

Commit 4efd965

Browse files
authored
feature: escape filename (#71)
1 parent d1c10fa commit 4efd965

2 files changed

Lines changed: 36 additions & 4 deletions

File tree

src/FileAccess.php

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ public function __construct(
88
}
99

1010
public function getData(string $name):mixed {
11-
$filePath = "$this->dirPath/$name";
11+
$filePath = $this->getFilePath($name);
1212
if(!is_file($filePath)) {
1313
throw new FileNotFoundException($filePath);
1414
}
@@ -18,15 +18,15 @@ public function getData(string $name):mixed {
1818
}
1919

2020
public function setData(string $name, mixed $value):void {
21-
$filePath = "$this->dirPath/$name";
21+
$filePath = $this->getFilePath($name);
2222
if(!is_dir(dirname($filePath))) {
2323
mkdir(dirname($filePath), 0775, true);
2424
}
2525
file_put_contents($filePath, serialize($value));
2626
}
2727

2828
public function checkValidity(string $name, int $secondsValidity):void {
29-
$filePath = "$this->dirPath/$name";
29+
$filePath = $this->getFilePath($name);
3030
if(!is_file($filePath)) {
3131
throw new CacheInvalidException("$filePath (does not exist)");
3232
}
@@ -37,11 +37,16 @@ public function checkValidity(string $name, int $secondsValidity):void {
3737
}
3838

3939
public function invalidate(string $name):void {
40-
$filePath = "$this->dirPath/$name";
40+
$filePath = $this->getFilePath($name);
4141
if(!is_file($filePath)) {
4242
return;
4343
}
4444

4545
unlink($filePath);
4646
}
47+
48+
private function getFilePath(string $name):string {
49+
$escapedName = rawurlencode($name);
50+
return "$this->dirPath/$escapedName";
51+
}
4752
}

test/phpunit/CacheTest.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,33 @@ public function testGet_nullValueCanBeCached():void {
6666
self::assertSame(1, $count);
6767
}
6868

69+
public function testGet_urlName_isEscapedToReadableFilename():void {
70+
$sut = $this->getSut();
71+
$name = "https://example.com/test";
72+
$value = "cached-value";
73+
74+
self::assertSame($value, $sut->get($name, fn() => $value));
75+
76+
$expectedFile = sys_get_temp_dir()
77+
. "/phpgt-filecache/"
78+
. rawurlencode($name);
79+
self::assertFileExists($expectedFile);
80+
self::assertSame($value, unserialize(file_get_contents($expectedFile)));
81+
}
82+
83+
public function testGet_urlName_doesNotTraverseFilesystem():void {
84+
$sut = $this->getSut();
85+
$name = "../outside-cache";
86+
$value = "cached-value";
87+
88+
self::assertSame($value, $sut->get($name, fn() => $value));
89+
90+
$cacheDir = sys_get_temp_dir() . "/phpgt-filecache";
91+
$expectedFile = $cacheDir . "/" . rawurlencode($name);
92+
self::assertFileExists($expectedFile);
93+
self::assertFileDoesNotExist(sys_get_temp_dir() . "/outside-cache");
94+
}
95+
6996
public function testGet_generationExceptionDoesNotWriteInvalidValue():void {
7097
$fileAccess = self::createMock(FileAccess::class);
7198
$fileAccess->expects(self::once())

0 commit comments

Comments
 (0)