Skip to content

Obfuscate secret-looking values in apppack config get / config list #174

Description

@blsmth

Problem

apppack config get and apppack config list print secret values in plaintext with no guardrails.

  • cmd/config.go:56-62config get calls GetParameter with WithDecryption: true and fmt.Printlns the raw value.
  • cmd/config.go:141config list dumps every variable and its decrypted value via ConfigVariables.ToConsole.

Config vars are SecureString params in SSM, so this includes SECRET_KEY, DATABASE_URL, API tokens, etc. In practice this means credentials end up in:

  • shell scrollback and tmux/terminal history
  • CI job logs when someone runs config list in a pipeline
  • screen shares and pairing sessions
  • terminal recordings / support screenshots

There is no masking anywhere in the config path today (app/config.go has no redaction logic).

Proposal

Best-effort obfuscation of values that look like secrets, on by default for interactive output only.

1. Heuristic detection by variable name. Case-insensitive substring match on a deny-list, roughly:

SECRET, PASSWORD, PASSWD, TOKEN, API_KEY, APIKEY, PRIVATE_KEY,
CREDENTIAL, AUTH, SALT, SIGNATURE, ACCESS_KEY, SESSION_KEY, DSN

Plus values that parse as a URL with a non-empty userinfo section (catches DATABASE_URL, REDIS_URL, postgres://user:pw@host), where we mask only the password component rather than the whole value — the host/db name is useful and not sensitive.

2. Mask format. Show enough to identify without leaking: first 2 and last 2 chars for values > 8 chars, e.g. sk•••••••••••••4f, and a fixed •••••••• for short values. Keep it obvious that it's masked, not the literal value.

3. Escape hatches. Masking must never break automation:

  • Skip masking entirely when stdout is not a TTY (config get FOO | ..., $(apppack config get FOO) in scripts). config list already checks isatty at cmd/config.go:136, so the plumbing exists.
  • --reveal (or --no-mask) flag on get and list to print plaintext in a TTY.
  • config export stays unmasked always — it exists to produce a machine-readable dump and is the documented backup/restore path. Same for --json.

4. Print a hint when something was masked: 1 value masked — use --reveal to show so nobody thinks the value is literally bullets.

Notes / open questions

  • This is deliberately a heuristic. It will miss things (STRIPE_SK, FOO_PW) and over-mask others (AUTH_ENABLED=true). That's the accepted tradeoff — the goal is reducing accidental exposure, not a security boundary. Worth saying so in the docs.
  • Alternative to a name deny-list: mask everything by default in TTY mode and require --reveal to see any value. Safer and much simpler to reason about, but more annoying day-to-day. Leaning toward the heuristic, open to being talked into blanket masking for config list.
  • Should --reveal be gated behind a confirmation prompt? Probably not — too noisy.

Scope

  • apppack/app/config.go — masking helper + tests (table-driven over name/value pairs)
  • apppack/cmd/config.go — wire into get and list, add --reveal
  • apppack-docs — note the behavior and the escape hatch in the config command reference

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestgoPull requests that update go code

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions