## Security Validation Standards

### FormRequest Security Rules

**MANDATORY: All FormRequests Must Implement Security Validations**

All iPaaS FormRequests (and similar sensitive modules) must follow these security standards to prevent XSS, SQL injection, DoS attacks, and command injection.

#### Security Patterns Class

Use the centralized `SecurityPatterns` class for common regex validations:

```php
// src/App/Validation/SecurityPatterns.php
namespace App\Validation;

class SecurityPatterns
{
    public const HTTPS_URL = '/^https:\/\//';
    public const SANITIZED_STRING = '/^[a-zA-Z0-9\s\-_.,|()\']+$/';
    public const SAFE_PATH = '/^\/[a-zA-Z0-9\/_\-\{\}\.]+$/';
    public const ALPHANUMERIC_STRICT = '/^[a-zA-Z0-9]+$/';
}
```

#### Text Field Validation (XSS Prevention)

All user-provided text fields must use `SecurityPatterns::SANITIZED_STRING`:

```php
use App\Validation\SecurityPatterns;

public function rules(): array
{
    return [
        'name' => [
            'required',
            'string',
            'max:255',
            'regex:' . SecurityPatterns::SANITIZED_STRING  // XSS prevention
        ],
        'description' => [
            'nullable',
            'string',
            'max:1000',
            'regex:' . SecurityPatterns::SANITIZED_STRING
        ],
    ];
}

public function messages(): array
{
    return [
        'name.regex' => 'The name contains invalid characters. Only letters, numbers, spaces, and basic punctuation are allowed.',
        'description.regex' => 'The description contains invalid characters. Only letters, numbers, spaces, and basic punctuation are allowed.',
    ];
}
```

#### JSON Field Validation (DoS Prevention)

Limit JSON configuration fields to prevent JSON bomb attacks:

```php
public function rules(): array
{
    return [
        'request_config' => [
            'required',
            'json',
            'max:51200'  // 50KB limit for JSON config
        ],
        'pagination_config' => [
            'required',
            'json',
            'max:51200'  // 50KB limit
        ],
    ];
}

public function messages(): array
{
    return [
        'request_config.max' => 'The request configuration is too large (maximum 50KB).',
        'pagination_config.max' => 'The pagination configuration is too large (maximum 50KB).',
    ];
}
```

#### Large Text Field Validation

For script/code fields, enforce reasonable size limits:

```php
public function rules(): array
{
    return [
        'script' => [
            'nullable',
            'string',
            'max:512000'  // 500KB limit for script code
        ],
    ];
}

public function messages(): array
{
    return [
        'script.max' => 'The script is too large (maximum 500KB).',
    ];
}
```

#### Whitelist Validation (Command Injection Prevention)

For fields that control application behavior, use strict whitelists:

```php
public function rules(): array
{
    return [
        'request_type' => [
            'required',
            'string',
            'in:request,sftp,download'  // Strict whitelist
        ],
        'condition_operator' => [
            'required',
            'string',
            // Whitelist of allowed condition types for security
            'in:equals,not_equals,contains,not_contains,greater_than,less_than,greater_than_or_equal,less_than_or_equal,is_empty,is_not_empty,starts_with,ends_with,regex_match'
        ],
    ];
}

public function messages(): array
{
    return [
        'condition_operator.in' => 'Invalid condition type. Allowed types: equals, not_equals, contains, not_contains, greater_than, less_than, greater_than_or_equal, less_than_or_equal, is_empty, is_not_empty, starts_with, ends_with, regex_match.',
    ];
}
```

#### Array Size Limits (DoS Prevention)

Limit nested array sizes to prevent resource exhaustion:

```php
public function rules(): array
{
    return [
        'branches' => [
            'required',
            'array',
            'min:1',
            'max:50'  // Reasonable limit for branches
        ],
        'branches.*.conditions' => [
            'required',
            'array',
            'max:100'  // Reasonable limit for conditions per branch
        ],
        'branches.*.conditions.*.value' => [
            'nullable',
            'string',
            'max:1000'  // Limit condition values to prevent abuse
        ],
    ];
}

public function messages(): array
{
    return [
        'branches.max' => 'Cannot create more than 50 branches.',
        'branches.*.conditions.max' => 'Cannot create more than 100 conditions per branch.',
        'branches.*.conditions.*.value.max' => 'Condition value is too long (maximum 1000 characters).',
    ];
}
```

#### URL Validation (HTTPS Enforcement)

For external API URLs, enforce HTTPS:

```php
public function rules(): array
{
    return [
        'base_url' => [
            'required',
            'url',
            'max:2048',
            'regex:' . SecurityPatterns::HTTPS_URL  // HTTPS only
        ],
    ];
}

public function messages(): array
{
    return [
        'base_url.regex' => 'The base URL must use HTTPS for security.',
    ];
}
```

#### Security Testing Requirements

All security validations MUST have corresponding tests:

```php
// tests/Feature/Http/Controllers/Ipaas/ApiNodeControllerTest.php

test('rejects XSS attempts in name field', function () {
    $data = [
        'name' => '<script>alert("XSS")</script>',
        // ... other fields
    ];
    
    $response = $this->postTenantJson('/ipaas/api-node/store', $data);
    
    $response->assertStatus(422)
        ->assertJsonValidationErrors(['name']);
});

test('rejects excessively large JSON config', function () {
    $largeJson = json_encode(array_fill(0, 10000, str_repeat('a', 100)));
    
    $data = [
        'name' => 'Test Node',
        'request_config' => $largeJson,  // > 50KB
        // ... other fields
    ];
    
    $response = $this->postTenantJson('/ipaas/api-node/store', $data);
    
    $response->assertStatus(422)
        ->assertJsonValidationErrors(['request_config']);
});

test('rejects invalid condition types', function () {
    $data = [
        'condition_type' => 'drop_table',  // Invalid condition
        // ... other fields
    ];
    
    $response = $this->postTenantJson('/ipaas/branch-node/store', $data);
    
    $response->assertStatus(422)
        ->assertJsonValidationErrors(['condition_type']);
});
```

#### Security Validation Checklist

For ALL new FormRequests in sensitive modules:

- [ ] Text fields use `SecurityPatterns::SANITIZED_STRING`
- [ ] URLs enforce HTTPS with `SecurityPatterns::HTTPS_URL`
- [ ] JSON fields have size limits (typically 50KB)
- [ ] Script/code fields have size limits (typically 500KB)
- [ ] Enum-like fields use strict whitelists (`in:value1,value2`)
- [ ] Array fields have maximum size limits
- [ ] Nested array fields have maximum depth/size limits
- [ ] Custom error messages explain security restrictions
- [ ] Security tests cover XSS, large payloads, invalid values
- [ ] FormRequest logging includes security context

---

### BUG-L: HTTP Input Empty-String Normalization

**Rule:** When reading optional string inputs from an HTTP request that will be forwarded to a downstream service or API (e.g., `folder_id`, `parent_id`, a provider-specific identifier), you MUST use the falsy-coalescing operator `?:` — NOT the null-coalescing operator `??`.

**Root cause of BUG-L:** HTML hidden or select form fields submit an empty string `''` when no value is selected. The PHP null-coalescing operator `??` only replaces `null`, not `''`. So `$request->input('folder_id') ?? null` yields `''` when the field is blank — and that empty string propagates into provider API calls, producing malformed requests (e.g., Microsoft Graph URL `items/:/filename:/content` instead of `items/root:/filename:/content`).

**❌ BAD — `??` does not catch empty string:**
```php
$folderId = $request->input('folder_id') ?? null;  // '' passes through unchecked
$folderId = $request['folder_id'] ?? 'root';        // same bug
```

**✅ GOOD — `?:` normalizes both `null` and `''` to `null`:**
```php
$folderId = $request->input('folder_id') ?: null;
$folderId = $request['folder_id'] ?: null;
```

**Checklist for any optional string read from HTTP input:**
- [ ] Uses `?:` (Elvis / falsy-coalescing), not `??` (null-coalescing)
- [ ] Downstream service method signature accepts `?string $param = null`
- [ ] Regression test confirms `folder_id = ''` normalizes to `null`

**Why this matters:** Provider method signatures declare `?string $folderId = null`. A provider that checks `$folderId !== null` will treat `''` as a valid folder identifier, forwarding it verbatim to the cloud API and producing malformed endpoint paths.

---

#### Why These Rules Exist

User-controlled text fields without character-class restrictions are XSS vectors. Unbounded JSON fields are JSON bomb / DoS vectors. Open-ended string fields used as control flow operators (condition types, request types) are command injection vectors. Unconstrained nested arrays exhaust memory and CPU.

**The general principle:** Every user-provided input field in a FormRequest must be classified into one of these categories and validated accordingly:

| Field category | Attack surface | Required validation |
|---|---|---|
| Free text (names, labels) | XSS | `regex:SANITIZED_STRING`, `max:255` |
| JSON blobs (config, rules) | DoS / JSON bomb | `json`, `max:51200` |
| Code / script fields | DoS | `string`, `max:512000` |
| Enum-like control fields | Command injection | `in:value1,value2,...` |
| External URLs | Mixed content | `url`, `regex:HTTPS_URL` |
| Nested arrays | Resource exhaustion | `array`, `max:N` on each level |

A FormRequest that lacks size limits or character-class constraints on any of these categories is incomplete, regardless of whether the module has been audited before.

---

