Code quality plan, MR D: security defaults

Phase D of the code quality action plan (https://gitlab.com/postgres-ai/database-lab/-/blob/docs/code-quality-action-plan/docs/plans/20260913-code-quality-assessment-action-plan.md). One MR.

Security defaults confirmed on origin/master 8a42a6ef.

Findings

  • upgradeImageAllowList empty means any image (internal/srv/upgrade.go:206, all example configs)
  • --dbname unquoted in the sh -c dump-and-restore path (logical/dump.go:821); name comes from pg_database
  • PhysicalEnvs not sensitive; not in the YAML mask list, so WAL-G / pgBackRest credentials show up in config views
  • http.Server without timeouts (srv/server.go:331, runci/server.go:66); no MaxBytesReader; ReadJSON echoes the request body into the error
  • dblabapi and webhook http.Client without Timeout (#720)

Tasks

  • 13. Empty allow-list resolves to the repository of the clone image (containerOptions.DockerImage plus clone.DockerImage override), not the pg_upgrade image; ["*"] keeps the old behavior; changelog entry, five example configs updated. Intentional breaking default.
  • 14. shellQuote every value joined into the dump-and-restore string; narrow fix, #728 owns the argv-exec migration
  • 15. groups:"sensitive" on PhysicalEnvs; mask retrieval.spec.physicalRestore.options.envs values in the YAML view
  • 16. ReadHeaderTimeout / ReadTimeout / IdleTimeout on both servers (no WriteTimeout, streaming endpoints); MaxBytesReader at 1 MiB with a 413 mapping at the 17 ReadJSON call sites; client Timeout from --request-timeout, with DownloadArtifact on a separate client so streamed bodies are not truncated

Related

  • #728 command-injection hardening
  • #720 engine connection timeout