# iPaaS Form Security Audit & Refactoring Plan

**Version:** 1.0
**Created:** 2025-11-03
**Status:** Planning Phase

---

## Executive Summary

This document outlines a comprehensive security audit and refactoring plan for all iPaaS form handling to eliminate injection vulnerabilities, mass-assignment risks, and validation bypasses. The plan leverages Laravel's built-in security features and standardized request validation patterns.

### Security Risks Identified

1. **Mass Assignment Vulnerabilities**: Direct use of `$request->all()` bypassing model protections
2. **Validation Bypasses**: Validation logic in Action classes instead of at the controller/request layer
3. **SQL Injection Risk**: Unvalidated input in database queries
4. **JSON Injection**: Unvalidated JSON payloads stored in `request_config`, `pagination_config`
5. **Authorization Gaps**: No explicit authorization checks in Form Request classes
6. **Type Confusion**: Mixed validation patterns across different controllers

---

## Objective

Audit and refactor all iPaaS form handling to eliminate injection, mass-assignment, and validation risks using Laravel's built-in security features and standardized request validation patterns.

---

## Phase 1 – Immediate Security Fixes

### Overview

Focus on the **highest-risk forms** that handle external API credentials, configuration data, and executable code. These modules are primary attack vectors and require immediate hardening.

---

### 1.1 Create Form Request Classes for High-Risk Forms

#### Affected Modules

**Priority 1 - Critical Security Risk:**

| Module | Files | Risk Level | Justification |
|--------|-------|------------|---------------|
| **Connector Management** | `ConnectorController.php`<br>`ConnectorForm.php` (Livewire)<br>`CreateConnector.php` (Action)<br>`UpdateConnector.php` (Action) | 🔴 **CRITICAL** | Handles OAuth tokens, API keys, private keys, base URLs. Direct credential storage. Mass assignment via `$request->all()` on lines 57, 102. |
| **API Node Management** | `ApiNodeController.php`<br>`ApiNodeForm.php` (Livewire)<br>`CreateApiNode.php` (Action)<br>`UpdateApiNode.php` (Action) | 🔴 **CRITICAL** | Processes API requests, stores JSON payloads, pagination configs. JSON injection risk in `request_config`. Mass assignment via `$request->all()` on lines 39, 59. |
| **Transform Node** | `TransformNodeController.php`<br>`CreateTransformNode.php`<br>`UpdateTransformNode.php` | 🟠 **HIGH** | Executes custom transformation scripts. Code injection risk. Mass assignment via `$request->all()` on lines 27, 52. |
| **Map Node** | `MapNodeController.php`<br>`MapNodeForm.php` (Livewire)<br>`CreateMapNode.php`<br>`UpdateMapNode.php` | 🟡 **MEDIUM** | Field mapping configurations. Schema manipulation risk. |

**Current Security Gaps:**

```php
// ❌ CURRENT VULNERABLE PATTERN - ConnectorController.php:57
public static function store(Request $request)
{
    $item = $request->all(); // No validation, no authorization
    $connector = CreateConnector::create($item);
    // ...
}

// ❌ CURRENT VULNERABLE PATTERN - ApiNodeController.php:39
public function store(Request $request)
{
    $item = $request->all(); // Direct mass assignment
    return CreateApiNode::create($item);
}
```

#### Actions Required

**Step 1: Create Dedicated Form Request Classes**

Create the following Form Request classes in `src/App/Http/Requests/Ipaas/`:

```
src/App/Http/Requests/Ipaas/
├── Connector/
│   ├── StoreConnectorRequest.php
│   ├── UpdateConnectorRequest.php
│   └── DeleteConnectorRequest.php
├── Node/
│   ├── StoreApiNodeRequest.php
│   ├── UpdateApiNodeRequest.php
│   ├── StoreTransformNodeRequest.php
│   ├── UpdateTransformNodeRequest.php
│   ├── StoreMapNodeRequest.php
│   └── UpdateMapNodeRequest.php
└── Flow/
    ├── StoreFlowRequest.php
    └── UpdateFlowRequest.php
```

**Step 2: Implement Validation Rules Pattern**

Each Form Request must include:

1. **Field-level validation** using Laravel's built-in validation rules (preferred)
2. **Authorization logic** in `authorize()` method
3. **Custom error messages** in `messages()` method
4. **After-validation hooks** for complex cross-field validation
5. **Security logging** for failed authorization and validation

**Validation Philosophy:**
- **Use Laravel's built-in rules for 95% of validation** (standard, well-documented, maintained)
- **Use SecurityPatterns constants** for commonly-used regex patterns (see Appendix C)
- **Use validation closures** for complex, context-specific logic

**Generic Form Request Implementation Pattern**

```php
namespace App\Http\Requests\Ipaas\[Module];

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
use App\Validation\SecurityPatterns;

class Store[Resource]Request extends FormRequest
{
    public function authorize(): bool
    {
        // Check tenant-level permissions using Laravel policies
        return $this->user()->can('create', [ResourceModel]::class);
    }

    public function rules(): array
    {
        return [
            // String fields - Laravel built-in + SecurityPatterns constant (see Appendix C)
            'name' => [
                'required',
                'string',
                'max:255',
                'regex:' . SecurityPatterns::SANITIZED_STRING
            ],

            // URLs - Laravel built-in + SecurityPatterns constant (see Appendix C)
            'base_url' => [
                'required',
                'url',
                'max:2048',
                'regex:' . SecurityPatterns::HTTPS_URL
            ],

            // Foreign keys - Laravel's exists rule
            'related_id' => ['required', 'integer', 'exists:related_table,id'],

            // Enum fields - Laravel's Rule::in()
            'type' => ['required', 'string', Rule::in(['type1', 'type2', 'type3'])],

            // JSON with structure validation - use closures for context-specific needs
            'json_config' => [
                'required',
                'json',
                function ($attribute, $value, $fail) {
                    $decoded = json_decode($value, true);

                    if (json_last_error() !== JSON_ERROR_NONE) {
                        $fail("The {$attribute} must be valid JSON.");
                        return;
                    }

                    // Validate required nested fields
                    if (!isset($decoded['required_field'])) {
                        $fail("The {$attribute} must include required_field.");
                    }

                    // Validate nested field formats (e.g., URLs, paths)
                    if (isset($decoded['endpoint'])) {
                        if (!filter_var($decoded['endpoint'], FILTER_VALIDATE_URL)) {
                            if (!preg_match(SecurityPatterns::SAFE_PATH, $decoded['endpoint'])) {
                                $fail("The {$attribute} endpoint must be a valid URL or path.");
                            }
                        }
                    }
                },
            ],

            // Conditional validation - Laravel's required_if
            'oauth_client_id' => ['required_if:auth_type,oauth', 'string', 'max:255'],
            'oauth_secret' => ['required_if:auth_type,oauth', 'string', 'max:500'],
        ];
    }

    public function messages(): array
    {
        return [
            'name.required' => 'The resource name is required.',
            'name.regex' => 'The name can only contain letters, numbers, spaces, hyphens, and underscores.',
            'base_url.regex' => 'The URL must use HTTPS protocol for security.',
            // Add clear, user-friendly error messages for all validation rules
        ];
    }

    public function withValidator($validator)
    {
        $validator->after(function ($validator) {
            // Cross-field validation logic
            // Check for conflicting field combinations
            // Example: Ensure OAuth fields aren't mixed with Basic auth fields
            if ($this->has('oauth_client_id') && $this->has('basic_username')) {
                $validator->errors()->add('auth_type', 'Cannot mix OAuth and Basic authentication.');
            }
        });
    }

    protected function failedAuthorization()
    {
        Log::warning('Unauthorized [resource] creation attempt', [
            'user_id' => $this->user()->id,
            'ip_address' => $this->ip(),
            'user_agent' => $this->userAgent(),
        ]);

        parent::failedAuthorization();
    }

    protected function failedValidation(\Illuminate\Contracts\Validation\Validator $validator)
    {
        Log::error('[Resource] validation failed', [
            'user_id' => $this->user()->id,
            'errors' => $validator->errors()->toArray(),
            'input_fields' => array_keys($this->all()), // Log field names, not values
        ]);

        parent::failedValidation($validator);
    }
}
```

**Key Validation Patterns:**

1. **String Fields**: Laravel's `string` + `max` + SecurityPatterns regex constant
2. **URLs**: Laravel's `url` + SecurityPatterns HTTPS enforcement constant
3. **Foreign Keys**: Laravel's `exists` rule for referential integrity
4. **Enums**: Laravel's `Rule::in()` for allowed value lists
5. **JSON Structure**: Laravel's `json` rule + validation closures for required fields
6. **Conditional Fields**: Laravel's `required_if` for dependent field validation
7. **Security Logging**: Log failures with context (no sensitive data)

**When to Use What:**
- **Laravel built-in rules**: 95% of validation (use first)
- **SecurityPatterns constants**: Frequently-used regex patterns (see Appendix C)
- **Validation closures**: Complex JSON structure validation, nested field checks

**Step 3: Update Controllers**

Replace `$request->all()` calls with `$request->validated()` in all affected controllers:

```php
// ❌ BEFORE (Vulnerable)
public function store(Request $request)
{
    $item = $request->all(); // Mass assignment risk
    return CreateResource::create($item);
}

// ✅ AFTER (Secured)
public function store(StoreResourceRequest $request)
{
    $validated = $request->validated(); // Only validated fields
    return CreateResource::create($validated);
}
```

**Step 4: Update Livewire Forms**

Livewire components should leverage Form Request validation rules:

```php
// Livewire Component
protected function rules()
{
    return (new StoreResourceRequest())->rules();
}

public function submit()
{
    $validated = $this->validate();

    // Authorization check
    if (!auth()->user()->can('create', ResourceModel::class)) {
        abort(403);
    }

    $response = CreateResource::create($validated);
    // Handle response...
}
```

---

### 1.2 Acceptance Criteria

**Implementation is complete when all of the following criteria are met:**

#### Security Criteria

- [ ] **No `$request->all()` calls remain** in any iPaaS controller or Livewire form
- [ ] **All user input is validated** before reaching Action classes
- [ ] **All Form Requests implement `authorize()` method** with proper permission checks
- [ ] **JSON fields are validated for structure** and required nested fields
- [ ] **URL fields enforce HTTPS** for external API connections
- [ ] **Conditional validation works correctly** (e.g., OAuth fields required only for OAuth auth type)
- [ ] **Rate limiting is implemented** for sensitive operations (connector creation, node execution)

#### Functional Criteria

- [ ] **Validation errors return structured 422 JSON responses** for API endpoints
- [ ] **Validation errors flash to session** and return with `withInput()` for web forms
- [ ] **Custom error messages are clear** and user-friendly
- [ ] **Authorization failures are logged** with user ID, IP address, and timestamp
- [ ] **Validation failures are logged** with sanitized input field names (not values)

#### Testing Criteria

- [ ] **Unit tests confirm invalid submissions are rejected** for each Form Request
- [ ] **Feature tests confirm authorized users can submit** valid requests
- [ ] **Feature tests confirm unauthorized users cannot submit** requests (403 responses)
- [ ] **Integration tests confirm end-to-end flow** (form → validation → action → database)
- [ ] **Regression tests confirm existing functionality** still works after refactoring

#### Code Quality Criteria

- [ ] **PHPStan level 5 passes** on all modified files
- [ ] **No PHPStan errors introduced** by refactoring
- [ ] **Code follows Laravel best practices** (Form Requests, validation rules, authorization)
- [ ] **All public methods have docblocks** with parameter and return types
- [ ] **Logging is consistent** across all controllers and Form Requests

---

### 1.3 Testing Requirements

**Location:** `tests/Unit/Http/Requests/Ipaas/`

#### Unit Tests for Form Request Validation Rules

**Test Structure (using Pest):**
```php
describe('[ResourceName] Form Request Validation', function () {
    beforeEach(function () {
        // Setup test database using file-based SQLite (see ai_tests.md)
        // Create required tables for foreign key validation
    });

    // Test individual validation rules
    // Test conditional validation logic
    // Test custom validation closures
    // Test authorization logic
});
```

**Required Assertions:**

1. **Required Field Validation**
   - Assert validation rules include 'required' for mandatory fields
   - Submit request without required fields → expect 422 status
   - Submit request with required fields → expect success

2. **Format Validation**
   - Submit HTTP URL when HTTPS required → expect validation error on 'base_url'
   - Submit invalid regex pattern → expect validation error with custom message
   - Submit string exceeding max length → expect validation error

3. **Foreign Key Validation**
   - Submit non-existent foreign key ID → expect validation error with 'exists' rule failure
   - Submit valid foreign key ID → expect validation success

4. **Conditional Validation**
   - When auth_type = 'oauth', OAuth fields are required → assert 'required_if' rules present
   - Submit OAuth auth without client_id → expect validation error
   - Submit Basic auth without username → expect validation error

5. **Authorization Tests**
   - Unauthorized user submits request → expect 403 status
   - Log entry created with user_id, ip_address, user_agent
   - Authorized user submits request → expect success

6. **JSON Structure Validation**
   - Submit JSON missing required nested field → expect validation error from closure
   - Submit valid JSON with all required fields → expect validation success
   - Submit malformed JSON → expect validation error

7. **Cross-Field Validation**
   - Submit conflicting auth credentials → expect validation error from `withValidator()` hook
   - Submit incomplete throttling config → expect validation error for missing dependent field

**Testing Best Practices:**

- Use file-based SQLite for debugging (see `ai_tests.md` Quick Reference)
- Spy on `Log` facade to verify security logging
- Use `RefreshDatabase` trait at file level
- Test both positive and negative cases
- Verify error messages are user-friendly
- Assert database state matches expectations

**Example Test Pattern:**

```php
// tests/Unit/Http/Requests/Ipaas/Connector/StoreConnectorRequestTest.php
describe('StoreConnectorRequest Validation', function () {
    beforeEach(function () {
        // File-based SQLite setup
        $testDatabase = storage_path('framework/testing/test_' . uniqid() . '.sqlite');
        touch($testDatabase);

        config(['database.connections.tenant_connection' => [
            'driver' => 'sqlite',
            'database' => $testDatabase,
        ]]);

        DB::purge('tenant_connection');
        DB::reconnect('tenant_connection');

        // Create required tables
        Schema::connection('tenant_connection')->create('config_types', function ($table) {
            $table->id();
            $table->string('name');
        });

        Log::spy();
    });

    it('requires name field', function () {
        $request = new StoreConnectorRequest();
        $rules = $request->rules();

        expect($rules)->toHaveKey('name');
        expect($rules['name'])->toContain('required');
    });

    it('enforces HTTPS on base_url', function () {
        $request = new StoreConnectorRequest();
        $rules = $request->rules();

        expect($rules['base_url'])->toContain('url');
        // Find regex rule that enforces HTTPS
        $hasHttpsRegex = collect($rules['base_url'])->contains(function ($rule) {
            return is_string($rule) && str_contains($rule, 'regex:') && str_contains($rule, 'https');
        });

        expect($hasHttpsRegex)->toBeTrue();
    });

    it('validates conditional OAuth fields', function () {
        $request = new StoreConnectorRequest();
        $rules = $request->rules();

        expect($rules['client_id'])->toContain('required_if:auth_type,oauth');
        expect($rules['client_secret'])->toContain('required_if:auth_type,oauth');
    });
});
```

---

### 1.4 Risk Analysis

| Risk | Likelihood | Impact | Mitigation |
|------|------------|--------|------------|
| **Breaking existing integrations** | Medium | High | Comprehensive regression testing, staged rollout |
| **Over-restrictive validation** | Medium | Medium | Review validation rules with product team, add bypass for admin users |
| **Performance degradation** | Low | Medium | Benchmark validation overhead, optimize complex rules |
| **Authorization logic errors** | Low | Critical | Thorough testing of authorization checks, manual QA review |
| **JSON schema validation complexity** | High | Low | Use json-schema validation libraries, extensive test coverage |

---

## Appendix A: Current Vulnerable Patterns

**All instances of `$request->all()` in iPaaS domain:**

1. `src/App/Http/Controllers/Ipaas/ConnectorController.php:57`
2. `src/App/Http/Controllers/Ipaas/ConnectorController.php:102`
3. `src/App/Http/Controllers/Ipaas/Nodes/ApiNodeController.php:39`
4. `src/App/Http/Controllers/Ipaas/Nodes/ApiNodeController.php:59`
5. `src/App/Http/Controllers/Ipaas/Nodes/TransformNodeController.php:27`
6. `src/App/Http/Controllers/Ipaas/Nodes/TransformNodeController.php:52`
7. `src/App/Http/Controllers/Ipaas/Nodes/TransformNodeController.php:58`
8. `src/App/Http/Controllers/Ipaas/Nodes/MapNodeController.php` (multiple locations)
9. `src/App/Http/Controllers/Ipaas/flows/FlowsController.php` (multiple locations)

**All must be replaced with `$request->validated()` after Form Request implementation.**

---

## Appendix B: Reference Implementations

**Existing secure patterns in codebase:**

1. `src/App/Http/Requests/ImportRequest.php` - Good example of validation rules and error messages
2. `src/App/Http/Requests/ConfigureMicrosoftRequest.php` - Good example of conditional validation
3. `src/App/Http/Requests/ConfigureGoogleRequest.php` - Good example of file upload validation

**Study these patterns and adapt for iPaaS security requirements.**

---

## Appendix C: Validation Patterns and Security Rules

### Philosophy: Laravel First, Minimal Custom Code

**Use Laravel's built-in validation for 95% of validation needs.** Only create custom components when they provide clear, measurable value.

### Standard Laravel Validation Patterns (Use These First)

Use these Laravel built-in patterns directly in Form Requests:

```php
// String validation
'name' => ['required', 'string', 'max:255'],

// URL validation with HTTPS enforcement
'base_url' => ['required', 'url', 'max:2048', 'regex:/^https:\/\//'],

// Foreign key validation
'connector_id' => ['required', 'integer', 'exists:connectors,id'],

// Enum validation
'type' => ['required', 'string', Rule::in(['request', 'sftp', 'download'])],

// Conditional validation
'client_id' => ['required_if:auth_type,oauth', 'string', 'max:255'],

// Numeric ranges
'throttle_max_requests' => ['nullable', 'integer', 'min:1', 'max:1000'],

// JSON validation with structure checking
'config' => [
    'required',
    'json',
    function ($attribute, $value, $fail) {
        $decoded = json_decode($value, true);
        if (json_last_error() !== JSON_ERROR_NONE) {
            $fail("The {$attribute} must be valid JSON.");
            return;
        }

        // Validate required fields
        if (!isset($decoded['required_field'])) {
            $fail("The {$attribute} must include required_field.");
        }
    },
],
```

---

### SecurityPatterns Class (Regex Constants)

**Purpose:** Centralize frequently-used regex patterns to ensure consistency and ease maintenance.

**File:** `src/App/Validation/SecurityPatterns.php`

```php
<?php

namespace App\Validation;

/**
 * Security validation patterns for iPaaS form handling.
 *
 * Provides regex constants for commonly-validated fields across the application.
 * Use these constants instead of duplicating regex patterns in multiple Form Requests.
 *
 * @see docs/security/ipaas-form-security-audit-plan.md
 */
class SecurityPatterns
{
    /**
     * HTTPS URL pattern - enforces HTTPS protocol for all external URLs.
     *
     * Usage:
     * 'base_url' => ['required', 'url', 'regex:' . SecurityPatterns::HTTPS_URL]
     */
    public const HTTPS_URL = '/^https:\/\//';

    /**
     * Sanitized string pattern - allows alphanumeric, spaces, hyphens, underscores.
     * Prevents special characters that could be used for injection attacks.
     *
     * Usage:
     * 'name' => ['required', 'string', 'regex:' . SecurityPatterns::SANITIZED_STRING]
     */
    public const SANITIZED_STRING = '/^[a-zA-Z0-9\s\-_]+$/';

    /**
     * Safe path pattern - validates relative paths and API endpoints.
     * Allows forward slashes, alphanumeric, hyphens, underscores, dots, and curly braces (for path params).
     *
     * Usage:
     * 'endpoint' => ['required', 'string', 'regex:' . SecurityPatterns::SAFE_PATH]
     */
    public const SAFE_PATH = '/^\/[a-zA-Z0-9\/_\-\{\}\.]+$/';

    /**
     * Alphanumeric identifier pattern - strict alphanumeric only (no spaces).
     * Used for IDs, keys, and identifiers that should not contain special characters.
     *
     * Usage:
     * 'api_key' => ['required', 'string', 'regex:' . SecurityPatterns::ALPHANUMERIC_STRICT]
     */
    public const ALPHANUMERIC_STRICT = '/^[a-zA-Z0-9]+$/';
}
```

**When to Use:**
- Use these constants when the same regex pattern appears in multiple Form Requests (5+ uses)
- Ensures consistency across the application
- Single point of update if pattern requirements change

**When NOT to Use:**
- One-off regex patterns used in a single Form Request (inline is fine)
- Simple patterns that are self-explanatory without a constant

---

### Usage Examples

**Example 1: Connector Form Request (using SecurityPatterns)**

```php
use App\Validation\SecurityPatterns;
use Illuminate\Validation\Rule;

public function rules(): array
{
    return [
        'name' => [
            'required',
            'string',
            'max:255',
            'regex:' . SecurityPatterns::SANITIZED_STRING
        ],
        'base_url' => [
            'required',
            'url',
            'max:2048',
            'regex:' . SecurityPatterns::HTTPS_URL
        ],
        'auth_type' => [
            'required',
            'integer',
            'exists:config_types,id'
        ],
    ];
}
```

**Example 2: API Node Form Request (JSON validation with closure)**

```php
use App\Validation\SecurityPatterns;

public function rules(): array
{
    return [
        'name' => [
            'required',
            'string',
            'max:255',
            'regex:' . SecurityPatterns::SANITIZED_STRING
        ],
        'request_config' => [
            'required',
            'json',
            function ($attribute, $value, $fail) {
                $decoded = json_decode($value, true);

                if (json_last_error() !== JSON_ERROR_NONE) {
                    $fail("The {$attribute} must be valid JSON.");
                    return;
                }

                // Validate required fields
                if (!isset($decoded['connector_id'])) {
                    $fail("The {$attribute} must include a connector_id.");
                }

                if (!isset($decoded['endpoint'])) {
                    $fail("The {$attribute} must include an endpoint.");
                }

                // Validate endpoint format
                if (isset($decoded['endpoint'])) {
                    if (!filter_var($decoded['endpoint'], FILTER_VALIDATE_URL)) {
                        // Check if it's a relative path
                        if (!preg_match(SecurityPatterns::SAFE_PATH, $decoded['endpoint'])) {
                            $fail("The endpoint must be a valid URL or relative path.");
                        }
                    }
                }
            },
        ],
    ];
}
```

**Example 3: Complex Validation (combining all patterns)**

```php
use App\Validation\SecurityPatterns;
use Illuminate\Validation\Rule;

public function rules(): array
{
    return [
        // SecurityPatterns constant
        'name' => [
            'required',
            'string',
            'max:255',
            'regex:' . SecurityPatterns::SANITIZED_STRING
        ],

        // Laravel built-in with SecurityPatterns
        'base_url' => [
            'required',
            'url',
            'max:2048',
            'regex:' . SecurityPatterns::HTTPS_URL
        ],

        // Laravel built-in foreign key
        'auth_type' => [
            'required',
            'integer',
            'exists:config_types,id'
        ],

        // Laravel Rule::in()
        'request_type' => [
            'required',
            'string',
            Rule::in(['request', 'sftp', 'download'])
        ],

        // JSON with closure validation
        'pagination_config' => [
            'nullable',
            'json',
            function ($attribute, $value, $fail) {
                if (empty($value)) return;

                $decoded = json_decode($value, true);

                if (json_last_error() !== JSON_ERROR_NONE) {
                    $fail("The {$attribute} must be valid JSON.");
                    return;
                }

                if (!isset($decoded['type'])) {
                    $fail("The {$attribute} must include a type.");
                    return;
                }

                $validTypes = ['generic', 'token', 'graphql', 'none'];
                if (!in_array($decoded['type'], $validTypes)) {
                    $fail("The {$attribute} type must be one of: " . implode(', ', $validTypes));
                }
            },
        ],

        // Laravel conditional validation
        'client_id' => [
            'required_if:auth_type,oauth',
            'string',
            'max:255'
        ],
    ];
}
```

---

### Testing Validation Patterns

**SecurityPatterns** (no tests needed - just constants)

**Form Request Validation Tests:**

**File:** `tests/Unit/Http/Requests/Ipaas/Connector/StoreConnectorRequestTest.php`

```php
describe('StoreConnectorRequest Validation', function () {
    it('enforces HTTPS URLs using SecurityPatterns constant', function () {
        $request = new StoreConnectorRequest();
        $rules = $request->rules();

        expect($rules['base_url'])->toContain('url');

        // Verify HTTPS regex is present
        $hasHttpsRegex = collect($rules['base_url'])->contains(function ($rule) {
            return is_string($rule) &&
                   str_contains($rule, 'regex:') &&
                   str_contains($rule, SecurityPatterns::HTTPS_URL);
        });

        expect($hasHttpsRegex)->toBeTrue();
    });

    it('enforces sanitized strings using SecurityPatterns constant', function () {
        $request = new StoreConnectorRequest();
        $rules = $request->rules();

        $hasRegex = collect($rules['name'])->contains(function ($rule) {
            return is_string($rule) && str_contains($rule, SecurityPatterns::SANITIZED_STRING);
        });

        expect($hasRegex)->toBeTrue();
    });
});
```

---

### Maintenance Guidelines

**When to Update SecurityPatterns:**
- Security team changes HTTPS requirements (e.g., TLS 1.3 enforcement)
- New special characters need to be blocked in string inputs
- New path validation requirements emerge
- Multiple Form Requests need the same new pattern (5+ uses)

**Review Schedule:**
- Quarterly security review of validation patterns
- Update after any security incident related to input validation
- Monitor OWASP guidelines for new attack patterns

**Adding New Patterns:**
Only add constants if:
1. Pattern is used in 5+ Form Requests
2. Pattern has security implications
3. Pattern is complex enough to justify centralization

