Subchapter 63.18
references/input-validation.mdMarkdown6 KBView on GitHub
Part of the Aurora DSQL Skill.
When constructing SQL strings (for psql -c "...", ad-hoc shell pipelines, or any code path
that does not use the driver’s native parameter binding), build every query with the
helper. Do not interpolate values into SQL with
f-strings, , , or concatenation.
%.format()When using a Postgres driver in application code (psycopg, pgx, sqlx, JDBC, etc.), prefer the
driver’s native parameter binding (%s for psycopg, $1 for pgx, ? for JDBC). safe_query
is the canonical fallback whenever you must build a raw SQL string.
from safe_query import build, allow, regex, ident, keyword, integer, literal, UnsafeSQLError
from safe_query import TENANT_SLUG, UUID, ISO_DATE
sql = build(
"SELECT * FROM {tbl} WHERE tenant_id = {tid} AND entity_id = {eid}",
tbl=ident("entities"),
tid=regex(tenant_id, TENANT_SLUG),
eid=regex(entity_id, UUID),
)
# Pass `sql` to your driver: cur.execute(sql), conn.Query(ctx, sql), psql -c "$sql", etc.build() raises UnsafeSQLError when a placeholder receives a raw string, so
build("... {x} ...", x=user_input) fails loudly at the call site.
| Value kind | Validator | Emits |
|---|---|---|
| Known set (tenant ID, status enum) | allow(v, SET) | 'value' |
| Known set used as SQL keyword | keyword(v, SET) | value (unquoted) |
| Strict format (UUID, slug) | regex(v, PATTERN) | 'value' |
| Table or column name | ident(name) | "value" |
| Integer | integer(v) | value |
| Free text (description, comment) | literal(v) | $dq_xxx$value$dq_xxx$ |
Built-in patterns in safe_query.py: TENANT_SLUG ([a-z0-9-]{1,64}),
UUID, INT, ISO_DATE (structurally validated YYYY-MM-DD — month 01–12, day
01–31; does NOT check month-day combinations such as Feb 30. The database will
reject impossible calendar dates at execute time).
Format validation proves the value is shaped correctly. It does not prove the
caller is allowed to act on it. Authorize the caller against the tenant or
resource before validating format or calling build():
assert_caller_has_tenant_access(caller, tenant_id) # authorization
sql = build("... WHERE tenant_id = {tid}", tid=regex(tenant_id, TENANT_SLUG))psql -c "..." and many shell-driven pipelines accept only SQL strings — there is no
parameter-binding facility at that layer.safe_query.build() is the canonical way to do that without opening an injection vector.ident() is the
only safe way.safe_query.build().%, .format(), or concatenation when
a validator rejects a value — fix the caller or widen the validator.UnsafeSQLError to recover silently. Re-raise or return
an error to the caller.safe_query.py rather than inlining regex at
call sites, so reviewers can audit them in one place.Bash deliverables (CI/CD steps, cron jobs, bastion runbooks) apply the same
rule. ALWAYS compose the SQL in a python3 subshell so safe_query.py
actually executes. MUST NOT rely on native reference alone. MUST invoke to defend against injection.
SQL=$(python3 - "$USER_ID" <<'PY'
import sys
from safe_query import build, regex, literal, TENANT_SLUG
print(build(
"INSERT INTO audit_log (action, user_id) VALUES ({a}, {u})",
a=literal("psql-write-smoke"),
u=regex(sys.argv[1], TENANT_SLUG), # raises on malformed input
))
PY
)
PGPASSWORD=$(aws dsql generate-db-connect-admin-auth-token --hostname "$HOST" --region "$REGION") \
psql -P pager=off "host=$HOST port=5432 user=admin dbname=postgres sslmode=verify-full sslrootcert=system" <<<"$SQL"regex() raises before psql runs; literal() escapes the validated
value into the final SQL text.
ALWAYS explicitly set SSL mode — DSQL rejects non-TLS connections
and psql’s default PGSSLMODE=prefer will attempt plaintext first.
Two equivalent forms, both valid per the
AWS DSQL psql guide (opens in a new tab):
export PGSSLMODE=verify-full PGSSLROOTCERT=system
before the psql invocation, with flag-form arguments
(psql --username admin --dbname postgres --host $HOST).sslmode=verify-full sslrootcert=system in a libpq URI
(psql "host=$HOST port=5432 user=admin dbname=postgres sslmode=verify-full sslrootcert=system").Use sslmode=verify-full sslrootcert=system (matches psql-connect.sh‘s default and what the
Java/Rust/Python connectors enforce). Drop to sslmode=require only when the client genuinely
cannot reach a trusted CA bundle — and document the downgrade in the runbook.