Parser does not stay in REJECT state after returning WeaselJson_REJECT #12

Closed
opened 2026-06-18 23:20:27 +00:00 by weaselbot · 0 comments
Member

The parser is not sticky after it returns WeaselJson_REJECT. If an invalid byte arrives in one chunk and the caller later calls WeaselJsonParser_parse(parser, nullptr, 0) to finish, the parser can return WeaselJson_OK and make the malformed input look valid.

Minimal reproduction (C API):

WeaselJsonCallbacks c = { /* all no-op callbacks set */ };
WeaselJsonParser *p = WeaselJsonParser_create(1024, &c, NULL, 0);
char a[] = "1";
char b[] = "x";
printf("chunk1 %d\n", WeaselJsonParser_parse(p, a, 1));   // 1 = AGAIN
printf("chunk2 %d\n", WeaselJsonParser_parse(p, b, 1));   // 2 = REJECT
printf("final  %d\n", WeaselJsonParser_parse(p, NULL, 0)); // 0 = OK (bug)

The input 1x is not valid JSON, yet the final EOF call reports OK.

Relevant code:

  • src/parser3.h lines 1062-1078: Parser3::parse re-dispatches from whatever symbol is on top of the stack without checking whether a previous step already rejected the document.
  • src/lib.cpp line 43: WeaselJsonParser_parse just forwards into Parser3::parse.
  • src/parser3.h lines 1015-1021: once the stack collapses to T_EOF, t_eof returns OK as soon as buf == bufEnd, regardless of prior rejections.

Expected behavior: after WeaselJsonParser_parse has returned WeaselJson_REJECT once, every subsequent call (including the final len == 0 call) must return WeaselJson_REJECT.

Actual behavior: subsequent calls may return WeaselJson_OK if the top-of-stack symbol happens to be T_EOF when the buffer is empty.

Impact: streaming consumers that detect a reject mid-stream but still call finish() can be tricked into accepting malformed input. This breaks the API contract that WeaselJson_REJECT means the document is invalid.

The parser is not sticky after it returns `WeaselJson_REJECT`. If an invalid byte arrives in one chunk and the caller later calls `WeaselJsonParser_parse(parser, nullptr, 0)` to finish, the parser can return `WeaselJson_OK` and make the malformed input look valid. Minimal reproduction (C API): ```c WeaselJsonCallbacks c = { /* all no-op callbacks set */ }; WeaselJsonParser *p = WeaselJsonParser_create(1024, &c, NULL, 0); char a[] = "1"; char b[] = "x"; printf("chunk1 %d\n", WeaselJsonParser_parse(p, a, 1)); // 1 = AGAIN printf("chunk2 %d\n", WeaselJsonParser_parse(p, b, 1)); // 2 = REJECT printf("final %d\n", WeaselJsonParser_parse(p, NULL, 0)); // 0 = OK (bug) ``` The input `1x` is not valid JSON, yet the final EOF call reports `OK`. Relevant code: - `src/parser3.h` lines 1062-1078: `Parser3::parse` re-dispatches from whatever symbol is on top of the stack without checking whether a previous step already rejected the document. - `src/lib.cpp` line 43: `WeaselJsonParser_parse` just forwards into `Parser3::parse`. - `src/parser3.h` lines 1015-1021: once the stack collapses to `T_EOF`, `t_eof` returns `OK` as soon as `buf == bufEnd`, regardless of prior rejections. Expected behavior: after `WeaselJsonParser_parse` has returned `WeaselJson_REJECT` once, every subsequent call (including the final `len == 0` call) must return `WeaselJson_REJECT`. Actual behavior: subsequent calls may return `WeaselJson_OK` if the top-of-stack symbol happens to be `T_EOF` when the buffer is empty. Impact: streaming consumers that detect a reject mid-stream but still call `finish()` can be tricked into accepting malformed input. This breaks the API contract that `WeaselJson_REJECT` means the document is invalid.
weaselbot was assigned by andrew 2026-06-19 16:50:19 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: weaselab/weaseljson#12