An HMAC over each stored snippet, so code that did not come through this application does not run.
What it buys, and it is one thing
Every future arbitrary-database-write bug in this CMS — an injection, a mis-scoped Ash action, a
restored dump somebody tampered with — currently ends in remote code execution, because whatever is
in events, infos or helpers is compiled into the module that serves the site. With signing
on, such a write produces code that does not run: the signature will not match, and the
compiler emits an inert clause instead.
It is not a permission check and does not replace one. The resource's policies decide who may write; this decides whether what is stored was written by this application at all.
The page still renders
A snippet whose signature is missing or wrong is replaced by a clause that does nothing, and the rest of the document compiles and serves as normal. The alternative — refusing to compile — turns a tampered helper into a site-wide outage, which hands whoever tampered with it a better outcome than they had.
The cost, stated plainly
A database restored into another environment runs no page code until it is re-signed. The key
is per-deployment, so a production dump loaded into staging carries signatures staging cannot
verify. That is the feature working, and it will read as a bug the first time somebody hits it —
mix mishka.resign_code is the answer, and the log line says so by name.
Off unless a key is configured
No key, no signing and no verification: enabled?/0 is false, and everything behaves exactly as it
did before this module existed. A deployment opts in by setting one, which is the only way to add
this to an existing database without every page stopping at once.
config :mishka_cms_core, :code_signing_key, System.get_env("CODE_SIGNING_KEY")
Summary
Functions
Whether this deployment signs and verifies stored code.
One snippet with its signature attached — what the resources store.
The signature for one snippet's code, or nil when signing is off.
Whether a stored snippet carries a signature this deployment made — what the compiler asks before it turns the snippet into a clause.
Whether signature is this deployment's signature for code.
Functions
@spec enabled?() :: boolean()
Whether this deployment signs and verifies stored code.
False when no key is configured, which is the default and which makes every other function here a no-op — an existing database keeps working until somebody deliberately turns this on.
One snippet with its signature attached — what the resources store.
A map with string keys keeps whatever it had; the key is added under the same kind of key the map already uses, so a snippet that arrived from JSON:API and one that arrived from the editor come out looking the same.
The signature for one snippet's code, or nil when signing is off.
Normalized first, because a save that only changes indentation must not invalidate the signature
and, more importantly, because the compiler has to compute the same digest from the same text —
\r\n from a paste and trailing spaces from an editor are the two ways that goes wrong.
Whether a stored snippet carries a signature this deployment made — what the compiler asks before it turns the snippet into a clause.
Whether signature is this deployment's signature for code.
Answers true when signing is off — with no key there is nothing to verify, and the compiler must behave exactly as it did before. Compared in constant time, because a signature is a secret-derived value and a byte-at-a-time comparison leaks where it stopped matching.