How do I migrate from hardcoded secrets to secure patterns in EK9?

← Security and Sanitization · Ref: Q705

When the EK9 compiler detects a hardcoded secret (E11080-E11086), follow this migration pattern:

STEP 1: IDENTIFY THE SECRET TYPE

The error code tells you what was detected:

  E11080 → Cloud provider key (AWS, GCP, Azure)
  E11081 → Platform token (GitHub, GitLab, Slack)
  E11082 → Private key material (RSA, ECDSA, OpenSSH)
  E11083 → Database URL with password
  E11084 → JWT token
  E11086 → API key (Stripe, OpenAI, etc.)

STEP 2: CHOOSE AN ENVIRONMENT VARIABLE NAME

Use a descriptive, uppercase name with underscores, such as AWS_ACCESS_KEY_ID, STRIPE_SECRET_KEY or DB_PASSWORD.

STEP 3: REPLACE WITH SENSITIVEGET

  BEFORE: key <- "AKIAIOSFODNN7EXAMPLE1"
  AFTER:  env <- EnvVars()
          key <- env.sensitiveGet("AWS_ACCESS_KEY_ID")

STEP 4: ADD GUARD FOR MISSING VALUES

  if key <- env.sensitiveGet("AWS_ACCESS_KEY_ID")
    configureAws(key)
  else
    stderr.println("AWS_ACCESS_KEY_ID not set")

STEP 5: SET ENVIRONMENT VARIABLE

Set the variable outside the source, with an export of AWS_ACCESS_KEY_ID in the shell that starts the program, or use your platform's secrets management.

DATABASE URL MIGRATION

  BEFORE: url <- "postgres://admin:pass@host/db"
  AFTER:  url <- env.sensitiveGet("DATABASE_URL")

JWT TOKEN MIGRATION

  BEFORE: token <- "eyJhbGci..."
  AFTER:  token <- env.sensitiveGet("AUTH_TOKEN")
  Or generate tokens dynamically at runtime.

See Q701 for Sensitive type. See Q702 for sensitiveGet(). See Q704 for detection details.

Example

defines module qa.security.credentialmigration

  defines function

    <?-
      Correct pattern: cloud provider credential migration.
    -?>
    testCloudCredentialMigration()
      stdout <- Stdout()
      stderr <- Stderr()
      env <- EnvVars()

      //AFTER: Load from environment variable
      if awsKey <- env.sensitiveGet("AWS_ACCESS_KEY_ID")
        stdout.println("AWS key loaded: " + awsKey)
      else
        stderr.println("Set AWS_ACCESS_KEY_ID environment variable")

    <?-
      Correct pattern: API key migration.
    -?>
    testApiKeyMigration()
      stdout <- Stdout()
      stderr <- Stderr()
      env <- EnvVars()

      //AFTER: Load from environment variable
      if stripeKey <- env.sensitiveGet("STRIPE_SECRET_KEY")
        stdout.println("Stripe key loaded: " + stripeKey)
      else
        stderr.println("Set STRIPE_SECRET_KEY environment variable")

    <?-
      Correct pattern: database URL migration.
    -?>
    testDatabaseUrlMigration()
      stdout <- Stdout()
      stderr <- Stderr()
      env <- EnvVars()

      //AFTER: Separate configuration from secrets
      dbHost <- env.get("DB_HOST")
      dbName <- env.get("DB_NAME")
      dbPassword <- env.sensitiveGet("DB_PASSWORD")

      if dbHost? and dbName? and dbPassword?
        stdout.println("Database configured with separate components")
      else
        stderr.println("Configure DB_HOST, DB_NAME, and DB_PASSWORD")

    <?-
      Correct pattern: JWT token migration (generate at runtime).
    -?>
    testJwtTokenMigration()
      stdout <- Stdout()
      stderr <- Stderr()
      env <- EnvVars()

      //For test tokens: load from environment
      if testToken <- env.sensitiveGet("TEST_JWT_TOKEN")
        stdout.println("Test JWT loaded: " + testToken)
      else
        stderr.println("Set TEST_JWT_TOKEN for testing")

    <?-
      Correct pattern: private key migration.
    -?>
    testPrivateKeyMigration()
      stdout <- Stdout()
      stderr <- Stderr()
      env <- EnvVars()

      //AFTER: Load key path or content from environment
      if keyPath <- env.get("TLS_KEY_PATH")
        stdout.println("TLS key path: " + keyPath)
      else
        stderr.println("Set TLS_KEY_PATH environment variable")

  defines program

    CredentialMigrationDemo()
      stdout <- Stdout()
      stdout.println("Credential migration demonstrations")
      testCloudCredentialMigration()
      testApiKeyMigration()
      testDatabaseUrlMigration()
      testJwtTokenMigration()
      testPrivateKeyMigration()

Common mistakes

E11080 — An AWS access key starting with 'AKIA' is detected at compile time (E11080). Extract the key to an environment variable and load it with sensitiveGet(). See ek9 -h E11080 for details.

Incorrect:

if awsKey <- "AKIAIOSFODNN7EXAMPLE1"

Correct:

env <- EnvVars()

      //AFTER: Load from environment variable
      if awsKey <- env.sensitiveGet("AWS_ACCESS_KEY_ID")

E11086 — A Stripe API key with the 'sk_test_' prefix is detected at compile time (E11086). Load API keys from environment variables using sensitiveGet(). See ek9 -h E11086 for details.

Incorrect:

if stripeKey <- "sk_test_abcdefghijklmnopqrstuvwxyz"

Correct:

env <- EnvVars()

      //AFTER: Load from environment variable
      if stripeKey <- env.sensitiveGet("STRIPE_SECRET_KEY")

E11081 — A GitHub personal access token with the 'ghp_' prefix is detected at compile time (E11081). Store platform tokens in environment variables. See ek9 -h E11081 for details.

Incorrect:

if testToken <- "ghp_ABCDEFabcdef1234567890abcdef12345678"

Correct:

env <- EnvVars()

      //For test tokens: load from environment
      if testToken <- env.sensitiveGet("TEST_JWT_TOKEN")

E11083 — Database URLs with embedded passwords are detected at compile time. Store the connection URL in an environment variable. See ek9 -h E11083 for details.

Incorrect:

dbPassword <- "postgres://admin:s3cret@db.example.com/prod"

Correct:

dbPassword <- env.sensitiveGet("DB_PASSWORD")

E11082 — EC private key material is detected at compile time (E11082). Load private keys from files or environment variables at runtime. See ek9 -h E11082 for details.

Incorrect:

if keyPath <- "-----BEGIN EC PRIVATE KEY-----"

Correct:

env <- EnvVars()

      //AFTER: Load key path or content from environment
      if keyPath <- env.get("TLS_KEY_PATH")
Other ways to ask this
  • How do I fix hardcoded credential errors in EK9?
  • What is the EK9 pattern for replacing inline secrets?
  • How do I move from hardcoded passwords to environment variables in EK9?

Coming from another language?

Java: Secrets are typically moved from source to properties files (still in repo), then to environment variables, then to Vault/AWS Secrets Manager. Multiple migration steps. Python: Similar journey from .env files to proper secrets management. EK9: The compiler forces the first migration step (source → environment variable) automatically. No optional intermediate steps.

Keywords: hardcoded, environment, url, service, credential, database, secret, fix, refactor, connect, pattern, driver, password, replace, migrate, error, detection