Must an override keep the 'sanitized' modifier of its parent?
← Security and Sanitization · Ref: Q686
Yes. If a parent method marks a parameter 'sanitized', every override must keep it (E07940). An override may also ADD 'sanitized' to a parameter the parent leaves plain - that is allowed.
DROPPING IS REJECTED (E07940)
Removing 'sanitized' in an override creates a vulnerability: callers holding the parent type expect their input to be cleaned, but through polymorphism the override would receive it raw.
ADDING IS ALLOWED
The receiving method cleans its own 'sanitized' parameters, so an override that adds 'sanitized' cleans its input on every route, including a call through the parent type. A subtype may weaken a precondition, never strengthen it (Liskov Substitution).
WHY THIS MATTERS
handler <- getHandler() // Could be Base or Derived handler.process(userInput) // Base promised to clean userInput
Because no override can drop 'sanitized', the promise holds whichever runtime type is active.
SAFE COPY PATTERN
Inside methods with sanitized parameters, create a safe copy before processing (a direct 'local <- param' is E07930):
safeCopy <- String(inputText) result: transform(safeCopy)
See Q663 for adding and dropping sanitized in detail. See Q661 for sanitized parameter basics. See Q662 for safe copy patterns.
Example
defines module qa.sanitizeddeep.overridematch defines class <?- Base class with sanitized parameter. All overrides must maintain the sanitized modifier. -?> InputHandler as open processInput() -> userInput as sanitized String <- cleanResult as String: "" safeCopy <- String(userInput) cleanResult: "Handled: " + safeCopy default operator ? <?- Correct override: sanitized matches the parent. Security contract is maintained through polymorphism. -?> StrictHandler extends InputHandler override processInput() -> userInput as sanitized String <- cleanResult as String: "" safeCopy <- String(userInput) cleanResult: "Strict: " + safeCopy default operator ? <?- Second correct override demonstrating consistent sanitized. -?> LoggingHandler extends InputHandler override processInput() -> userInput as sanitized String <- cleanResult as String: "" safeCopy <- String(userInput) cleanResult: "Logged: " + safeCopy default operator ? defines program SanitizedOverrideMatchDemo() stdout <- Stdout() handlers <- List() of InputHandler handlers += StrictHandler() handlers += LoggingHandler() for handler in handlers result <- handler.processInput("user <script> input") stdout.println(result)
Common mistakes
E07930 — Direct assignment from a sanitized parameter creates hidden aliasing where both variables share the same sanitized copy. Use String(param) copy constructor for an independent copy. See ek9 -h E07930 for details.
Incorrect:
safeCopy <- userInput cleanResult: "Handled: " + safeCopy
Correct:
safeCopy <- String(userInput) cleanResult: "Handled: " + safeCopy
E50060 — String has no toUpperCase() method in EK9. Use upperCase() instead. See ek9 -h E50060 for details.
Incorrect:
result <- handler.processInput("user <script> input").toUpperCase()
Correct:
result <- handler.processInput("user <script> input")
Other ways to ask this
- Is E07940 raised when an override adds sanitized?
- Why must a child keep the parent's sanitized modifier?
- Must sanitized match exactly when overriding a method?
- What happens if I remove sanitized from an override?
Coming from another language?
Java: no compiler enforcement, overrides can change validation. Python: no enforcement. Go: no inheritance. Rust: trait implementations must match. Kotlin: no sanitized concept. EK9: the compiler rejects an override that drops sanitized, keeping the parent's security contract for Liskov compliance.
Keywords: handler, virtual, abstract, visitor, match, open, Liskov, LSP, security, polymorphism, E07940, E07930, sanitized, sealed, function, validate, override, sanitize