Skip to content

fix(postgis): patch CVE-2026-73514 / CVE-2026-73515 and gate vulnerable builds - #16

Merged
pyramation merged 2 commits into
mainfrom
feat/postgis-cve-2026-73514-73515
Aug 16, 2026
Merged

fix(postgis): patch CVE-2026-73514 / CVE-2026-73515 and gate vulnerable builds#16
pyramation merged 2 commits into
mainfrom
feat/postgis-cve-2026-73514-73515

Conversation

@pyramation

Copy link
Copy Markdown
Contributor

Summary

The published constructiveio/postgres-plus:18 ships PostGIS 3.6.2, and that build is exploitable from plain SQL today — on the current image, an 8-byte bytea takes the backend down:

postgres=# CREATE TEMP TABLE fgb_probe (geom geometry, name text);
postgres=# SELECT ST_FromFlatGeobuf(NULL::fgb_probe, decode('6667620366676201','hex'));
server closed the connection unexpectedly

That matters beyond the image, because postgis is a hard requires of the constructive and metaschema extensions, PostGIS grants EXECUTE on ST_FromFlatGeobuf to PUBLIC, and both proxy SQL-filter presets allow the call. So the decoder is reachable by any tenant with SQL-lane access.

Fix is POSTGIS_VERSION 3.6.2 → 3.6.4 plus patches/, because no released tarball carries either fix — not 3.6.4, and not the 3.7.0beta2 line for the standardizer. Each patch is a git format-patch of the upstream commit cherry-picked onto the 3.6.4 tag:

Patch Provenance
0001-flatgeobuf-validate-input-buffers-before-decoding postgis/stable-3.6 53e273fae, landed after 3.6.4 — CVE-2026-73515. 3.6.4's release note ("Flatgeobuf schema mismatch vulnerability") is the earlier 70d46a679, which does not cover truncated buffers.
0002-address_standardizer-harden-scanner-and-rule-parsing stable-3.6 1ce0012b7; verified byte-equivalent (one blank line) to eda92ce/423570b in the split-out postgis/address_standardizer repo, which is where the CVE-2026-73514 fix now lives since master dropped the in-tree extension.
0003-address_standardizer-clean-up-partial-2D-allocations stable-3.6 c38c87319.
0004-Avoid-out-of-bounds-write-uninitialized-memory 5964d6f1a, the actual off-by-one: parse_rule() wrote rule_arr[nr] before the bound test, so a 129-token rule wrote past rule_arr[MAX_RULE_LENGTH]. Present only on stable-3.2/3.3, i.e. absent from every 3.6 tarball.

Two conflicts in 0001 were resolved in favour of 3.6.4's datum semantics while taking the fix's endian-safe reads (UInt32GetDatum and the FLOAT8OID coercion kept; memcpyflatgeobuf_read_le32/flatgeobuf_read_float_le).

patch runs without --force, so a patch that stops applying fails the build instead of being silently skipped — the failure mode that matters when the tarball moves.

The gate

scripts/verify-postgis-security.sh <container> interrogates the installed extension, never a Dockerfile ARG or a tag (tags move, and the version alone can't distinguish a patched 3.6.4 from a plain one):

  1. pg_extension.extversion >= POSTGIS_MIN_VERSION (3.6.4),
  2. the patch manifest baked into the image at /usr/local/share/postgresql/security/postgis-patches.txt records both CVE ids,
  3. the truncated-buffer probe above errors and SELECT 1 still answers on a fresh connection — the functional proof, and what fails loudly on an unpatched build,
  4. standardize_address() still returns a parse, so the standardizer hardening didn't break it.

Wired into make test and into CI on both paths: on a PR the amd64 build is loaded and verified; on push it runs against the pushed digest before publish-postgres-plus-manifest, so a vulnerable build can never have latest/18 moved onto it.

Verified locally: make test passes on the patched image, and the same script fails on the published one with FAIL: postgis 3.6.2 is below the patched floor 3.6.4.

Merging and republishing 18 is what actually remediates the platform; a follow-up PR in constructive-db adds a matching gate and blocks the FlatGeobuf decoders in the proxy presets as defence in depth.

Link to Devin session: https://app.devin.ai/sessions/ef16f622f23747b38e801ae0aa1ae1a7
Requested by: @pyramation

Build PostGIS from 3.6.4 with the four upstream security commits applied as
patch files, and gate every build on a check that the installed extension is
not vulnerable.
@pyramation pyramation self-assigned this Aug 16, 2026
@devin-ai-integration

Copy link
Copy Markdown

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@pyramation
pyramation merged commit 212c13e into main Aug 16, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant