diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..ca423ee85 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,48 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Listen for Xdebug", + "type": "php", + "request": "launch", + "port": 9003 + }, + { + "name": "Launch currently open script", + "type": "php", + "request": "launch", + "program": "${file}", + "cwd": "${fileDirname}", + "port": 0, + "runtimeArgs": [ + "-dxdebug.start_with_request=yes" + ], + "env": { + "XDEBUG_MODE": "debug,develop", + "XDEBUG_CONFIG": "client_port=${port}" + } + }, + { + "name": "Launch Built-in web server", + "type": "php", + "request": "launch", + "runtimeArgs": [ + "-dxdebug.mode=debug", + "-dxdebug.start_with_request=yes", + "-S", + "localhost:8000" + ], + "env": { + "ENVIRONMENT": "dev" + }, + "program": "${workspaceRoot}/src/index.php", // Updated to src directory + "cwd": "${workspaceRoot}/src", // Updated working directory to src + "port": 9003, + "serverReadyAction": { + "pattern": "Development Server \\(http://localhost:([0-9]+)\\) started", + "uriFormat": "http://localhost:%s", + "action": "openExternally" + } + } + ] +} \ No newline at end of file diff --git a/RELEASENOTES.md b/RELEASENOTES.md index dbf64de5e..27a8383d5 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,5 +1,8 @@ # Release Notes +### 4.4.3 (UNRELEASED) +* Fix for blocklist capability working inconsistenly [#1329] + ### 4.4.2 (March 12, 2025) * Support for Gather 2.0 added. [#1323] diff --git a/src/app/Http/Middleware/CallBlocklist.php b/src/app/Http/Middleware/CallBlocklist.php index 3838248a8..64f070f5a 100644 --- a/src/app/Http/Middleware/CallBlocklist.php +++ b/src/app/Http/Middleware/CallBlocklist.php @@ -29,11 +29,14 @@ public function __construct(SettingsService $settings) public function handle(Request $request, Closure $next) { if ($this->settings->has("blocklist") && - strlen($this->settings->get("blocklist") > 0) && + strlen($this->settings->get("blocklist")) > 0 && $request->has("Caller")) { + $caller = $this->ensureLeadingPlus($request->get('Caller')); + $blocklistItems = explode(",", $this->settings->get('blocklist')); foreach ($blocklistItems as $blocklistItem) { - if (str_starts_with($blocklistItem, $request->get('Caller'))) { + $blocklistItem = $this->ensureLeadingPlus($blocklistItem); + if (str_starts_with($blocklistItem, $caller)) { return response()->view('rejectCall')->header("Content-Type", "text/xml; charset=utf-8"); } } @@ -41,4 +44,19 @@ public function handle(Request $request, Closure $next) return $next($request); } + + /** + * Ensures a phone number has a leading plus sign and no whitespace + * + * @param string $number + * @return string + */ + private function ensureLeadingPlus(string $number): string + { + $number = trim($number); + if (!str_starts_with($number, '+')) { + return '+' . $number; + } + return $number; + } } diff --git a/src/app/Services/SettingsService.php b/src/app/Services/SettingsService.php index 3bbe6dca4..8fd83d8b0 100644 --- a/src/app/Services/SettingsService.php +++ b/src/app/Services/SettingsService.php @@ -11,7 +11,7 @@ class SettingsService { - private string $version = "4.4.2"; + private string $version = "4.4.3"; private array $allowlist = [ 'announce_servicebody_volunteer_routing' => ['description' => '/helpline/announce_servicebody_volunteer_routing' , 'default' => false, 'overridable' => true, 'hidden' => false], 'blocklist' => ['description' => '/general/blocklist' , 'default' => '', 'overridable' => true, 'hidden' => false], diff --git a/src/tests/Feature/BlocklistTest.php b/src/tests/Feature/BlocklistTest.php index f2f76a333..bf38bbbc5 100644 --- a/src/tests/Feature/BlocklistTest.php +++ b/src/tests/Feature/BlocklistTest.php @@ -1,4 +1,7 @@ set("blocklist", $caller); + app()->instance(SettingsService::class, $settingsService); $response = $this->call($method, '/', [ "Caller"=>$caller ]); @@ -22,9 +27,25 @@ ->assertSeeInOrderExact([""], false); })->with(['GET', 'POST']); +test('test the blocklist with multiple items and an exact match', function ($method) { + $caller = "+15557778888,+15557778890"; + $settingsService = new SettingsService(); + $settingsService->set("blocklist", $caller); + app()->instance(SettingsService::class, $settingsService); + $response = $this->call($method, '/', [ + "Caller"=>"+15557778890" + ]); + $response + ->assertStatus(200) + ->assertHeader("Content-Type", "text/xml; charset=utf-8") + ->assertSeeInOrderExact([""], false); +})->with(['GET', 'POST']); + test('test the blocklist without a match', function ($method) { - $caller = "5557778888"; - $_SESSION['override_blocklist'] = $caller; + $caller = "+15557778888"; + $settingsService = new SettingsService(); + $settingsService->set("blocklist", $caller); + app()->instance(SettingsService::class, $settingsService); $response = $this->call($method, '/', [ "Caller"=>"5557778889" ]); @@ -33,3 +54,98 @@ ->assertHeader("Content-Type", "text/xml; charset=utf-8") ->assertDontSee([""], false); })->with(['GET', 'POST']); + +test('test the blocklist with whitespace in caller number', function ($method) { + $caller = "+15557778888"; + $settingsService = new SettingsService(); + $settingsService->set("blocklist", $caller); + app()->instance(SettingsService::class, $settingsService); + $response = $this->call($method, '/', [ + "Caller" => " 15557778888 " + ]); + $response + ->assertStatus(200) + ->assertHeader("Content-Type", "text/xml; charset=utf-8") + ->assertSeeInOrderExact([""], false); +})->with(['GET', 'POST']); + +test('test the blocklist with whitespace in blocklist entries', function ($method) { + $caller = " +15557778888 , +15557778890 "; + $settingsService = new SettingsService(); + $settingsService->set("blocklist", $caller); + app()->instance(SettingsService::class, $settingsService); + $response = $this->call($method, '/', [ + "Caller" => "+15557778890" + ]); + $response + ->assertStatus(200) + ->assertHeader("Content-Type", "text/xml; charset=utf-8") + ->assertSeeInOrderExact([""], false); +})->with(['GET', 'POST']); + +test('test the blocklist with missing plus sign in caller number', function ($method) { + $caller = "+15557778888"; + $settingsService = new SettingsService(); + $settingsService->set("blocklist", $caller); + app()->instance(SettingsService::class, $settingsService); + $response = $this->call($method, '/', [ + "Caller" => "15557778888" + ]); + $response + ->assertStatus(200) + ->assertHeader("Content-Type", "text/xml; charset=utf-8") + ->assertSeeInOrderExact([""], false); +})->with(['GET', 'POST']); + +test('test the blocklist with missing plus sign in blocklist entries', function ($method) { + $caller = "15557778888,15557778890"; + $settingsService = new SettingsService(); + $settingsService->set("blocklist", $caller); + app()->instance(SettingsService::class, $settingsService); + $response = $this->call($method, '/', [ + "Caller" => "+15557778890" + ]); + $response + ->assertStatus(200) + ->assertHeader("Content-Type", "text/xml; charset=utf-8") + ->assertSeeInOrderExact([""], false); +})->with(['GET', 'POST']); + +test('test the blocklist with empty blocklist', function ($method) { + $settingsService = new SettingsService(); + $settingsService->set("blocklist", ""); + app()->instance(SettingsService::class, $settingsService); + $response = $this->call($method, '/', [ + "Caller" => "+15557778888" + ]); + $response + ->assertStatus(200) + ->assertHeader("Content-Type", "text/xml; charset=utf-8") + ->assertDontSee([""], false); +})->with(['GET', 'POST']); + +test('test the blocklist with missing Caller parameter', function ($method) { + $caller = "+15557778888"; + $settingsService = new SettingsService(); + $settingsService->set("blocklist", $caller); + app()->instance(SettingsService::class, $settingsService); + $response = $this->call($method, '/', []); + $response + ->assertStatus(200) + ->assertHeader("Content-Type", "text/xml; charset=utf-8") + ->assertDontSee([""], false); +})->with(['GET', 'POST']); + +test('test the blocklist with partial match', function ($method) { + $caller = "+15557778888"; + $settingsService = new SettingsService(); + $settingsService->set("blocklist", $caller); + app()->instance(SettingsService::class, $settingsService); + $response = $this->call($method, '/', [ + "Caller" => "+1555777888899" + ]); + $response + ->assertStatus(200) + ->assertHeader("Content-Type", "text/xml; charset=utf-8") + ->assertDontSee([""], false); +})->with(['GET', 'POST']);