From 8bac9feef71c1baa0001bc7e3eca1ca7e624de92 Mon Sep 17 00:00:00 2001 From: Patrik Date: Mon, 2 Aug 2021 10:44:20 +0300 Subject: [PATCH] Disable executing solutions on non-local environments or from non-local IP addresses on version 1.x (#404) * Solutions can only be ran from local environments and local IPs * Set fail-fast to false for run-tests workflow * Revert set fail-fast to false for run-tests workflow * Skip execute solution tests for Laravel below version 5.7.14 Co-authored-by: Patrik Rusk --- .../Controllers/ExecuteSolutionController.php | 23 +++++ .../ExecuteSolutionControllerTest.php | 84 +++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 tests/Http/Controllers/ExecuteSolutionControllerTest.php diff --git a/src/Http/Controllers/ExecuteSolutionController.php b/src/Http/Controllers/ExecuteSolutionController.php index 9d16b5fd..adbdaef3 100644 --- a/src/Http/Controllers/ExecuteSolutionController.php +++ b/src/Http/Controllers/ExecuteSolutionController.php @@ -14,10 +14,33 @@ public function __invoke( ExecuteSolutionRequest $request, SolutionProviderRepository $solutionProviderRepository ) { + $this->ensureLocalEnvironment(); + $this->ensureLocalRequest(); + $solution = $request->getRunnableSolution(); $solution->run($request->get('parameters', [])); return response(''); } + + public function ensureLocalEnvironment() + { + if (! app()->environment('local')) { + abort(403, "Runnable solutions are disabled in non-local environments. Please make sure `APP_ENV` is set correctly. Additionally please make sure `APP_DEBUG` is set to false on ANY production environment!"); + } + } + + public function ensureLocalRequest() + { + $ipIsPublic = filter_var( + request()->ip(), + FILTER_VALIDATE_IP, + FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE + ); + + if ($ipIsPublic) { + abort(403, "Solutions can only be executed by requests from a local IP address. Please also make sure `APP_DEBUG` is set to false on ANY production environment."); + } + } } diff --git a/tests/Http/Controllers/ExecuteSolutionControllerTest.php b/tests/Http/Controllers/ExecuteSolutionControllerTest.php new file mode 100644 index 00000000..d475b8aa --- /dev/null +++ b/tests/Http/Controllers/ExecuteSolutionControllerTest.php @@ -0,0 +1,84 @@ +app->version(), '5.7.14', '<')) { + $this->markTestSkipped('Laravel version < 5.7.14 does not support setting the environment to running in console'); + } + + $this->app['env'] = 'local'; + $this->app['config']->set('app.debug', true); + + $this->postJson(route('ignition.executeSolution'), $this->solutionPayload()) + ->assertSuccessful(); + } + + /** @test */ + public function it_wont_execute_solutions_on_a_production_environment() + { + if (version_compare($this->app->version(), '5.7.14', '<')) { + $this->markTestSkipped('Laravel version < 5.7.14 does not support setting the environment to running in console'); + } + + $this->app['env'] = 'production'; + $this->app['config']->set('app.debug', true); + + $this->postJson(route('ignition.executeSolution'), $this->solutionPayload()) + ->assertForbidden(); + } + + /** @test */ + public function it_wont_execute_solutions_when_debugging_is_disabled() + { + if (version_compare($this->app->version(), '5.7.14', '<')) { + $this->markTestSkipped('Laravel version < 5.7.14 does not support setting the environment to running in console'); + } + + $this->app['env'] = 'local'; + $this->app['config']->set('app.debug', false); + + $this->postJson(route('ignition.executeSolution'), $this->solutionPayload()) + ->assertNotFound(); + } + + /** @test */ + public function it_wont_execute_solutions_for_a_non_local_ip() + { + if (version_compare($this->app->version(), '5.7.14', '<')) { + $this->markTestSkipped('Laravel version < 5.7.14 does not support setting the environment to running in console'); + } + + $this->app['env'] = 'local'; + $this->app['config']->set('app.debug', true); + $this->withServerVariables(['REMOTE_ADDR' => '138.197.187.74']); + + $this->postJson(route('ignition.executeSolution'), $this->solutionPayload()) + ->assertForbidden(); + } + + protected function solutionPayload(): array + { + return [ + 'parameters' => [ + 'variableName' => 'test', + 'viewFile' => 'resources/views/welcome.blade.php', + ], + 'solution' => 'Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution', + ]; + } +}