From 4161a65e0be5d9605b102161571549ef0e986224 Mon Sep 17 00:00:00 2001 From: Matej Baco Date: Mon, 31 Jan 2022 16:04:30 +0100 Subject: [PATCH] Path validator + tests --- app/controllers/general.php | 10 +++- src/Appwrite/Storage/Validator/Path.php | 78 +++++++++++++++++++++++++ tests/e2e/Client.php | 17 +++++- tests/e2e/General/HTTPTest.php | 17 ++++++ 4 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 src/Appwrite/Storage/Validator/Path.php diff --git a/app/controllers/general.php b/app/controllers/general.php index 7f82c36a3..64e122ff8 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -2,6 +2,7 @@ require_once __DIR__.'/../init.php'; +use Appwrite\Storage\Validator\Path; use Utopia\App; use Utopia\Logger\Log; use Utopia\Logger\Log\User; @@ -525,8 +526,15 @@ App::get('/.well-known/acme-challenge') ->inject('request') ->inject('response') ->action(function ($request, $response) { + $filePath = $request->getURI(); + + $validator = new Path(); + if (!$validator->isValid($filePath)) { + throw new Exception('Invalid file path. Please use relative path without \'../\'', 400); + } + $base = \realpath(APP_STORAGE_CERTIFICATES); - $path = \str_replace('/.well-known/acme-challenge/', '', $request->getURI()); + $path = \str_replace('/.well-known/acme-challenge/', '', $filePath); $absolute = \realpath($base.'/.well-known/acme-challenge/'.$path); if (!$base) { diff --git a/src/Appwrite/Storage/Validator/Path.php b/src/Appwrite/Storage/Validator/Path.php new file mode 100644 index 000000000..110ab47d8 --- /dev/null +++ b/src/Appwrite/Storage/Validator/Path.php @@ -0,0 +1,78 @@ +endpoint; + } + /** * @param string $key * @param string $value @@ -183,12 +195,13 @@ class Client unset($headers[$i]); } + curl_setopt($ch, CURLOPT_PATH_AS_IS, 1); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36'); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); - curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 0); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 0); curl_setopt($ch, CURLOPT_TIMEOUT, 15); curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($curl, $header) use (&$responseHeaders) { $len = strlen($header); diff --git a/tests/e2e/General/HTTPTest.php b/tests/e2e/General/HTTPTest.php index a83237928..4e2647f31 100644 --- a/tests/e2e/General/HTTPTest.php +++ b/tests/e2e/General/HTTPTest.php @@ -94,6 +94,23 @@ class HTTPTest extends Scope $this->assertStringContainsString('# robotstxt.org/', $response['body']); } + public function testAcmeChallenge() + { + $previousEndpoint = $this->client->getEndpoint(); + $this->client->setEndpoint("http://localhost"); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_GET, '/.well-known/acme-challenge/../../../../../../../etc/passwd', \array_merge([ + 'origin' => 'http://localhost', + ]), []); + + $this->client->setEndpoint($previousEndpoint); + + $this->assertEquals(400, $response['headers']['status-code']); + } + // public function testSpecSwagger2() // { // $response = $this->client->call(Client::METHOD_GET, '/specs/swagger2?platform=client', [