Security advisory — 0.18¶
Affected: 0.18.13 and every release before it. Partly fixed in: 0.18.14, released 2026-09-15.
The 0.18 line is in maintenance. It received a subset of the work described in the 1.1 advisory, chosen for what could be backported without changing behaviour the line's users depend on.
1.x is where security work lands first, and in full. If the choice is open to you, upgrade rather than staying on 0.18.
It is also worth being plain about what 0.18 is: every release before 1.0.0 was
published as Beta (Development Status :: 4 - Beta), and this line still
carries that classifier. 1.1.0 is the current stable release. The upgrade
crosses 1.0.0, so read the breaking changes and the
CHANGELOG before
starting — that is the work this page is asking you to do, and it is smaller
than the list below.
What 0.18.14 fixes¶
- The transaction that committed instead of rolling back when a Python exception escaped the block.
Field.set()comparators, now validated before reaching the WHERE clause.- Function and procedure names, and the keys of named parameters, checked before interpolation.
config_fileconfined to its directory, and the reconnect guard extended to the branch that skipped it.ho_copy()andho_acopy()column names checked against the relation.- Relation names quoted properly, and no longer conflated with one another.
- The
eval()inhotest.py, replaced by an attribute path walk. - The CLI extension trust store moved out of the project directory, and
half-orm-test-extensionremoved from the allowlist of automatically trusted names. - Script injection through the documentation workflow's dispatch inputs.
What it does not fix¶
These remain open on 0.18, and upgrading to 1.1 is the only remedy:
order_byand thejson_aggprojection are still interpolated unchecked. If your application derives either from a request — a?sort=parameter, a field list from an API client — that path is exploitable, and it is the most reachable defect in this list.Model._import_class()still builds an import path from schema names read out of the database.- Query parameters are still written to the log when a query fails.
- Relation aliases still carry
id(self), a heap address. ho_cast()still widens silently to an ancestor's whole extension.- The extension trust work beyond the store's location — loading on first
use rather than at import, checking that the module belongs to the
distribution vouching for it, pinning approval to a digest of the code, and
HALF_ORM_TRUST_EXTENSIONSfor unattended runs — is 1.1 only. On 0.18,--trusted-extensionsand--untrustare still parsed after the extensions have loaded.
What to do if you must stay on 0.18¶
Upgrade to 0.18.14 first. Then, for what it does not cover:
- never build
order_by, ajson_aggprojection or an alias from input you do not control — map the user's choice onto a name your own code holds; - treat the database you connect to as trusted, since its schema names still select Python modules to import;
- do not rely on a failing query keeping its parameters out of your logs;
- approve CLI extensions deliberately, and re-check them after any reinstall.
Credit¶
Found during an internal review of the 1.0.0 release, and backported from 1.1.0.