Skip to content

Fix geometry type: perf, nil-limit crash, adapter pollution, and DDL hardening#6

Merged
waratuman merged 3 commits into
masterfrom
waratuman/security-performance-review
Jul 15, 2026
Merged

Fix geometry type: perf, nil-limit crash, adapter pollution, and DDL hardening#6
waratuman merged 3 commits into
masterfrom
waratuman/security-performance-review

Conversation

@waratuman

Copy link
Copy Markdown
Owner

Summary

Security & performance review of the PostGIS geometry type and adapter. Four fixes, ranked by impact.

1. Perf — RGeo factory rebuilt on every read

oid/geometry.rbcast allocated a fresh GEOS factory (and serialize/parsers) for every geometry value loaded from the DB, even though the SRID is fixed per column. The factory, WKB/WKT parsers, and WKB generator are now built once in the initializer. Eager construction (rather than lazy ||= memoization) is also required because ActiveRecord freezes Type instances after initialization — lazy memoization raised FrozenError in Rails 8.1 (verified).

2. Correctness — bare geometry column crashed on read

A column defined as plain geometry (no geometry(Type,srid) modifier) has no limit, so limit[:srid] raised NoMethodError on read. Added a srid helper defaulting to 0 (PostGIS "unknown SRID").

3. Bug — merge! polluted the parent PostgreSQL adapter

NATIVE_DATABASE_TYPES = PostgreSQLAdapter::NATIVE_DATABASE_TYPES.merge!(...) mutated the parent adapter's hash in place, leaking :geometry into the vanilla postgresql adapter (and would raise FrozenError if that constant were frozen). Changed to non-mutating merge.

4. Security (low) — unvalidated interpolation into DDL

type_to_sql interpolated srid straight into the geometry(...) type modifier. Now coerced with to_i, so it can't carry arbitrary SQL text. (type is still interpolated; it's the developer-supplied geometry type name.)

Verification

Exercised end-to-end against a real PostGIS database (all pass):

  • srid sanitization: type_to_sql(:geometry, limit: { srid: "4326); DROP TABLE geos;--" })geometry(Point,4326)
  • typed Point create/reload round-trip
  • bare geometry column create/reload (nil-limit path, no crash)
  • factory built eagerly on the frozen type instance
  • parent PostgreSQLAdapter::NATIVE_DATABASE_TYPES no longer contains :geometry

Note: the rake test task currently fails to load in this environment due to a pre-existing incompatibility between minitest 6 and this ActiveSupport's autorun (minitest/rails_plugin missing) — unrelated to these changes; verification was done via a direct script exercising the same code paths.

🤖 Generated with Claude Code

waratuman and others added 3 commits July 15, 2026 10:42
…hardening

- Build the RGeo factory, WKB/WKT parsers, and WKB generator once in the
  Geometry type's initializer instead of rebuilding them on every cast/
  serialize. A GEOS factory was previously allocated per value read; it is
  invariant per column. Eager construction (vs. lazy memoization) is also
  required because AR freezes Type instances after initialization.
- Guard the nil-limit path: a bare `geometry` column (no type/srid modifier)
  has no limit, so `limit[:srid]` raised NoMethodError on read. Add a `srid`
  helper defaulting to 0 (PostGIS "unknown SRID").
- type_to_sql: coerce srid with `to_i` so it cannot carry arbitrary text into
  the geometry() type modifier.
- Use `merge` instead of `merge!` for NATIVE_DATABASE_TYPES so the parent
  PostgreSQLAdapter's type map is not mutated with :geometry.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The test suite failed to load: ActiveSupport 8.1's
active_support/testing/autorun now calls `Minitest.load :rails`, which
requires railties' minitest/rails_plugin. This gem depends only on
activerecord (not the full Rails), so railties is absent from the bundle
and the require raised LoadError (surfaced by minitest 6 adding
Minitest.load). Use plain minitest/autorun, the conventional choice for a
non-Rails gem, instead of pulling in railties.

Also rename the duplicate `test_polygon_with_hole` (the second definition
silently shadowed the first) to `test_collection` so the geometry
collection case runs as its own test.

Add a CI workflow running the suite against a postgis/postgis service
container on Ruby 3.2-3.4, installing libgeos-dev before bundle install so
rgeo's CAPI extension compiles.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@waratuman waratuman merged commit 57df078 into master Jul 15, 2026
3 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