Skip to content

Report JSON that is not an Ink story instead of aborting - #166

Open
jerrytron wants to merge 1 commit into
JBenda:masterfrom
choosatron:fix/reject-non-story-json
Open

Report JSON that is not an Ink story instead of aborting#166
jerrytron wants to merge 1 commit into
JBenda:masterfrom
choosatron:fix/reject-non-story-json

Conversation

@jerrytron

Copy link
Copy Markdown

json_compiler::compile() reached for inkVersion and root with operator[]. On a const nlohmann::json a missing key is assert(it != end()), so with assertions enabled the process aborts, and with them disabled it dereferences an end iterator. compile_container() then took rbegin() and end() - 1 without checking the container was a non-empty array.

Anything at all can be handed to a compiler that accepts "a .json file" — a settings file, an API response, a package.json. Today that is fatal rather than reportable.

Measured

18 malformed inputs against the current master:

input result
object with no inkVersion or no root, {} abort (4 cases)
"root": [], "root": {} SIGSEGV (2 cases)
"root": "x", "root": 42 silently emits a bogus 64-byte binary
non-JSON, top-level array/string/number already clean

This change

Guarded lookups at the entry, plus one non-empty-array check in compile_container() that covers the root and every nested container in a single place. Both raise ink_exception, which every other failure in this compiler already uses, so callers can report the problem instead of losing the process.

All 18 now return a message. 77 real stories compile to byte-identical output, and ctest passes.

Where it was found

An embedded target, where the abort was a panic and a reboot, and a WebAssembly build, where it killed the module for the rest of the page load.

I have not added a Catch2 case for this — happy to if you would like one; it would fit as a compiler test asserting ink_exception on a handful of non-story documents.

Anything at all can be handed to a compiler that accepts "a .json
file": a settings file, an API response, a package.json. Reaching for
the top-level keys with operator[] made that fatal.

On a const nlohmann::json a missing key is assert(it != end()), so with
assertions enabled the process aborts, and with them disabled it
dereferences an end iterator. compile_container() then took rbegin()
and end() - 1 without checking the container was a non-empty array.

Measured over 18 malformed inputs, before this change: 4 aborted
(missing inkVersion or root, and {}), 2 segfaulted (a root of [] or
{}), and 2 silently emitted a bogus 64-byte binary from a scalar root.

Now they all raise ink_exception, which every other failure in this
compiler already uses, so a caller can report the problem instead of
losing the process. The container guard covers the root and every
nested container in one place.

Reported on an embedded target where the abort was a panic and a
reboot, and in a WebAssembly build where it killed the module for the
rest of the page.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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