Record Class NormalizationStage

java.lang.Object
java.lang.Record
de.cuioss.http.security.validation.NormalizationStage
Record Components:
config - Security configuration controlling validation behavior.
validationType - Type of validation being performed (URL_PATH, PARAMETER_NAME, etc.).
All Implemented Interfaces:
HttpSecurityValidator

public record NormalizationStage(SecurityConfiguration config, ValidationType validationType) extends Record implements HttpSecurityValidator
Path normalization validation stage with security checks.

This stage performs RFC 3986 Section 5.2.4 path normalization to resolve relative path segments (. and ..) while detecting and preventing path traversal attacks. The stage processes paths through multiple security layers:

  1. Segment Parsing - Splits path into segments for processing
  2. Normalization - Resolves . and .. segments according to RFC 3986
  3. Security Validation - Detects remaining traversal attempts
  4. Root Escape Detection - Prevents escaping application root

Design Principles

  • RFC Compliance - Follows RFC 3986 path normalization rules
  • Security First - Detects attacks through normalization analysis
  • DoS Protection - Prevents excessive nesting and recursion attacks
  • Thread Safety - Safe for concurrent use across multiple threads

Security Validations

  • Path Traversal - Detects ../ patterns that remain after normalization
  • Root Escape - Prevents paths from escaping the application root
  • Excessive Nesting - Limits path depth to prevent resource exhaustion
  • Malicious Patterns - Identifies suspicious path construction

Usage Examples

 // Create normalization stage
 SecurityConfiguration config = SecurityConfiguration.defaults();
 NormalizationStage normalizer = new NormalizationStage(config, ValidationType.URL_PATH);

 // Normalize legitimate path
 String normalized = normalizer.validate("/api/users/./123/../456");
 // Returns: "/api/users/456"

 // Detect path traversal attack
 try {
     normalizer.validate("/api/../../etc/passwd");
     // Throws UrlSecurityException with DIRECTORY_ESCAPE_ATTEMPT
 } catch (UrlSecurityException e) {
     logger.warn("Path traversal blocked: {}", e.getFailureType());
 }

 // Detect excessive nesting attack
 try {
     normalizer.validate("/a/../b/../c/../d/../e/../f/../g/../h/../i/../j/../k/../l/../m/../n/../o/../p/../q/../r/../s/../t");
     // Throws UrlSecurityException with EXCESSIVE_NESTING
 } catch (UrlSecurityException e) {
     logger.warn("DoS attack blocked: {}", e.getFailureType());
 }
 

Performance Characteristics

  • O(n) time complexity where n is the number of path segments
  • Single pass through path segments with early termination
  • Minimal memory allocation - reuses StringBuilder
  • DoS protection through segment counting

RFC 3986 Compliance

This implementation follows RFC 3986 Section 5.2.4 "Remove Dot Segments":

  • Single dot segments (.) are removed
  • Double dot segments (..) remove the previous segment
  • Trailing slashes are preserved
  • Leading slashes are preserved

Implements: Task V2 from HTTP verification specification

Since:
1.0
See Also:
  • Constructor Details

  • Method Details

    • validate

      public Optional<String> validate(@Nullable String value) throws UrlSecurityException
      Validates and normalizes a path with comprehensive security checks.

      Processing stages:

      1. Input validation - handles null/empty inputs
      2. Path segment parsing - splits on directory separators
      3. RFC 3986 normalization - resolves . and .. segments
      4. Security validation - detects remaining attack patterns
      Specified by:
      validate in interface HttpSecurityValidator
      Parameters:
      value - The input path to validate and normalize
      Returns:
      The validated and normalized path wrapped in Optional, or Optional.empty() if input was null
      Throws:
      UrlSecurityException - if any security violations are detected:
      • EXCESSIVE_NESTING - if path contains too many segments or depth
      • PATH_TRAVERSAL_DETECTED - if ../ patterns remain after normalization
      • DIRECTORY_ESCAPE_ATTEMPT - if normalized path tries to escape root
    • when

      Creates a conditional validator that only processes inputs matching the condition.
      Specified by:
      when in interface HttpSecurityValidator
      Parameters:
      condition - The condition to test before validation
      Returns:
      A conditional HttpSecurityValidator that applies normalization conditionally
    • toString

      public final String toString()
      Returns a string representation of this record class. The representation contains the name of the class, followed by the name and value of each of the record components.
      Specified by:
      toString in class Record
      Returns:
      a string representation of this object
    • hashCode

      public final int hashCode()
      Returns a hash code value for this object. The value is derived from the hash code of each of the record components.
      Specified by:
      hashCode in class Record
      Returns:
      a hash code value for this object
    • equals

      public final boolean equals(Object o)
      Indicates whether some other object is "equal to" this one. The objects are equal if the other object is of the same class and if all the record components are equal. All components in this record class are compared with Objects::equals(Object,Object).
      Specified by:
      equals in class Record
      Parameters:
      o - the object with which to compare
      Returns:
      true if this object is the same as the o argument; false otherwise.
    • config

      Returns the value of the config record component.
      Returns:
      the value of the config record component
    • validationType

      Returns the value of the validationType record component.
      Returns:
      the value of the validationType record component