How does the 'sanitized' parameter modifier work in EK9?

← Security and Sanitization · Ref: Q215

The 'sanitized' modifier on a parameter makes the RECEIVING function clean that parameter at run time, as it is entered. It is not compile-time taint tracking: the compiler only checks where the modifier may be written and one aliasing rule (E07930).

SANITIZED PARAMETER

Mark a parameter as sanitized:

  processInput() as pure
    -> input as sanitized String
    <- result as String: input

On entry the function scans the value for threats (SQL injection, XSS, command injection, path traversal and others). A safe value becomes the function's own clean copy; a value carrying a threat arrives UNSET, so it prints nothing and a set-check on it fails.

NO LOCAL ALIAS (E07930)

In any function, pure or not, you cannot alias the parameter into a new local ('local <- input'). Make an independent copy instead:

  localCopy <- String(input)

This creates independent data. Using the parameter directly in an expression, or assigning it to the return variable, is also allowed.

COPYING INTO AN EXISTING VARIABLE

In any function, :=: or :~: copy into an initialized variable:

  safeCopy <- String(sql)
  result: ""
  result :=: safeCopy

DIRECT USE IN EXPRESSIONS

Using sanitized parameters in expressions creates new values:

  output: "Processed: " + input

See Q216 for threat detection. See Q217 for pure function interaction. See Q49 for function basics. See Q54 for pure functions. See Q269 for input validation. See Q271 for secrets and environment variables. See Q272 for defense in depth. See Q661 for sanitized parameter restrictions. See Q662 for safe copy patterns. See Q663 for override matching.

Example

defines module qa.security.sanitized

  defines function

    // Pure function with sanitized parameter
    pureProcess() as pure
      -> input as sanitized String
      <- result as String?

      // Copy constructor creates safe local copy
      localCopy <- String(input)
      result: "Processed: " + localCopy

    // Non-pure function with sanitized parameter
    queryProcess()
      -> sql as sanitized String
      <- result as String?

      // Copy constructor first
      safeCopy <- String(sql)
      result: ""
      result :=: safeCopy

    // Direct expression use is safe
    logInput() as pure
      -> userInput as sanitized String
      <- output as String?

      // Concatenation creates new string
      output: "Log: " + userInput

  defines program

    SanitizedParameterDemo()
      stdout <- Stdout()

      // === PURE FUNCTION WITH SANITIZED ===

      query <- "SELECT * FROM users"
      result1 <- pureProcess(query)
      if result1?
        stdout.println(result1)

      // === NON-PURE WITH COPY ===

      result2 <- queryProcess(query)
      if result2?
        stdout.println("Query: " + result2)

      // === DIRECT EXPRESSION ===

      result3 <- logInput("user data")
      if result3?
        stdout.println(result3)

Common mistakes

E50060 — String does not have a clone() method. Use the copy constructor String(value) to create a defensive copy. Calling a non-existent method triggers E50060 — method not resolved. See ek9 -h E50060 for details.

Incorrect:

localCopy <- input.clone()

Correct:

localCopy <- String(input)
Other ways to ask this
  • How do I mark parameters as sanitized in EK9?
  • How does EK9 clean tainted input?
  • How do I handle user input safely in EK9?

Coming from another language?

Java: no language-level sanitization, relies on OWASP libraries. Python: no built-in taint tracking. Rust: newtype pattern for manual tracking. Go: no taint tracking. Kotlin: no sanitization modifier. EK9: 'sanitized' on a parameter makes the receiving function clean it at run time (a threat arrives unset); the compile-time rules only cover where the modifier is allowed and the no-local-alias rule E07930.

Keywords: security, tainted, safe, runtime, input, copy, validation, sanitized, protect, injection