Skip to content

Conversation

@Punksolid
Copy link
Owner

@Punksolid Punksolid commented Jan 8, 2026

This pull request adds Cloudflare Turnstile CAPTCHA integration to the authentication and registration flows, as well as to the lessee creation form, to enhance security against automated abuse. It refactors request validation by introducing custom request classes and a reusable validation rule for Turnstile. The views are updated to conditionally display the Turnstile widget when enabled via configuration.

Cloudflare Turnstile Integration

  • Added configuration for Cloudflare Turnstile in config/services.php and phpunit.xml, enabling feature toggling and key management. [1] [2]
  • Implemented TurnstileHelper and TurnstileRule for validating Turnstile tokens server-side. [1] [2]

Authentication and Registration Refactor

  • Introduced LoginRequest and RegisterRequest classes to handle validation, including Turnstile when enabled, with custom error messages. [1] [2]
  • Updated AuthController and RegisterController to use these new request classes and simplified user creation logic. [1] [2] [3] [4] [5]

Lessee Creation Form Security

  • Added Turnstile validation to LesseeRequest and updated the lessee creation view to display the widget and handle errors. [1] [2] [3] [4] [5]

Frontend Updates for Turnstile

  • Modified login and registration Blade views to conditionally show the Turnstile widget and error messages, and ensured scripts are loaded only when needed. [1] [2] [3] [4]
  • Updated layout files to support stacking scripts and improved user menu display and logout button. [1] [2] [3] [4]

Note

Implements optional Cloudflare Turnstile protection and streamlines validation while improving CI/Dusk reliability.

  • Security: New TurnstileHelper and reusable TurnstileRule; adds cf-turnstile-response checks (toggled via config/services.php) in LoginRequest, RegisterRequest, and LesseeRequest
  • Auth refactor: AuthController and RegisterController now use LoginRequest/RegisterRequest; simplifies user creation; changes logout to POST with named route logout in routes/web.php
  • Views: auth/login.blade.php, auth/register.blade.php, and lessee create view conditionally render Turnstile widget and errors; layouts add @stack('scripts') and improve user menu/logout
  • Testing/CI: Overhauls Dusk GitHub Action to install matching Chrome/ChromeDriver, wait for readiness, and set APP_URL; DuskTestCase uses RefreshDatabase, ensures SQLite file, and updates headless Chrome flags; phpunit configs set SQLite and disable Turnstile by default
  • Migrations: Clarifies irreversible down() by throwing RuntimeException

Written by Cursor Bugbot for commit 38a380e. This will update automatically on new commits. Configure here.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request integrates Cloudflare Turnstile CAPTCHA into authentication, registration, and lessee creation flows to protect against automated abuse. The implementation introduces custom request classes for cleaner validation, a reusable Turnstile validation rule, and a helper class for server-side token verification. Frontend views are updated to conditionally display the Turnstile widget based on configuration.

Key changes include:

  • Addition of Cloudflare Turnstile configuration with feature toggle support
  • Refactoring of authentication and registration to use dedicated request classes
  • Implementation of server-side Turnstile validation with appropriate error handling

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
config/services.php Adds Cloudflare Turnstile configuration (enabled flag, site key, secret key)
phpunit.xml Disables Turnstile for tests to avoid validation during automated testing
app/Helpers/TurnstileHelper.php Implements server-side validation of Turnstile tokens via Cloudflare API
app/Rules/TurnstileRule.php Provides reusable validation rule for Turnstile token verification
app/Http/Requests/LoginRequest.php Extracts login validation logic with conditional Turnstile validation
app/Http/Requests/RegisterRequest.php Extracts registration validation logic with conditional Turnstile validation
app/Http/Requests/LesseeRequest.php Adds Turnstile validation to lessee creation form
app/Http/Controllers/AuthController.php Updates to use LoginRequest for cleaner validation
app/Http/Controllers/Auth/RegisterController.php Refactors to use RegisterRequest and simplifies user creation
resources/views/auth/login.blade.php Adds conditional Turnstile widget display with error handling
resources/views/auth/register.blade.php Adds conditional Turnstile widget display with error handling
resources/views/catalogos/arrendatario/create.blade.php Adds conditional Turnstile widget to lessee creation form
resources/views/layouts/app.blade.php Adds @stack directive for dynamic script injection
resources/views/layouts/layout-v2.blade.php Adds @stack directive and improves user menu with logout button
routes/web.php Adds named route for logout functionality

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

}

return $rules;
}
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The LesseeRequest is missing custom error messages for the Turnstile validation, unlike LoginRequest and RegisterRequest which provide user-friendly Spanish messages. Consider adding a messages() method to provide consistent, localized error messages for the 'cf-turnstile-response.required' validation.

Suggested change
}
}
/**
* Get custom validation messages for this request.
*
* @return array
*/
public function messages()
{
return [
'cf-turnstile-response.required' => 'Por favor, verifica que no eres un robot.',
];
}

Copilot uses AI. Check for mistakes.
Comment on lines +30 to +34
$response = Http::asForm()->post('https://challenges.cloudflare.com/turnstile/v0/siteverify', [
'secret' => $secretKey,
'response' => $token,
'remoteip' => $ip ?? request()->ip(),
]);
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The HTTP request to Cloudflare Turnstile API lacks timeout configuration. If the Cloudflare API is slow or unresponsive, this could cause the authentication/registration process to hang indefinitely. Consider adding a timeout to the HTTP request to prevent blocking users for extended periods.

Suggested change
$response = Http::asForm()->post('https://challenges.cloudflare.com/turnstile/v0/siteverify', [
'secret' => $secretKey,
'response' => $token,
'remoteip' => $ip ?? request()->ip(),
]);
$response = Http::asForm()
->timeout(5)
->post('https://challenges.cloudflare.com/turnstile/v0/siteverify', [
'secret' => $secretKey,
'response' => $token,
'remoteip' => $ip ?? request()->ip(),
]);

Copilot uses AI. Check for mistakes.
Comment on lines +30 to +46
$response = Http::asForm()->post('https://challenges.cloudflare.com/turnstile/v0/siteverify', [
'secret' => $secretKey,
'response' => $token,
'remoteip' => $ip ?? request()->ip(),
]);

$result = $response->json();

if (!$response->successful() || !isset($result['success'])) {
Log::warning('Turnstile validation request failed', [
'status' => $response->status(),
'body' => $response->body(),
]);
return false;
}

return $result['success'] === true;
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error handling for failed Turnstile validation could be improved. When the HTTP request fails or returns an unexpected response, only a generic log warning is created. Consider differentiating between network failures, API errors, and validation failures to provide better debugging information and potentially different user experiences.

Suggested change
$response = Http::asForm()->post('https://challenges.cloudflare.com/turnstile/v0/siteverify', [
'secret' => $secretKey,
'response' => $token,
'remoteip' => $ip ?? request()->ip(),
]);
$result = $response->json();
if (!$response->successful() || !isset($result['success'])) {
Log::warning('Turnstile validation request failed', [
'status' => $response->status(),
'body' => $response->body(),
]);
return false;
}
return $result['success'] === true;
try {
$response = Http::asForm()->post('https://challenges.cloudflare.com/turnstile/v0/siteverify', [
'secret' => $secretKey,
'response' => $token,
'remoteip' => $ip ?? request()->ip(),
]);
} catch (\Exception $e) {
Log::error('Turnstile validation network or client error', [
'message' => $e->getMessage(),
'exception' => get_class($e),
'ip' => $ip ?? request()->ip(),
]);
return false;
}
if (!$response->successful()) {
Log::warning('Turnstile validation API responded with error status', [
'status' => $response->status(),
'body' => $response->body(),
]);
return false;
}
$result = $response->json();
if (!is_array($result) || !array_key_exists('success', $result)) {
Log::warning('Turnstile validation response missing expected "success" field', [
'status' => $response->status(),
'body' => $response->body(),
]);
return false;
}
if ($result['success'] !== true) {
Log::info('Turnstile validation failed', [
'status' => $response->status(),
'error_codes' => $result['error-codes'] ?? null,
]);
return false;
}
return true;

Copilot uses AI. Check for mistakes.

use App\Rules\TurnstileRule;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rules\Password;
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Password validation rule is imported but not used. The password validation currently uses simple string rules ('required', 'string', 'min:8', 'confirmed'). Either remove this unused import or consider using Laravel's Password rule object for more robust password validation (e.g., Password::min(8) with additional requirements).

Copilot uses AI. Check for mistakes.
Comment on lines +22 to +23
Log::warning('Cloudflare Turnstile secret key is not configured');
return false;
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When Turnstile is enabled but the secret key is not configured, the validation silently fails for all users attempting to login, register, or create lessees. This creates a poor user experience as users receive generic validation errors without understanding the root cause. Consider throwing a configuration exception in non-production environments or logging an error-level message (instead of warning) to make configuration issues more visible during development and deployment.

Suggested change
Log::warning('Cloudflare Turnstile secret key is not configured');
return false;
if (app()->environment('production')) {
Log::error('Cloudflare Turnstile secret key is not configured');
return false;
}
throw new \RuntimeException('Cloudflare Turnstile secret key is not configured.');

Copilot uses AI. Check for mistakes.
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the final PR Bugbot will review for you during this billing cycle

Your free Bugbot reviews will reset on February 9

Details

You are on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

@section('javascript')
@if(config('services.cloudflare.turnstile', false))
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
@endif
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turnstile script breaks Vue initialization in lessee form

High Severity

The Turnstile <script src=""> tag is placed inside @section('javascript') which contains raw JavaScript code (Vue initialization). When @yield('javascript') outputs this section in layout-v2.blade.php, the HTML script tag is rendered followed by raw JavaScript that isn't wrapped in <script> tags. This causes the Vue code to be displayed as plain text rather than executed, breaking the form's image preview functionality when Turnstile is enabled. The fix is to use @push('scripts') like the other views (login.blade.php and register.blade.php) do.

Fix in Cursor Fix in Web

];

if (config('services.cloudflare.turnstile', false)) {
$rules['cf-turnstile-response'] = ['required', new TurnstileRule()];
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing string validation causes TypeError crash on array input

Medium Severity

The cf-turnstile-response validation rules are missing a 'string' validator. When a malicious request submits this field as an array (e.g., cf-turnstile-response[]=value), the array passes both the required check and TurnstileRule's empty() check, then gets passed to TurnstileHelper::validate() which has a string $token type hint. This causes a TypeError crash (500 error) instead of returning a proper validation error. Other fields like password correctly include 'string' in their rules.

Additional Locations (2)

Fix in Cursor Fix in Web


if (config('services.cloudflare.turnstile', false)) {
$rules['cf-turnstile-response'] = ['required', new TurnstileRule()];
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Edit form missing Turnstile widget breaks updates

High Severity

The LesseeRequest class is used for both store and update controller methods, and now requires cf-turnstile-response when Turnstile is enabled. However, only create.blade.php was updated with the Turnstile widget—edit.blade.php was not modified. When Turnstile is enabled, any attempt to update a lessee record will fail validation because the edit form doesn't include the required cf-turnstile-response field, effectively breaking all lessee edits.

Fix in Cursor Fix in Web

}

// Validar el token con Cloudflare
if (!TurnstileHelper::validate($value)) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing type validation causes TypeError on array input

Medium Severity

The TurnstileRule::validate() method receives a mixed $value and passes it directly to TurnstileHelper::validate() which expects a string. If a user manipulates the form to submit cf-turnstile-response as an array, the empty() check passes for non-empty arrays, and PHP 8 throws a TypeError when attempting to pass the array to the helper. The validation rules lack a 'string' validator to ensure type safety before the custom rule executes.

Additional Locations (2)

Fix in Cursor Fix in Web

@Punksolid Punksolid merged commit 4ded888 into develop Jan 11, 2026
3 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants