03 — Shell / injection review (A1, A2, A7, A8, A15)
03 — Shell / injection review (rows 1-2, 28-29; attacks A1, A2, A7, A8, A15)
Static greps (authoritative copies on VPS)
grep -RInE 'eval|sh -c|sh -lc|bash -c|\$[*]' bin plan → only 3 hits, all COMMENT lines in _common.sh
grep -RInE 'docker exec.*sh -c|.*sh -lc' bin plan → only 1 COMMENT line
secrets grep (SYNC_SECRET|PASSWORD|SECRET|TOKEN|key…) → NO_SECRETS_FOUND
'directus' in code (bin/plan/sql) → 1 hit only: _common.sh:25 off-limits guard list
official table tokens in sql/ → only p5:48 in-sandbox isolation probe + p6:66 error string
APR/quorum/promotion path grep → NO_APR_QUORUM_PROMOTION_PATH
psql is always invoked as explicit argv: _common.sh:87 docker exec postgres psql -U "$(stg_pg_user)" -v ON_ERROR_STOP=1 "$@" -d "$db" -f "$rmt" </dev/null. No sh -lc, no eval, no $*. User values
(purpose/owner/ttl) ride as psql -v argv words; SQL files use psql :'var' / :"var" (psql-side
escaping). The sandbox id is regex-gated (^c1_staging_[0-9]{8}_[0-9]{4}$) BEFORE it is ever
interpolated into any SQL string.
Static parse
bash -n → 8/8 bins + plan OK. shellcheck -S warning (v0.9.0) → CLEAN for bin/* + plan.
Live hostile guard self-tests (no-write; each fails closed BEFORE any DB write)
Every case below stg_dies before the first DB contact (the two "absent" cases issue a single
read-only SELECT). Ran on VPS; post-fix re-run identical:
| case | input | exit | meaning |
|---|---|---|---|
| A1_create_rm_rf | --sandbox-id 'c1_staging_x; rm -rf /' |
4 | regex reject (A1 refuted) |
| A2_create_sqlinj | --sandbox-id "x'; DROP DATABASE directus;--" |
4 | regex reject (A2 refuted) |
| A7_drop_directus | drop --sandbox-id directus |
4 | off-limits/regex reject (A7 refuted) |
| A7_drop_postgres | drop --sandbox-id postgres |
4 | reject |
| A7_drop_template1 | drop --sandbox-id template1 |
4 | reject |
| A8_create_force | create … --force |
4 | --force DISABLED (A8 refuted) |
| create_no_args | create |
3 | ADMISSION_DENIED |
| create_bad_ttl | --ttl 99x |
4 | REFUSE_BAD_TTL |
| create_unknown_arg | --bogus |
2 | unknown arg |
| drop_no_args | drop |
3 | --sandbox-id required |
| vocab_bad_name | --sandbox-id 'bad name with spaces' |
4 | regex reject |
| verify_unknown_arg | --nope |
2 | unknown arg |
| vocab_absent_validname | --sandbox-id c1_staging_20990101_0000 |
5 | refuses absent sandbox (read-only) |
| drop_absent_validname | --sandbox-id c1_staging_20990101_0000 |
0 | idempotent NO_OP |
POST-TEST staging_DBs = 0 — the hostile harness created no database.
Conclusion
A1, A2, A7, A8, A15 are refuted by live execution evidence (exit codes) plus code reading. No
shell metacharacter, quote, semicolon, or whitespace can survive STG_SANDBOX_RE, and that assertion
runs before any DB op or SQL interpolation in all 7 scripts.