Skip to content

Mask secret-looking config values in TTY output - #175

Open
blsmth wants to merge 1 commit into
mainfrom
agent/go-engineer/adhoc-292b9d93
Open

Mask secret-looking config values in TTY output#175
blsmth wants to merge 1 commit into
mainfrom
agent/go-engineer/adhoc-292b9d93

Conversation

@blsmth

@blsmth blsmth commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Summary

  • apppack config get and apppack config list previously printed secret values (SECRET_KEY, DATABASE_URL, API tokens, etc.) in plaintext, unconditionally.
  • Adds a best-effort, name-based heuristic (app/mask.go) that masks values which look sensitive: a case-insensitive deny-list substring match on the variable name, plus URL-userinfo password masking for DATABASE_URL/REDIS_URL-style values (host/db stay readable).
  • Masking only kicks in when stdout is a TTY, and only on config get / config list — never on config export, --json, or piped output — so scripts and $(apppack config get FOO) keep working unchanged.
  • New --reveal flag on both get and list prints plaintext even in a TTY.
  • A hint (N value(s) masked — use --reveal to show) is printed to stderr so it never pollutes captured/piped stdout.

This is a heuristic, not a security boundary — it will miss things like STRIPE_SK and over-mask false positives like AUTH_ENABLED (test asserts this known tradeoff explicitly). See apppack#174 for the design discussion.

Closes #174

Test plan

  • make fmt, make test, make lint all pass (no new lint issues; pre-existing repo lint debt in unrelated files left untouched).
  • New table-driven tests in app/mask_test.go and app/config_test.go cover: sensitive names, short/long/empty values, DATABASE_URL password-only masking, redis://host:6379 (no userinfo) untouched, plain vars untouched, and the AUTH_ENABLED false-positive documented as expected behavior.
  • TestConfigExportRemainsUnmasked guards ToJSON/ToJSONUnmanaged against ever being masked.
  • Manual smoke test of MaskConfigValue:
    SECRET_KEY           masked=true  "sk••••••••••••op"
    DATABASE_URL         masked=true  "postgres://user:••••••••@host:5432/db"
    REDIS_URL            masked=false "redis://host:6379"
    ENVIRONMENT          masked=false "production"
    
  • Manual verification of the CLI wiring:
    • apppack config get SECRET_KEY in a TTY → prints masked value + value masked — use --reveal to show on stderr.
    • apppack config get SECRET_KEY --reveal → plaintext.
    • apppack config get SECRET_KEY | cat / $(apppack config get SECRET_KEY) (non-TTY stdout) → plaintext, unchanged.
    • apppack config list → masked table + N value(s) masked — use --reveal to show hint on stderr; --reveal and non-TTY behave the same as get.
    • apppack config export / --json → always plaintext, untouched by this change.

apppack config get/list printed secret values (SECRET_KEY, DATABASE_URL,
API tokens, etc.) in plaintext with no guardrails, leaking into shell
scrollback, CI logs, and screen shares. Add a best-effort, name-based
heuristic that masks values in interactive terminals only, with --reveal
to opt out. Piped/scripted usage, --json, and config export stay
unmasked so automation keeps working unchanged.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

1 participant