You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Split from #4046. Parts 1, 1b and 1c stop a planted line from making a target's log read fail (#4049 is merged; #4051 is in review):
a line stamped in another zone is skipped and counted;
a byte that is not valid UTF-8 is read through pg_read_binary_file instead of throwing 22021.
They don't stop forgery. That is this issue.
The gap
In the stderr log format, a role or database name from the startup packet lands unescaped in the FATAL message. The server writes it before authentication. A newline in that name starts a new line of the client's choosing, and the line can carry a fake timestamp, pid and label under any log_line_prefix that puts %u/%d before %p. The RDS default, %t:%r:%u@%d:[%p]:, is one such prefix. No stderr parser can tell that line from a real one.
Fix
When a target's log_destination includes csvlog or jsonlog (PostgreSQL 15+), the three log-tail collectors read that file instead of the stderr one:
pg_log_events
pg_deadlocks
pg_plan_capture
Today the tail CTE excludes \.(csv|json)$. The collectors need a real CSV/JSON parser. In those formats every field is quoted or escaped, so a planted newline stays inside its field.
Self-hosted route: the tail CTE picks the newest .csv/.json file. A tail window can start mid-record, and a CSV record can span lines, so drop everything up to the first complete record.
Managed route (RDS/Aurora): the log download API serves the .csv files when csvlog is on. Use the same parser.
Keep the stderr path as the fallback. The logging audit (get_pg_logging_audit) should recommend csvlog or jsonlog and give this reason.
Split from #4046. Parts 1, 1b and 1c stop a planted line from making a target's log read fail (#4049 is merged; #4051 is in review):
pg_read_binary_fileinstead of throwing 22021.They don't stop forgery. That is this issue.
The gap
In the stderr log format, a role or database name from the startup packet lands unescaped in the FATAL message. The server writes it before authentication. A newline in that name starts a new line of the client's choosing, and the line can carry a fake timestamp, pid and label under any
log_line_prefixthat puts%u/%dbefore%p. The RDS default,%t:%r:%u@%d:[%p]:, is one such prefix. No stderr parser can tell that line from a real one.Fix
When a target's
log_destinationincludescsvlogorjsonlog(PostgreSQL 15+), the three log-tail collectors read that file instead of the stderr one:pg_log_eventspg_deadlockspg_plan_captureToday the tail CTE excludes
\.(csv|json)$. The collectors need a real CSV/JSON parser. In those formats every field is quoted or escaped, so a planted newline stays inside its field..csv/.jsonfile. A tail window can start mid-record, and a CSV record can span lines, so drop everything up to the first complete record..csvfiles when csvlog is on. Use the same parser.get_pg_logging_audit) should recommend csvlog or jsonlog and give this reason.Also open, carried over from #4051
Nobody knows yet whether AWS's log download re-encodes or rejects a byte that is not valid UTF-8. Test it on a throwaway RDS instance.
Done when