SecurityInputFilter is the request-level input guard for high-confidence XSS detection. It inspects GET and POST values before controller logic runs, canonicalizes user input to reduce encoding bypasses, and blocks the request when a known dangerous pattern is detected.

i
Good name in Features This docs page is listed as Input Security Guard because the filter is not only about sanitizing forms. It is a request gate that checks user-controlled input before normal application logic continues.

Overview

flowchart TD A[Incoming request] --> B[Read GET and POST input] B --> C{Any input present} C -->|No| D[Allow request] C -->|Yes| E[Canonicalize each value] E --> F[Trim input] F --> G[Check XSS patterns] G --> H{Dangerous pattern found} H -->|No| D H -->|Yes| I[Log metadata and hash] I --> J[Return 403 JSON]

Where it runs

The filter is globally registered in app/Config/Filters.php in the before chain, which means it runs for normal incoming web requests unless the route is explicitly excluded.

'SecurityInputFilter' => SecurityInputFilter::class,

'before' => [
    'SecurityInputFilter' => [
        'except' => [
            '/client/notification/create',
            '/ticket/crud_mail_template/*',
            'test_mail',
            'leads/sendMail',
            'ticket/reply'
        ]
    ],
]

The filter only reads:

It does not inspect uploaded file contents. File uploads are handled by the separate GlobalPostFileUploadGuard filter.

What it checks

The filter uses a focused list of high-confidence XSS patterns to reduce false positives while still blocking obvious injection attempts.

CategoryExamples from the filter
Script tags <script, </script>
JavaScript execution schemes javascript:, vbscript:, data:text/html
Inline event handlers onclick=, onerror=, onload=
Dangerous HTML tags <iframe, <object, <embed, <applet, <img
SVG / MathML vectors <svg, <math
Meta refresh payloads <meta http-equiv="refresh"
Injected src/href handlers HTML tags using src=javascript: or href=data:
!
Detection, not rich sanitization This filter is a blocker for clearly malicious input. It is not a full HTML sanitizer for rich text fields. If a feature needs controlled HTML input, design that path explicitly and make sure the route is handled appropriately.

Canonicalization

Before pattern matching, the filter canonicalizes each value:

  1. URL decode

    Helps catch encoded payloads that would otherwise bypass naive matching.

  2. HTML entity decode

    Turns entity-encoded payloads into their real characters before detection.

  3. Strip invisible control characters

    Removes null bytes and other control characters from the evaluation string.

  4. Trim the final value

    Reduces noise before regex evaluation.

private function canonicalize(string $value): string
{
    $value = urldecode($value);
    $value = html_entity_decode($value, ENT_QUOTES | ENT_HTML5, 'UTF-8');

    return preg_replace('/[\x00-\x1F\x7F]/u', '', $value);
}

Array inputs are converted to JSON first, then canonicalized as a string.

Block behavior

When a pattern matches, the filter logs security metadata and immediately returns a JSON 403 response:

Logged fieldPurpose
ipSource IP address
methodRequest method
uriCurrent request URL
fieldInput field name
attackStatic marker XSS_PATTERN
lengthCanonicalized payload length
hashSHA-256 hash of the canonicalized payload

The raw input value is not logged directly. The filter logs intent metadata and a hash instead.

{
  "status": 403,
  "error": "Forbidden",
  "message": "Malicious input detected"
}

Filter exceptions

Some routes are explicitly excluded from the global security-input filter:

Excluded routeWhy developers should care
/client/notification/create Global input blocking does not run here.
/ticket/crud_mail_template/* Template-editing paths often need richer content and should be handled deliberately.
test_mail Bypassed globally.
leads/sendMail Bypassed globally.
ticket/reply Bypassed globally.
!
Important developer rule If you add a route to the exception list, you are taking responsibility for validating and safely handling that input somewhere else in the request flow.

Developer steps

When building a new form, endpoint, or feature that accepts user input, follow this checklist:

  1. Assume GET and POST are inspected automatically

    If your route is not in the exception list, the filter already evaluates GET and POST fields before controller code runs.

  2. Do not rely on this filter as your only validation

    Business validation, field-level validation, and output escaping are still required.

  3. Be careful with HTML-capable inputs

    If a feature legitimately accepts formatted HTML, do not silently fight the filter. Design a safe path for that route and document why it needs special handling.

  4. Only add filter exceptions deliberately

    If you exclude a route in Filters.php, add compensating server-side sanitization or allowlist logic in the receiving code.

  5. Test encoded attack strings too

    Because the filter canonicalizes input, test URL-encoded and HTML-entity-encoded payloads in addition to plain strings.

  6. Watch the logs when troubleshooting blocks

    The filter logs a structured critical event named SECURITY_BLOCKED_REQUEST with a payload hash and field name.

Common pitfalls

PitfallWhy it happens
A rich text feature keeps returning 403 The submitted markup matches one of the high-confidence XSS patterns, and the route is still under the global filter.
Input looks harmless in raw form but still gets blocked The canonicalization step decoded the payload into a dangerous form before matching.
A developer adds an exception without extra protection The route bypasses the global blocker and now depends entirely on downstream validation.
Files are assumed to be covered here Uploaded file contents are handled by the separate file-upload guard, not this filter.
+
Practical takeaway Treat SecurityInputFilter as the first request-level XSS tripwire. Keep it on by default, make exceptions rarely, and document every exception with the safer validation path that replaces it.