| Version | Supported |
|---|---|
| 0.1.x | yes — the only line, and pre-release |
The project is early: not on PyPI, and never run in production. There is no
backport branch, so a fix lands on main.
Use GitHub's private vulnerability reporting: Security → Report a vulnerability on https://github.com/vipierozan99/sqlom. That keeps the report private until there is something to release. Please do not open a public issue for anything exploitable.
Include what you'd want to receive: the statement or declaration involved, the
driver (aiosqlite, asyncpg, psycopg), and the versions of
Python, SQLAlchemy and the driver.
rowform/compile.py builds a hydrator function per statement shape as source text
and execs it. That is the core of the design, so it deserves a plain statement of
its boundary.
Everything interpolated into that source comes from your own model declarations, never from a query result or a request:
- attribute names come from
Mapped[]field names, which Python has already validated as identifiers, - model classes and each column's
result_processorare inserted into the namespace as objects, not as text, - row values are never interpolated — they arrive as arguments to the generated
function at call time, exactly as they would through
Row.
So a column's contents cannot reach the generated source, and neither can anything
a caller passes as a bind parameter. What is in scope: if you build model classes
dynamically from untrusted input — a field name taken from an HTTP request, say —
you are choosing what goes into generated code, and the same caution applies as
with type() or dataclasses.make_dataclass.
The generated source is on hydrate.__source__ and logged at DEBUG, so you can
always read what was built.
rowform generates no SQL. Statements are compiled by SQLAlchemy Core and executed
as parameterised queries, with values bound through the driver — so the usual
guidance applies unchanged: keep user input in bind parameters and out of
sa.text() fragments and identifiers you interpolate yourself.
One rowform-specific note: Connection.exec_driver_sql() takes a raw SQL string,
for the DDL and session state a statement object cannot express. It is not
compiled and its parameters go to the driver as-is, so anything you interpolate
into the string itself is unescaped. Do not build those strings out of untrusted
input. execute() and the fetch_* methods take statement objects, not strings,
and are not a route to this.