From 5cb68cda16be3ac5825187acc2c39b30e83e780c Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Fri, 2 Oct 2026 16:01:00 +0200 Subject: [PATCH] fix(api): return JSON validation errors and reject unknown fields on PATCH /team Co-Authored-By: Claude Opus 5.5 --- app/Http/Controllers/Api/TeamController.php | 18 ++++++- .../Api/TeamUpdateValidationApiTest.php | 50 +++++++++++++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 tests/Feature/Api/TeamUpdateValidationApiTest.php diff --git a/app/Http/Controllers/Api/TeamController.php b/app/Http/Controllers/Api/TeamController.php index ed8d34d226..98274e2d0d 100644 --- a/app/Http/Controllers/Api/TeamController.php +++ b/app/Http/Controllers/Api/TeamController.php @@ -268,11 +268,25 @@ class TeamController extends Controller } $this->authorize('update', $team); - $validated = $request->validate([ + + $allowedFields = ['is_build_server_fallback_enabled']; + $validator = customApiValidator($request->all(), [ 'is_build_server_fallback_enabled' => ['required', 'boolean'], ]); + $extraFields = array_diff(array_keys($request->all()), $allowedFields); + if ($validator->fails() || ! empty($extraFields)) { + $errors = $validator->errors(); + foreach ($extraFields as $field) { + $errors->add($field, 'This field is not allowed.'); + } - $team->update($validated); + return response()->json([ + 'message' => 'Validation failed.', + 'errors' => $errors, + ], 422); + } + + $team->update($validator->validated()); return response()->json($this->removeSensitiveData($team)); } diff --git a/tests/Feature/Api/TeamUpdateValidationApiTest.php b/tests/Feature/Api/TeamUpdateValidationApiTest.php new file mode 100644 index 0000000000..72febacf9b --- /dev/null +++ b/tests/Feature/Api/TeamUpdateValidationApiTest.php @@ -0,0 +1,50 @@ + InstanceSettings::updateOrCreate(['id' => 0], ['is_api_enabled' => true])); + + $this->team = Team::factory()->create(); + $this->user = User::factory()->create(); + $this->team->members()->attach($this->user->id, ['role' => 'owner']); + session(['currentTeam' => $this->team]); + + $this->bearerToken = $this->user->createToken('team-update-validation', ['write'])->plainTextToken; +}); + +test('PATCH /team returns JSON validation errors without an Accept header', function () { + $response = $this->call( + 'PATCH', + '/api/v1/team', + server: [ + 'HTTP_AUTHORIZATION' => 'Bearer '.$this->bearerToken, + 'CONTENT_TYPE' => 'application/json', + ], + content: json_encode(['is_build_server_fallback_enabled' => 'invalid']), + ); + + $response->assertUnprocessable() + ->assertJsonPath('message', 'Validation failed.') + ->assertJsonStructure(['errors' => ['is_build_server_fallback_enabled']]); + expect($this->team->fresh()->is_build_server_fallback_enabled)->toBeTrue(); +}); + +test('PATCH /team rejects unknown fields', function () { + $this->withToken($this->bearerToken) + ->patchJson('/api/v1/team', [ + 'is_build_server_fallback_enabled' => false, + 'name' => 'renamed', + ]) + ->assertUnprocessable() + ->assertJsonPath('errors.name.0', 'This field is not allowed.'); + + expect($this->team->fresh()) + ->is_build_server_fallback_enabled->toBeTrue() + ->name->not->toBe('renamed'); +});