Skip to content

Issue #938: Redact DATABASE_URL password before logging at startup#951

Open
bjagg wants to merge 1 commit into
LIF-Initiative:mainfrom
bjagg:fix/938-redact-database-url-log
Open

Issue #938: Redact DATABASE_URL password before logging at startup#951
bjagg wants to merge 1 commit into
LIF-Initiative:mainfrom
bjagg:fix/938-redact-database-url-log

Conversation

@bjagg
Copy link
Copy Markdown
Contributor

@bjagg bjagg commented May 27, 2026

Summary

database_setup.py was logging the full DATABASE_URL (with PG password) on every MDR API task startup. Shared CloudWatch retention means anyone with logs:FilterLogEvents could recover the dev/demo DB credential. Closes #938.

Fix

Small _redact_url(url) -> str helper using urlparse/urlunparse:

-logger.info(f"DATABASE_URL : {DATABASE_URL}")
+logger.info("DATABASE_URL : %s", _redact_url(DATABASE_URL))

Output before:

DATABASE_URL : postgresql+asyncpg://postgres:sNa22:}33eS$u:b&X_XdZu!v~4<$r.P2@devmdrdb.dev.aws:5432/devMdrDb

Output after:

DATABASE_URL : postgresql+asyncpg://postgres:***@devmdrdb.dev.aws:5432/devMdrDb

Helper is best-effort: any parsing surprise (malformed URL, missing env var producing a non-integer port) returns a <unparseable-url> sentinel rather than raising — a logging path should never take down MDR startup.

Tests

First tests under test/components/lif/mdr_utils/ (new directory):

  • _redact_url covers typical password redaction, special characters in the password, no-port URLs, no-password URLs (IAM auth), and unparseable input.
  • A small conftest.py seeds dummy POSTGRESQL_* env vars so importing database_setup doesn't fail at engine-construction time (these pure-Python tests never touch a real DB).

uv run pytest test/components/lif/mdr_utils/ — 5 passed.

Follow-up (out of scope here)

  • Rotate dev + demo DB passwords. Previous values are sitting in CloudWatch retention.
  • Audit other LIF services that share this logging pattern: graphql_*, translator_*, advisor_api, etc. Same pattern, same fix shape.

Both worth filing as separate issues once this lands.

Risk

Very low. Pure-Python helper added, single log line changed from f-string to lazy-format with redaction. No behavior change for the engine itself — create_async_engine still gets the raw DATABASE_URL (it needs the credential to actually connect).

🤖 Generated with Claude Code

… at startup

`database_setup.py` was logging the full `DATABASE_URL` (including the
PG password) on every MDR API task startup. The shared `dev`/`demo`
CloudWatch log group has broader read permissions than the SSM
parameter that backs the password, so anyone with `logs:FilterLogEvents`
could recover the credential by tailing the stream during startup —
and every restart leaves another copy in retention history.

Add a small `_redact_url` helper that uses `urlparse`/`urlunparse` to
swap the password segment for `***` while preserving scheme, username,
host, port, and database. The helper is best-effort: any parsing
surprise (malformed URL, port that isn't an integer because an env
var was unset) returns a `<unparseable-url>` sentinel rather than
raising, so a logging path can never take down MDR startup.

Adds the first tests under `test/components/lif/mdr_utils/`:
- `__init__.py` (package marker matching siblings)
- `conftest.py` seeds dummy POSTGRESQL_* env vars so importing
  `database_setup` doesn't fail at engine-construction time (these
  pure-Python helper tests never touch a real DB)
- `test_database_setup.py` — five cases for `_redact_url` covering
  typical password redaction, special characters, missing port,
  no-password URLs (e.g. IAM auth), and unparseable input.

Operational follow-up (out of scope here): rotate the dev + demo DB
passwords since the previous values are in CloudWatch history. Other
LIF services that share this logging pattern (graphql_*, translator_*,
advisor_api, etc.) are also flagged for an audit pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

MDR API logs DATABASE_URL with plaintext credentials at startup

1 participant