Annotation Interface XssCheck
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 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.
Use Cases
- User-generated content: Comments, reviews, forum posts, chat messages
- Profile information: Usernames, display names, bio fields
- Search queries: User input that will be displayed or processed
- Form inputs: Any field that accepts free-form text from users
- API parameters: String parameters that should not contain markup
Example Usage
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)
}
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": "<!--comment-->"} // 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>,<!--comment-->,<![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 <).
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:
public class SecureInputDTO {
@AutoTrim
@XssCheck
private String username; // First trimmed, then XSS-validated
@XssCheck
private String comment; // Only XSS-validated (not trimmed)
}
// Processing order:
// 1. String is trimmed (if @AutoTrim is present)
// 2. Trimmed string is checked for HTML tags (if @XssCheck is present)
// 3. If HTML tags found, IllegalArgumentException is thrown
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 and create 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.3.0
- Author:
- Pasindu OG
- See Also:
-
ApiResponseAutoConfiguration.strictJsonCustomizer()AutoTrimValueDeserializer.createContextual(tools.jackson.databind.DeserializationContext, tools.jackson.databind.BeanProperty)