Skip to content

Commit 9faedfb

Browse files
committed
Merge branch 'swoole-serve-files-from-symlink'
2 parents 2ef19b9 + 432e75e commit 9faedfb

File tree

4 files changed

+75
-2
lines changed

4 files changed

+75
-2
lines changed

src/Swoole/SwooleClient.php

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,19 +56,62 @@ public function canServeRequestAsStaticFile(Request $request, RequestContext $co
5656

5757
$publicPath = $context->publicPath;
5858

59+
$pathToFile = realpath($publicPath.'/'.$request->path());
60+
61+
if ($this->isValidFileWithinSymlink($request, $publicPath, $pathToFile)) {
62+
$pathToFile = $publicPath.'/'.$request->path();
63+
}
64+
5965
return $this->fileIsServable(
6066
$publicPath,
61-
realpath($publicPath.'/'.$request->path()),
67+
$pathToFile,
6268
);
6369
}
6470

6571
/**
66-
* Determine if the given file is servable.
72+
* Determine if the request is for a valid static file within a symlink.
6773
*
74+
* @param \Illuminate\Http\Request $request
6875
* @param string $publicPath
6976
* @param string $pathToFile
7077
* @return bool
7178
*/
79+
private function isValidFileWithinSymlink(Request $request, string $publicPath, string $pathToFile): bool
80+
{
81+
$pathAfterSymlink = $this->pathAfterSymlink($publicPath, $request->path());
82+
83+
return $pathAfterSymlink && str_ends_with($pathToFile, $pathAfterSymlink);
84+
}
85+
86+
/**
87+
* If the given public file is within a symlinked directory, return the path after the symlink.
88+
*
89+
* @param string $publicPath
90+
* @param string $path
91+
* @return string|bool
92+
*/
93+
private function pathAfterSymlink(string $publicPath, string $path)
94+
{
95+
$directories = explode('/', $path);
96+
97+
while ($directory = array_shift($directories)) {
98+
$publicPath .= '/'.$directory;
99+
100+
if (is_link($publicPath)) {
101+
return implode('/', $directories);
102+
}
103+
}
104+
105+
return false;
106+
}
107+
108+
/**
109+
* Determine if the given file is servable.
110+
*
111+
* @param string $publicPath
112+
* @param string $pathToFile
113+
* @return bool
114+
*/
72115
protected function fileIsServable(string $publicPath, string $pathToFile): bool
73116
{
74117
return $pathToFile &&

tests/SwooleClientTest.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,34 @@ public function test_static_file_can_be_served()
116116
$client->serveStaticFile($request, $context);
117117
}
118118

119+
/** @test */
120+
public function test_can_serve_static_files_through_symlink()
121+
{
122+
$client = new SwooleClient;
123+
124+
$request = Request::create('/symlink/foo.txt', 'GET');
125+
126+
$context = new RequestContext([
127+
'publicPath' => __DIR__.'/public/files',
128+
]);
129+
130+
$this->assertTrue($client->canServeRequestAsStaticFile($request, $context));
131+
}
132+
133+
/** @test */
134+
public function test_cant_serve_static_files_through_symlink_using_directory_traversal()
135+
{
136+
$client = new SwooleClient;
137+
138+
$request = Request::create('/symlink/../files/bar.txt', 'GET');
139+
140+
$context = new RequestContext([
141+
'publicPath' => __DIR__.'/public/files',
142+
]);
143+
144+
$this->assertFalse($client->canServeRequestAsStaticFile($request, $context));
145+
}
146+
119147
/** @doesNotPerformAssertions @test */
120148
public function test_respond_method_send_response_to_swoole()
121149
{

tests/public/files/symlink

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../symlinkedFolder
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
foobar

0 commit comments

Comments
 (0)