# Form Validation Security Policy

**Version:** 1.0
**Effective Date:** 2025-11-03
**Status:** Active
**Owner:** Development Team

---

## Table of Contents

1. [Purpose](#purpose)
2. [Scope](#scope)
3. [Policy Statement](#policy-statement)
4. [Mandatory Requirements](#mandatory-requirements)
5. [Laravel-First Validation Philosophy](#laravel-first-validation-philosophy)
6. [SecurityPatterns Usage](#securitypatterns-usage)
7. [Authorization Requirements](#authorization-requirements)
8. [Testing Requirements](#testing-requirements)
9. [Anti-Patterns and Prohibited Practices](#anti-patterns-and-prohibited-practices)
10. [Decision Framework](#decision-framework)
11. [Code Review Checklist](#code-review-checklist)
12. [Exceptions and Waivers](#exceptions-and-waivers)
13. [Related Documentation](#related-documentation)

---

## Purpose

This policy establishes mandatory standards for form validation and input handling across the SuiteX application to:

1. **Eliminate security vulnerabilities**: Mass assignment, injection attacks, authorization bypasses
2. **Ensure consistency**: Standardized validation patterns across all modules
3. **Leverage Laravel best practices**: Use framework features instead of custom solutions
4. **Maintain multi-tenant security**: Protect tenant isolation at the input layer

This policy applies to all new code and must be implemented when refactoring existing code.

---

## Scope

**This policy applies to:**
- All HTTP controller methods that accept user input
- All Livewire component methods that process form submissions
- All API endpoints that receive external data
- All modules handling sensitive data (credentials, configuration, financial data)

**This policy does NOT apply to:**
- Internal service-to-service communication using trusted data
- Automated system processes with no user input
- Read-only operations (GET requests with no input processing)

---

## Policy Statement

**All user input MUST be validated using Laravel Form Request classes before being processed by application logic.**

**Rationale:**
- Multi-tenant SaaS architecture requires strict input validation to prevent tenant isolation breaches
- Application handles sensitive credentials (OAuth tokens, JWT keys, API secrets)
- Mass assignment vulnerabilities can be exploited to escalate privileges or bypass authorization
- Validation at the controller layer provides defense-in-depth security

---

## Mandatory Requirements

### 1. Form Request Classes (REQUIRED)

**Policy:** All controller methods that accept user input MUST use Form Request classes.

**Compliant:**
```php
public function store(StoreConnectorRequest $request)
{
    $validated = $request->validated();
    return CreateConnector::create($validated);
}
```

**Non-Compliant:**
```php
// ❌ PROHIBITED
public function store(Request $request)
{
    $item = $request->all();
    return CreateConnector::create($item);
}
```

**Enforcement:**
- Code reviews MUST reject any use of `$request->all()`
- PHPStan rules SHOULD be configured to detect `$request->all()` usage
- Pull requests MUST include Form Request classes for new endpoints

---

### 2. Validated Data Only (REQUIRED)

**Policy:** Controllers MUST only use `$request->validated()` - never `$request->all()` or `$request->input()` for mass operations.

**Rationale:**
- `$request->validated()` returns only fields that passed validation rules
- Prevents attackers from injecting arbitrary fields (e.g., `is_admin`, `tenant_id`)
- Protects against mass assignment vulnerabilities

**Compliant:**
```php
public function store(StoreResourceRequest $request)
{
    $validated = $request->validated();

    // Safe - only validated fields are present
    $resource = Resource::create($validated);
}
```

**Non-Compliant:**
```php
// ❌ PROHIBITED - Bypasses validation
$resource = Resource::create($request->all());

// ❌ PROHIBITED - Bypasses validation
$resource = Resource::create($request->only(['name', 'email', 'is_admin']));
```

---

### 3. Authorization in Form Requests (REQUIRED)

**Policy:** All Form Requests MUST implement the `authorize()` method with explicit permission checks.

**Compliant:**
```php
public function authorize(): bool
{
    return $this->user()->can('create', Connector::class);
}
```

**Non-Compliant:**
```php
// ❌ PROHIBITED - Always returns true without checking permissions
public function authorize(): bool
{
    return true;
}
```

**Rationale:**
- Defense-in-depth: Authorization checks before validation
- Clear separation of concerns: permissions in Form Request, business logic in actions
- Prevents unauthorized users from even attempting operations

---

### 4. Security Logging (REQUIRED)

**Policy:** All Form Requests MUST log authorization failures and validation failures with context.

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

    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()), // Field names only, no values
        'resource' => static::class,
    ]);

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

**Critical:** Never log sensitive field values (passwords, tokens, secrets).

---

## Laravel-First Validation Philosophy

### Principle: Use Laravel's Built-In Features

**95% of validation should use Laravel's standard validation rules.**

**Standard Validation Patterns:**

```php
public function rules(): array
{
    return [
        // String validation
        'name' => ['required', 'string', 'max:255'],

        // URL validation
        'url' => ['required', 'url', 'max:2048'],

        // Foreign key validation
        'parent_id' => ['required', 'integer', 'exists:parents,id'],

        // Enum validation
        'status' => ['required', 'string', Rule::in(['active', 'inactive'])],

        // Conditional validation
        'field' => ['required_if:other_field,value', 'string'],

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

        // JSON validation
        'config' => [
            'required',
            'json',
            function ($attribute, $value, $fail) {
                // Context-specific validation
            },
        ],
    ];
}
```

### When to Use Custom Validation

**Only create custom validation rules when:**
1. Laravel doesn't provide the functionality
2. The logic is complex and used in 5+ places
3. The rule has security implications requiring isolation and testing

**Example: Custom Rule Justified**
- Complex security logic (e.g., prototype pollution prevention)
- Used across multiple Form Requests
- Requires dedicated unit tests

**Example: Custom Rule NOT Justified**
- Simple regex that can be inline: `'regex:/^[A-Z]+$/'`
- Used in only 1-2 places
- Wraps Laravel functionality without adding value

---

## SecurityPatterns Usage

### Purpose

`App\Validation\SecurityPatterns` provides regex constants for commonly-validated security patterns.

### When to Use SecurityPatterns

**Use SecurityPatterns when:**
- The same regex pattern is used in 5+ Form Requests
- The pattern has security implications (injection prevention, protocol enforcement)
- Centralization improves maintainability

**Example:**
```php
use App\Validation\SecurityPatterns;

'name' => [
    'required',
    'string',
    'max:255',
    'regex:' . SecurityPatterns::SANITIZED_STRING
],

'base_url' => [
    'required',
    'url',
    'max:2048',
    'regex:' . SecurityPatterns::HTTPS_URL
],
```

### When NOT to Use SecurityPatterns

**Don't use SecurityPatterns when:**
- Pattern is used in only 1-2 places (inline is clearer)
- Pattern is simple and self-explanatory
- Pattern is context-specific and won't be reused

### Available Patterns

| Constant | Purpose | Usage Count Requirement |
|----------|---------|------------------------|
| `HTTPS_URL` | Enforce HTTPS protocol | 5+ uses |
| `SANITIZED_STRING` | Prevent special character injection | 5+ uses |
| `SAFE_PATH` | Validate API endpoints/paths | 5+ uses |
| `ALPHANUMERIC_STRICT` | Strict alphanumeric IDs/keys | 5+ uses |

### Adding New Patterns

**Process:**
1. Identify pattern used in 5+ places
2. Verify security implications
3. Add to SecurityPatterns with docblock
4. Update this policy document
5. Refactor existing Form Requests to use new pattern

---

## Authorization Requirements

### Mandatory Authorization Implementation

**Every Form Request MUST implement authorization using Laravel policies:**

```php
public function authorize(): bool
{
    // Check specific permission for the action
    return $this->user()->can('create', ResourceModel::class);
}
```

### Multi-Tenant Authorization

**For multi-tenant resources, verify tenant ownership:**

```php
public function authorize(): bool
{
    $resource = $this->route('resource');

    // Verify user can access this tenant's resource
    return $this->user()->can('update', $resource)
        && $resource->tenant_id === $this->user()->current_tenant_id;
}
```

### Admin-Only Operations

**For admin-only operations, check role explicitly:**

```php
public function authorize(): bool
{
    return $this->user()->isAdmin()
        && $this->user()->can('delete', ResourceModel::class);
}
```

---

## Testing Requirements

### Unit Tests for Form Requests (REQUIRED)

**Every Form Request MUST have unit tests covering:**

1. **Validation Rules Structure**
   - Verify required fields are marked 'required'
   - Verify regex patterns use SecurityPatterns constants
   - Verify conditional validation logic

2. **Authorization Logic**
   - Verify authorized users can submit
   - Verify unauthorized users receive 403

3. **Validation Behavior**
   - Verify invalid data is rejected with 422
   - Verify valid data passes validation
   - Verify error messages are user-friendly

**Test Location:** `tests/Unit/Http/Requests/[Module]/`

**Example Test:**
```php
describe('StoreConnectorRequest', function () {
    it('requires HTTPS URLs', function () {
        $request = new StoreConnectorRequest();
        $rules = $request->rules();

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

        $hasHttpsRegex = collect($rules['base_url'])->contains(function ($rule) {
            return str_contains($rule, SecurityPatterns::HTTPS_URL);
        });

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

    it('enforces authorization', function () {
        $unauthorizedUser = User::factory()->create(['role' => 'guest']);

        $request = StoreConnectorRequest::create('/connectors', 'POST')
            ->setUserResolver(fn() => $unauthorizedUser);

        expect($request->authorize())->toBeFalse();
    });
});
```

### Testing Standards

- Use file-based SQLite for debugging (see `ai_tests.md`)
- Spy on `Log` facade to verify security logging
- Test both positive and negative cases
- Verify cross-field validation logic

---

## Anti-Patterns and Prohibited Practices

### Prohibited: Mass Assignment Without Validation

```php
// ❌ NEVER DO THIS
public function store(Request $request)
{
    $resource = Resource::create($request->all());
}
```

**Why:** Allows attackers to inject arbitrary fields.

---

### Prohibited: Validation in Action Classes

```php
// ❌ WRONG - Validation should be in Form Request
class CreateConnector
{
    public static function create(array $input)
    {
        $validator = Validator::make($input, [
            'name' => 'required|string',
        ]);

        if ($validator->fails()) {
            // ...
        }
    }
}
```

**Why:** Bypasses controller-level validation. Action classes should assume validated data.

---

### Prohibited: Always-True Authorization

```php
// ❌ WRONG - Never skip authorization
public function authorize(): bool
{
    return true;
}
```

**Why:** Bypasses permission checks. If no permission check needed, document why.

---

### Prohibited: Logging Sensitive Data

```php
// ❌ WRONG - Never log field values
Log::error('Validation failed', [
    'input' => $this->all(), // Contains passwords, tokens!
]);
```

**Why:** Exposes sensitive data in logs.

**Correct:**
```php
// ✅ CORRECT - Log field names only
Log::error('Validation failed', [
    'input_fields' => array_keys($this->all()),
]);
```

---

### Prohibited: Custom Rules That Wrap Laravel Features

```php
// ❌ WRONG - Unnecessary custom rule
class HttpsUrl implements Rule
{
    public function passes($attribute, $value)
    {
        return preg_match('/^https:\/\//', $value);
    }
}
```

**Why:** Laravel already provides `url` and `regex` rules. Use them directly.

---

## Decision Framework

### Should I Create a Form Request?

```
Does the endpoint accept user input?
├─ Yes → REQUIRED: Create Form Request
└─ No → NOT NEEDED: Use standard Request
```

### Should I Use SecurityPatterns?

```
Is this regex pattern used in multiple places?
├─ Yes: Used in 5+ Form Requests?
│   ├─ Yes → USE SecurityPatterns constant
│   └─ No → Inline regex is fine
└─ No: Used in 1 place only
    └─ Inline regex is fine
```

### Should I Create a Custom Validation Rule?

```
Does Laravel provide this validation?
├─ Yes → USE Laravel's built-in rule
└─ No: Is the logic complex?
    ├─ Yes: Will it be used in 5+ places?
    │   ├─ Yes → CREATE custom rule
    │   └─ No → USE validation closure
    └─ No → USE validation closure
```

### Can I Skip Authorization?

```
Does this operation require permission checks?
├─ Yes → IMPLEMENT authorize() with policy check
└─ No: Is this truly public/unrestricted?
    ├─ Yes → Document why (e.g., login, public API)
    └─ No → YOU NEED AUTHORIZATION
```

---

## Code Review Checklist

**Form Request Implementation:**
- [ ] Form Request class created in correct namespace
- [ ] `authorize()` method implements permission check (not just `return true`)
- [ ] `rules()` method uses Laravel built-in rules where possible
- [ ] SecurityPatterns constants used for regex (if pattern reused 5+ times)
- [ ] Custom error messages provided in `messages()` method
- [ ] Security logging implemented in `failedAuthorization()` and `failedValidation()`
- [ ] No sensitive data logged (passwords, tokens, secrets)

**Controller Implementation:**
- [ ] Controller uses Form Request type hint (not base `Request`)
- [ ] Controller uses `$request->validated()` (not `$request->all()`)
- [ ] No `$request->all()` calls anywhere in controller
- [ ] No `$request->only()` used for mass assignment

**Testing:**
- [ ] Unit tests created for Form Request in `tests/Unit/Http/Requests/`
- [ ] Tests verify validation rules structure
- [ ] Tests verify authorization logic
- [ ] Tests verify invalid data is rejected
- [ ] Tests use file-based SQLite (not `:memory:` for debugging)

**Documentation:**
- [ ] Complex validation logic documented in Form Request docblock
- [ ] Authorization requirements documented (which permission required)
- [ ] PR description explains validation rules chosen

---

## Exceptions and Waivers

### Process for Policy Exceptions

**Exceptions to this policy may be granted only if:**
1. Technical limitation prevents compliance (document specific limitation)
2. Performance impact is unacceptable (provide benchmarks)
3. Third-party integration requires alternative approach (document integration)

**Exception Request Process:**
1. Developer documents why exception needed
2. Security lead reviews exception request
3. Alternative mitigation measures proposed
4. Exception documented in code comments with reference number
5. Exception reviewed quarterly for continued validity

**Example Exception Documentation:**
```php
/**
 * SECURITY POLICY EXCEPTION: #SEC-2025-001
 *
 * This endpoint uses $request->input() instead of Form Request
 * because it handles streaming file uploads that cannot be validated
 * in memory. Alternative mitigation: File type validated in stream handler.
 *
 * Exception granted: 2025-11-03
 * Security Lead: [Name]
 * Review Date: 2026-02-03
 */
public function upload(Request $request)
{
    // Special handling...
}
```

---

## Related Documentation

**Implementation Guides:**
- [iPaaS Form Security Audit Plan](../../security/ipaas-form-security-audit-plan.md) - Implementation details for iPaaS module
- [AI Testing Guidelines](../../AI/ai_tests.md) - Testing patterns and best practices

**Laravel Documentation:**
- [Form Request Validation](https://laravel.com/docs/10.x/validation#form-request-validation)
- [Authorization](https://laravel.com/docs/10.x/authorization)
- [Validation Rules](https://laravel.com/docs/10.x/validation#available-validation-rules)

**Security Resources:**
- [OWASP Input Validation Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html)
- [Laravel Security Best Practices](https://laravel.com/docs/10.x/security)

---

## Policy Review and Updates

**Review Schedule:**
- **Quarterly:** Review policy effectiveness and update based on findings
- **After Security Incidents:** Update policy with lessons learned
- **Annual:** Comprehensive review with security team

**Policy Ownership:**
- **Owner:** Development Team Lead
- **Security Reviewer:** Security Team Lead
- **Approver:** CTO

**Version History:**

| Version | Date | Changes | Author |
|---------|------|---------|--------|
| 1.0 | 2025-11-03 | Initial policy created | Development Team |

---

## Summary: Key Principles

1. ✅ **Always use Form Requests** for user input
2. ✅ **Always use `$request->validated()`** - never `$request->all()`
3. ✅ **Always implement authorization** in `authorize()` method
4. ✅ **Always log security events** (authorization/validation failures)
5. ✅ **Use Laravel's built-in validation** for 95% of needs
6. ✅ **Use SecurityPatterns** for frequently-used security regex (5+ uses)
7. ✅ **Test all Form Requests** with unit tests
8. ❌ **Never create custom rules** that wrap Laravel features
9. ❌ **Never log sensitive field values** (passwords, tokens)
10. ❌ **Never skip authorization** without documented exception

**When in doubt: Use Laravel's standard features. They're secure, tested, and maintained.**

---

**Policy Effective Date:** 2025-11-03
**Next Review Date:** 2026-02-03
**Policy Status:** ✅ Active and Enforceable

