The class-name door on every nippy thaw the engine runs over a file.
A frozen nippy value can name a class in three of its type ids, and reading one
resolves that name and builds an instance of it: a record frame (Class/forName, then
the static create), a deftype frame (Class/forName, then the first public
constructor over the fields that follow), and a Serializable frame (an
ObjectInputStream over the bytes that follow). nippy 3.8.1 gates the third behind
*thaw-serializable-allowlist* and the first two behind nothing at all.
Every file the engine reads is untrusted input — a store directory or a dump arrives from wherever an operator copied it — so all three are gated here, and the gate is the tightest one there is:
A vaelii frame never carries a class name.
That is already the export format's stated rule (vaelii.impl.io.export), and the
disk codec writes a record's fields positionally for a size reason
(vaelii.impl.disk.codec), so nothing the engine writes states a class either. A
frame that names one therefore came from somewhere else, and allowed-classes is
empty: the name is refused (:disallowed-class) before the class is resolved.
The Serializable allowlist is pinned rather than inherited. nippy's default is
a safe set, but it lives in a dynamic var an embedding application is invited to
widen — nippy documents allow-and-record-any-serializable-class-unsafe for exactly
that migration — and a host that widened it would widen the engine's file readers with
it. Binding it per read makes the door this namespace's rather than the host's.
How the first two are gated, since nippy exposes no hook for them: the readers
taoensso.nippy.io dispatches to are vars, and this namespace installs a checked
reader in place of each. A refusal is thrown from there, which is before nippy's own
try — so it travels rather than becoming the {:nippy/unthawable …} placeholder a
failed resolution otherwise reads as, and no class is loaded on the way. The
replacement calls straight through to the reader it replaced unless with-guard is in
force, so a host application thawing its own records in this JVM is unaffected.
The class-name door on every nippy thaw the engine runs over a file.
A frozen nippy value can **name a class** in three of its type ids, and reading one
resolves that name and builds an instance of it: a record frame (`Class/forName`, then
the static `create`), a deftype frame (`Class/forName`, then the first public
constructor over the fields that follow), and a `Serializable` frame (an
`ObjectInputStream` over the bytes that follow). nippy 3.8.1 gates the third behind
`*thaw-serializable-allowlist*` and the first two behind nothing at all.
Every file the engine reads is **untrusted input** — a store directory or a dump
arrives from wherever an operator copied it — so all three are gated here, and the
gate is the tightest one there is:
> **A vaelii frame never carries a class name.**
That is already the export format's stated rule (`vaelii.impl.io.export`), and the
disk codec writes a record's fields **positionally** for a size reason
(`vaelii.impl.disk.codec`), so nothing the engine writes states a class either. A
frame that names one therefore came from somewhere else, and `allowed-classes` is
empty: the name is refused (`:disallowed-class`) before the class is resolved.
**The `Serializable` allowlist is pinned rather than inherited.** nippy's default is
a safe set, but it lives in a dynamic var an embedding application is invited to
widen — nippy documents `allow-and-record-any-serializable-class-unsafe` for exactly
that migration — and a host that widened it would widen the engine's file readers with
it. Binding it per read makes the door this namespace's rather than the host's.
**How the first two are gated**, since nippy exposes no hook for them: the readers
`taoensso.nippy.io` dispatches to are vars, and this namespace installs a checked
reader in place of each. A refusal is thrown from there, which is *before* nippy's own
`try` — so it travels rather than becoming the `{:nippy/unthawable …}` placeholder a
failed resolution otherwise reads as, and no class is loaded on the way. The
replacement calls straight through to the reader it replaced unless `with-guard` is in
force, so a host application thawing its own records in this JVM is unaffected.Is a thaw inside with-guard? The checked readers below stand in for nippy's own
process-wide, so this is what keeps the gate the engine's own: false — a host
application's thaw — calls straight through.
Is a thaw inside `with-guard`? The checked readers below stand in for nippy's own process-wide, so this is what keeps the gate the engine's own: false — a host application's thaw — calls straight through.
The class names a file the engine wrote may state — none.
Measured rather than asserted: a store built on each of the four durable backends and
a dump written in each variant and codec drive nippy's three class-name readers zero
times. A dump frame is a field map, a log frame is a positional vector, and every
leaf a sentence may carry is a type nippy has an id for (vaelii.impl.checks'
check-encodable refuses the rest at the front door). So the allowlist is empty, and
a name is a name this engine did not write.
A set rather than false because the refusal names the class either way, and because
a format that one day carried one would add it here and nowhere else.
The class names a file the engine wrote may state — **none**. Measured rather than asserted: a store built on each of the four durable backends and a dump written in each variant and codec drive nippy's three class-name readers zero times. A dump frame is a field map, a log frame is a positional vector, and every leaf a sentence may carry is a type nippy has an id for (`vaelii.impl.checks`' `check-encodable` refuses the rest at the front door). So the allowlist is empty, and a name is a name this engine did not write. A set rather than `false` because the refusal names the class either way, and because a format that one day carried one would add it here and nowhere else.
(guarded f)Run f — a thunk that thaws — behind the door, raising a :disallowed-class refusal
as itself rather than as whatever wrapped it.
The entry point for a caller reading many frames off one stream
(vaelii.impl.io.frames): the door is opened once for the run instead of once per
frame, which is one binding frame for ten thousand of them.
Run `f` — a thunk that thaws — behind the door, raising a `:disallowed-class` refusal as itself rather than as whatever wrapped it. The entry point for a caller reading **many** frames off one stream (`vaelii.impl.io.frames`): the door is opened once for the run instead of once per frame, which is one binding frame for ten thousand of them.
(thaw bs)nippy/thaw of bs behind the door.
`nippy/thaw` of `bs` behind the door.
(thaw-from-in! in)nippy/thaw-from-in! from in behind the door.
`nippy/thaw-from-in!` from `in` behind the door.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |