Annotation Interface XssCheck


@Target({TYPE,FIELD}) @Retention(RUNTIME) public @interface XssCheck
Annotation to explicitly enable XSS (Cross-Site Scripting) validation for string fields during JSON deserialization.

By default, the OG4Dev Spring API Response library does NOT perform XSS validation on strings. This annotation allows you to opt-in to automatic HTML/XML tag detection and rejection for specific fields or entire classes where preventing malicious content injection is critical for security.

Security Approach: This annotation implements a fail-fast rejection strategy - requests containing HTML tags are rejected entirely with a 400 Bad Request error. This is more secure than HTML escaping, as it prevents stored XSS, DOM-based XSS, and second-order injection vulnerabilities.

Target Scopes

  • Field Level (ElementType.FIELD): Applies XSS validation only to the specific annotated String field.
  • Class Level (ElementType.TYPE): Applies XSS validation to all String fields within the annotated class globally.

Example Usage: Field Level


 public class CommentDTO {
 @XssCheck
 private String content;        // XSS validated - rejects HTML tags

 @XssCheck
 private String authorName;     // XSS validated - rejects HTML tags

 private String commentId;      // NOT validated (no annotation)
 private Instant timestamp;     // NOT validated (not a string)
 }
 

Example Usage: Class Level


 @XssCheck // Automatically protects ALL String fields in this class!
 public class SecureUserProfileDTO {
 private String bio;            // XSS validated automatically
 private String displayName;    // XSS validated automatically
 private String websiteUrl;     // XSS validated automatically
 }
 

Valid and Invalid Inputs


 // ✅ Valid inputs (accepted)
 {"content": "Hello World"}                     // Plain text
 {"content": "Price: $100 < $200"}              // Comparison operators (no tag)
 {"content": "2 + 2 = 4"}                       // Math expressions
 {"content": "Use angle brackets: 3 < 5"}       // Text with < but no HTML tag

 // ❌ Invalid inputs (rejected with 400 Bad Request)
 {"content": "<script>alert('XSS')</script>"}   // Script injection
 {"content": "<img src=x onerror=alert(1)>"}    // Image XSS attack
 {"content": "Hello<br>World"}                  // HTML break tag
 {"content": ""}                  // HTML comment
 {"content": "<!DOCTYPE html>"}                 // DOCTYPE declaration
 {"content": "</div>"}                          // Closing tag
 {"content": "<b>Bold text</b>"}                // HTML formatting
 

Error Response Format

When HTML tags are detected, the request is rejected with a 400 Bad Request error:


 {
 "type": "about:blank",
 "title": "Bad Request",
 "status": 400,
 "detail": "Security Error: HTML tags or XSS payloads are not allowed in the request.",
 "traceId": "550e8400-e29b-41d4-a716-446655440000",
 "timestamp": "2026-02-21T10:30:45.123Z"
 }
 

XSS Detection Mechanism

The validation uses a robust regex pattern: (?s).*<\s*[a-zA-Z/!].*

This pattern detects:

  • Opening tags: <script>, <img>, <div>, <iframe>
  • Closing tags: </div>, </script>, </body>
  • Self-closing tags: <br/>, <input/>
  • Special tags: <!DOCTYPE>, , <![CDATA[]]>
  • Tags with attributes: <div class="test">, <img src="x">
  • Multiline tags: Tags spanning multiple lines (DOTALL mode enabled)

What is NOT detected (safe to use):

  • Mathematical comparisons: 5 < 10, x > y
  • Arrows and symbols: -> <-, <=>
  • Quoted examples: "less than symbol: <" (if properly escaped in JSON)

Why Rejection Instead of Escaping?

This library uses a fail-fast rejection approach rather than HTML escaping (converting < to &lt;). This is more secure because:

  • Prevents stored XSS: Malicious content never enters your database
  • Prevents DOM-based XSS: No chance of client-side re-interpretation
  • Prevents second-order attacks: Escaped content cannot be un-escaped later
  • Prevents encoding bypasses: No risk of double-encoding vulnerabilities
  • Clear security policy: Users know HTML is not allowed

Combining with @AutoTrim

You can combine @XssCheck with @AutoTrim for both behaviors:


 @XssCheck // Protects all fields
 public class SecureInputDTO {
 @AutoTrim // Trims only this field
 private String username;
 }
 

How It Works

This annotation is processed by the AdvancedStringDeserializer in ApiResponseAutoConfiguration.strictJsonCustomizer(). The deserializer uses ValueDeserializer.createContextual(tools.jackson.databind.DeserializationContext, tools.jackson.databind.BeanProperty) to detect the annotation on either the field itself or its declaring class, creating a specialized instance that enables XSS validation.

Null Value Handling

Null values are not validated (they are safe) and pass through unchanged:


 {"content": null}      → content = null (no validation)
 {"content": ""}        → content = "" (validated, but empty string is safe)
 {"content": "  "}      → content = "  " (validated, but whitespace is safe)
 

Performance Considerations

The regex validation is highly optimized and adds minimal overhead (typically <1ms per field). The deserializer is created once per field during mapper initialization, not on every request, ensuring optimal runtime performance.

When to Use This Annotation

Field Type Use @XssCheck? Reason
User comments ✅ Yes User-generated content that will be displayed
Profile bio ✅ Yes Free-form text that could contain malicious content
Search queries ✅ Yes User input that might be echoed back
Email address ⚠️ Optional Use if email will be displayed; skip if only stored
IDs/UUIDs ❌ No Structured format, not free-form text
Timestamps ❌ No Not a string field
Rich text (HTML editor) ❌ No Intentionally contains HTML; use server-side sanitization instead
Since:
1.3.0
Version:
1.4.0
Author:
Pasindu OG
See Also: