Write me a QueryBuilder class with an addClause method that takes a sanitized String fragment, returning 'accepted' for clean fragments and 'rejected' for malicious ones (LDAP injection, NoSQL operator injection).

← Security and Sanitization · Ref: Q1247

The 'sanitized' modifier works on method parameters identically to constructors and functions. When a method receives malicious input, the parameter is UNSET, so the method can branch on 'fragment?' to handle the rejected case without exceptions.

CLASS WITH SANITIZED METHOD PARAMETER

  QueryBuilder
    accepted as Integer: 0
    rejected as Integer: 0
    default QueryBuilder()
    addClause()
      -> fragment as sanitized String
      <- result as String: "unset"
      if fragment?
        accepted: accepted + 1
        result: "accepted"
      else
        rejected: rejected + 1
        result: "rejected"
    getStats()
      <- rtn as String: `accepted=${accepted} rejected=${rejected}`

The class keeps running counters of accepted and rejected fragments, demonstrating that the object remains usable across many calls — clean fragments increment the accepted counter, malicious ones increment rejected, neither path throws.

DEMONSTRATING DIVERSE INJECTION TYPES

This example deliberately uses injection categories OTHER than SQL/XSS, to show that 'sanitized' protects against any untrusted text. It also shows that a URL-shaped fragment is not, by itself, a threat: an ordinary redirect parameter is accepted:

  qb <- QueryBuilder()
  // Clean equality clause
  qb.addClause("name = 'alice'")           // accepted
  // A URL-shaped fragment is NOT flagged
  qb.addClause("redirect=http://evil/?p=1") // accepted
  // LDAP injection
  qb.addClause("(|(uid=*)(uid=*))")          // rejected
  // NoSQL operator injection
  qb.addClause("$where: function() { return true }") // rejected
  // Clean range clause
  qb.addClause("age >= 18")                  // accepted

The program ends with accepted=3 rejected=2, and each rejection writes a JSON warning line to stderr naming the threat (LDAP_INJECTION, NOSQL_INJECTION). 'sanitized' screens for injection patterns; it does not judge whether a URL is trustworthy, so validate redirect targets yourself.

WHEN TO USE METHOD-LEVEL SANITIZATION

Use method-level sanitization when an object is constructed from trusted sources but later receives untrusted input through method calls — query builders, log appenders, file writers that accept paths, HTTP request builders that accept headers.

See Q1246 for sanitized constructor parameters. See Q1237 for sanitized function parameters. See Q272 for defense in depth.

Example

defines module qa.security.querybuildermethod

  defines class

    QueryBuilder
      accepted as Integer: 0
      rejected as Integer: 0

      default QueryBuilder()

      addClause()
        -> fragment as sanitized String
        <- result as String: "unset"

        if fragment?
          accepted: accepted + 1
          result: "accepted"
        else
          rejected: rejected + 1
          result: "rejected"

      getStats()
        <- rtn as String: `accepted=${accepted} rejected=${rejected}`

      default operator ?

  defines program

    SanitizedQueryBuilderDemo()
      stdout <- Stdout()
      qb <- QueryBuilder()

      stdout.println("=== QueryBuilder Sanitization Demo ===")

      cleanEquality <- "name = 'alice'"
      stdout.println("Equality: " + qb.addClause(cleanEquality))

      urlInjection <- "redirect=http://evil/?p=1"
      stdout.println("URL: " + qb.addClause(urlInjection))

      ldapInjection <- "(|(uid=*)(uid=*))"
      stdout.println("LDAP: " + qb.addClause(ldapInjection))

      nosqlInjection <- "$where: function() { return true }"
      stdout.println("NoSQL: " + qb.addClause(nosqlInjection))

      cleanRange <- "age >= 18"
      stdout.println("Range: " + qb.addClause(cleanRange))

      stdout.println(qb.getStats())
      stdout.println("=== Complete ===")

Common mistakes

E07910 — The sanitized modifier can only be applied to String parameters. Injection attacks travel as text, so a non-String type has nothing to clean. See ek9 -h E07910 for details.

Incorrect:

-> fragment as sanitized Integer

Correct:

-> fragment as sanitized String
Other ways to ask this
  • Show me how the sanitized keyword works on a method parameter for a query builder.
  • Create a QueryBuilder class whose addClause method validates its input via the sanitized modifier.
  • Implement a method that protects query state from injection through call arguments.
  • Build a class with a sanitized-parameter method that classifies query fragments.

Coming from another language?

Java: validation inside the method body, or runtime libraries like Apache Validator. Python: input validation with re.match before use. Rust: validate by attempting to construct a newtype from the input. C#: data annotations validators on parameters. EK9: 'sanitized' modifier on the parameter — input is unset on rejection, no exception, no manual validation code.

Keywords: LDAP injection, sanitized, method validation, object protection, method, NoSQL injection, QueryBuilder