How do I handle secrets and environment configuration?

← Security and Sanitization · Ref: Q271

EK9 handles secrets through the EnvVars type, which provides auto-sanitized access to environment variables. This aligns with the 12-factor app methodology and container practices.

ENVVARS API

The EnvVars type provides:

  env <- EnvVars()
  env.get(name)            Auto-sanitized: blocks SQL, XSS, command injection but allows system paths
  env.unsanitizedGet(name) Raw value with no sanitization
  env contains name        Check if variable exists
  env.keys()               Iterate all variable names

AUTO-SANITIZATION

The get() method runs InputSanitizer automatically. This blocks injection patterns in environment variable values while allowing legitimate system paths like /usr/bin. Use unsanitizedGet() only when you specifically need raw values.

GUARD PATTERN

Combine EnvVars with guard expressions for safe access:

  env <- EnvVars()
  if apiKey <- env.get("API_KEY")
    processWithKey(apiKey)
  else
    stderr.println("API_KEY not configured")

The guard handles both missing variables and sanitization failures in one line.

SANITIZED FORWARDING

Pass secrets to functions via sanitized parameters to prevent accidental logging:

  connectToService()
    -> apiKey as sanitized String

The compiler tracks the tainted status through the call chain.

WHAT NOT TO DO

Never embed secrets in source code. Never log secret values (sanitized tracking helps prevent this). Never store secrets in the .ek9 build directory. Never use unsanitizedGet without a specific reason.

INDUSTRY ALIGNMENT

Environment variables are the industry standard for secrets. Docker and Kubernetes inject secrets as env vars. Vault and AWS Secrets Manager ultimately deliver values through env vars. The 12-factor app methodology specifies env vars for configuration. EK9's EnvVars type with auto-sanitization goes beyond what most languages provide.

See Q215 for sanitized parameters. See Q29 for unset variables and guard patterns. See Q74 for guard expressions. See Q268 for OWASP vulnerability prevention. See Q272 for defense in depth.

Example

defines module qa.security.secrets

  defines function

    // Accept sanitized secret for processing
    connectToService() as pure
      -> apiKey as sanitized String
      <- result as String: "No key provided"

      // Copy constructor for defensive copy
      safeKey <- String(apiKey)
      if safeKey?
        result: "Connected with key length: " + $length safeKey

  defines program

    SecretsAndEnvDemo()
      stdout <- Stdout()
      stderr <- Stderr()

      // === READ ENVIRONMENT VARIABLES ===

      env <- EnvVars()

      // Auto-sanitized access with guard
      if path <- env.get("PATH")
        stdout.println("PATH available, length: " + $length path)
      else
        stdout.println("PATH not available")

      // === CHECK EXISTENCE ===

      if env contains "HOME"
        stdout.println("HOME is configured")

      // === GUARD PATTERN FOR SECRETS ===

      if apiKey <- env.get("API_KEY")
        // Pass as sanitized to maintain tracking
        result <- connectToService(apiKey)
        if result?
          stdout.println(result)
      else
        stderr.println("API_KEY not configured")

      // === ITERATE AVAILABLE KEYS ===

      stdout.println("Environment variable access patterns:")
      stdout.println("  get() — auto-sanitized")
      stdout.println("  unsanitizedGet() — raw value")
      stdout.println("  contains — check existence")
      stdout.println("  keys() — iterate names")

Common mistakes

E50001 — Renaming the variable means later references to 'env' become unresolved, triggering E50001. Variable names must be consistent. See ek9 -h E50001 for details.

Incorrect:

envXYZ <- EnvVars()

Correct:

env <- EnvVars()

E50001 — Renaming the variable means later references to 'result' become unresolved, triggering E50001. Variable names must be consistent. See ek9 -h E50001 for details.

Incorrect:

resultXYZ <- connectToService(apiKey)

Correct:

result <- connectToService(apiKey)
Other ways to ask this
  • How do I read environment variables in EK9?
  • How does EK9 handle API keys and credentials?
  • What is the best way to manage secrets in EK9?

Coming from another language?

Java: System.getenv() returns raw String, no sanitization. Python: os.environ with no safety. Go: os.Getenv returns raw string. Rust: std::env::var returns raw string. Kotlin: System.getenv() with no sanitization. EK9: EnvVars.get() auto-sanitizes blocking injection patterns, unsanitizedGet() for raw access, guard pattern for missing variables.

Keywords: env, variable, sensitive, secret, protect, config, password, api, credential, token, security, environment, safe, key