KB-6F3C
Fix 1 — Injection / Shell-Safety
3 min read Revision 1
c1stagingcodex-r1-fixinjection2026-06-23
03 — FIX 1: INJECTION / SHELL SAFETY (_common.sh)
Root cause (Codex A13)
Rev-1 ran docker exec postgres sh -lc "psql … $* …". The $* joined the caller's psql args
(-v purpose="…" -v owner="…" -v ttl="…") into a single string that the container sh -lc
RE-PARSED. User-controlled purpose/owner/ttl could become shell syntax inside the container.
Fix
psql is now invoked as explicit argv directly (no shell on the container side):
docker exec "${PG_CONTAINER}" psql -U "$(stg_pg_user)" -v ON_ERROR_STOP=1 "$@" -d "$db" -f "$rmt" </dev/null
"$@"passes every caller arg as a separate argv word to psql — never re-split by a shell.stg_pg_userresolves the role once viadocker exec postgres printenv POSTGRES_USER(fixed args; no user input) →directus. Not hardcoded.- Temp files are created inside the container with
mktemp /tmp/c1stg.XXXXXXXXXX(unpredictable) and tracked inSTG_REMOTE_TMPS; atrap stg_cleanup_remote_tmps EXITguarantees removal even on failure. stg_scalarandstg_drop_dbuse the same argv-safe pattern.- New
stg_assert_ttlconstrains TTL to^[0-9]+[hd]$before use.
Why user data can no longer reach a shell or SQL
- purpose/owner/ttl arrive at psql as literal
-v name=valueargv words → used in SQL as:'name'(psql-quoted literal). No shell, no SQL injection. - sandbox names are regex-validated
^c1_staging_[0-9]{8}_[0-9]{4}$before any interpolation, so${SBX}embedded in scalar SQL cannot contain quotes/metacharacters. stg_drop_dbasserts the sandbox name then embeds it viaprintf '… "%s" …'— digits-only, no injection.
Static proof (deployed files)
grep -RnE '\$[*@]' bin/ -> ONLY: _common.sh:76 docker exec … psql … "$@" … (quoted argv passthrough)
grep -RnE 'eval|sh -c|sh -lc|bash -c' bin/ (non-comment) -> (none)
grep -RnE 'docker exec.*sh -lc|.*sh -c' bin/ (non-comment) -> (none)
Guard self-tests (pure-bash helpers, NO primitive invoked, NO DB op):
stg_assert_sandbox_name directus -> rc=4 (refused off-limits/regex)
stg_assert_sandbox_name postgres -> rc=4
stg_assert_sandbox_name c1_staging_2026 -> rc=4 (bad format)
stg_assert_sandbox_name c1_staging_20260623_0711 -> rc=0
stg_assert_sandbox_name 'c1_staging_..._0711; DROP DATABASE directus' -> rc=4 (injection-style refused)
stg_assert_ttl 24h / 7d -> rc=0
stg_assert_ttl 99x / 24 -> rc=4
bash -n OK; shellcheck CLEAN. sha256 bin/_common.sh = 1b2d13d0604e500136526f09eafec75ba90648508a6788146cd53e02bef57f8c.