Skip to content

docs+feat(#52): injection-safety doc and router-module collision guard#60

Merged
milmazz merged 2 commits into
mainfrom
injection-doc-router-guard
Jul 8, 2026
Merged

docs+feat(#52): injection-safety doc and router-module collision guard#60
milmazz merged 2 commits into
mainfrom
injection-doc-router-guard

Conversation

@milmazz

@milmazz milmazz commented Jul 7, 2026

Copy link
Copy Markdown
Owner

Two remaining small checklist items from #52 (references, does not close — other items remain).

What

1. Injection-safety doc (docs/injection_safety.md)
Consolidates the typed-literal binding strategy that was previously spread across site comments:

  • what is bound as a $n parameter vs. emitted as a single-quote-escaped '<escaped>'::type literal, and why (Postgres coerces text into ranges/arrays/typed args only from an unknown-typed literal, not from a text-typed parameter);
  • how QueryExecutor.quote_type/1 constrains every templated cast (conservative charset, 400 on violation), including which cast site is introspection-only (Rpc.call_arg/2);
  • site-by-site map of QueryExecutor.bind/3, Rpc.call_arg/2, and the Bier.Mutation builders (insert_sql/upsert_sql/set_clause/where_clause, extract_expr), plus identifier quoting and parameterized GUCs;
  • notes the doc as an anchor for a future property/fuzz test over filter values.

Added to the ex_doc extras in mix.exs and pointed to from the README request-flow section (2 lines).

2. Router-module uniqueness at startup
Bier.RouterBuilder names the generated router Module.concat(conf.name, Router), so two live instances with distinct names that concat to the same module (e.g. A.B and :"A.B") silently redefined each other's router. Bier.start_link/1 now checks the new Bier.Registry.instance_names/0 (live instance supervisors only) and raises an ArgumentError naming both the new instance, the colliding module, and the owning instance. Keying on live registrations (not Code.ensure_loaded?) keeps a stop/restart of the same named instance booting, and a same-name double start still returns {:error, {:already_started, pid}}.

Verification

  • mix format --check-formatted, mix compile --warnings-as-errors, mix credo --strict, mix docs --warnings-as-errors, MIX_ENV=test mix compile --warnings-as-errors all pass locally.
  • Guard exercised DB-free via mix run: collision raises the expected message; same-name start passes the guard and hits already_started as before.
  • New integration test test/bier/router_module_collision_test.exs boots real instances (collision raises; stop/restart of the same name boots while the router module still exists). CI runs the full suite.

🤖 Generated with Claude Code

milmazz and others added 2 commits July 6, 2026 23:00
Consolidate the injection-safety reasoning that lives as site comments in
QueryExecutor.bind/3, Rpc.call_arg/2 and the Bier.Mutation SQL builders
into docs/injection_safety.md: what is bound as a $n parameter vs. emitted
as a single-quote-escaped literal, why Postgres unknown-typed literal
contexts (ranges, arrays, typed comparisons, RPC args) force the latter,
and how QueryExecutor.quote_type/1 constrains the templated casts. Notes
the doc as an anchor for a future property/fuzz test over filter values.

Ships with the docs via the ex_doc extras list; the README request-flow
section gains a one-line pointer.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bier.RouterBuilder names the generated router Module.concat(conf.name,
Router), so two live instances whose distinct names concat to the same
module (e.g. A.B and :"A.B") silently redefine each other's router on
every rebuild. Bier.start_link/1 now checks the new
Bier.Registry.instance_names/0 (bare-atom keys, i.e. live instance
supervisors) for another instance owning the same router module and
raises a clear ArgumentError naming both instances and the module.

Keying on live registrations — not on the module being defined — keeps a
stop/restart of the same named instance booting, and a same-name double
start still surfaces as {:error, {:already_started, pid}} as before.
Covered by an integration test that boots real instances against the
test DB.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@milmazz
milmazz merged commit ae61ad6 into main Jul 8, 2026
3 checks passed
@milmazz
milmazz deleted the injection-doc-router-guard branch July 8, 2026 06:26
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