Skip to content

Fix/zencode parser security#1171

Merged
jaromil merged 13 commits intomasterfrom
fix/zencode-parser-security
Feb 26, 2026
Merged

Fix/zencode parser security#1171
jaromil merged 13 commits intomasterfrom
fix/zencode-parser-security

Conversation

@jaromil
Copy link
Copy Markdown
Member

@jaromil jaromil commented Feb 17, 2026

Hardens Zencode parser/control-flow against crash/DoS edge cases and adds runtime safety limits.

C parser safety

  1. Cast bytes to unsigned char before isspace/tolower in prefix parsing.
  2. Make trimq safe on empty/fully-trimmed input.

Lua parser/runtime safety

  1. In non-strict parse mode, skip nil-prefix lines cleanly instead of reaching invalid transition paths.
  2. Prevent branch_valid underflow/drift by decrementing once per failing IF condition.
  3. Add global execution budget: CONF.parser.max_statements (default 1,000,000).
  4. Reset parser/runtime state in ZEN:begin (AST, branch/loop state, counters) to avoid stale cross-run state.

Cast bytes to unsigned char before ctype calls in parse_prefix.
This prevents undefined behavior on non-ASCII input bytes.
Return an empty string when trimq receives empty/fully-trimmed input.
Also normalize ctype usage with unsigned-char casts in trim helpers.
When parse_prefix returns nil and strict_parse is disabled, record the invalid line and continue.
This avoids later nil-prefix transition handling crashes in validation flows.
Only decrement branch_valid once per IF condition evaluation.
Multiple failed asserts in the same IF no longer corrupt branching state.
Introduce parser.max_statements defaulting to 1,000,000 executed statements.
Abort execution when the budget is exceeded to reduce loop-based DoS impact.
Clear AST and control-flow runtime fields before each begin.
This prevents stale state reuse across executions in persistent Lua VM contexts.
Add normalize_stmt() in C and use it from set_sentence.
Preserves normalization semantics while reducing repeated Lua gsub overhead.
Replace heap allocation with a fixed MAX_LINE stack buffer and reject oversized lines.
Return explicit parser errors for normalization limits instead of silent nil fallbacks.
Use C parse_prefix for blank/comment detection and feed raw lines to callbacks.
Normalize_stmt now trims edges internally to preserve matching semantics.
Skip gmatch setup when a matched statement has no single quotes.
This avoids iterator overhead on common no-argument statements.
Keep parse-loop no-trim optimization, but trim ctx.msg in set_sentence.
This restores stable runtime error lines expected by JS decode_error tests.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request hardens the Zencode parser and runtime against crash/DoS vulnerabilities by adding multiple safety measures. The changes include proper handling of character encoding edge cases in C parser functions, safe handling of empty/nil input, prevention of branch counter underflow, and a global execution budget to prevent infinite loops.

Changes:

  • Added unsigned char casts in C parser functions to prevent undefined behavior with non-ASCII characters
  • Made trimq function safe on empty input by adding early return
  • Added normalize_stmt C function to optimize statement normalization (moved from Lua)
  • Fixed branch_valid underflow by tracking branch_condition_failed flag
  • Added global execution budget (max_statements) with default of 1,000,000
  • Reset all parser/runtime state in ZEN:begin to prevent cross-run contamination
  • Fixed nil-prefix line handling in non-strict parse mode

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.

File Description
src/zen_parse.c Added unsigned char casts for isspace/tolower safety, added empty string check in trimq, added new normalize_statement C function for performance
src/lua/zencode_debug.lua Added branch_condition_failed flag to prevent branch_valid underflow when multiple assertions fail in same IF block
src/lua/zencode.lua Reset parser state in begin(), skip nil/empty/comment lines cleanly, add execution budget enforcement, optimize statement normalization
src/lua/init.lua Added max_statements configuration parameter with default of 1,000,000

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@jaromil jaromil merged commit 26129f4 into master Feb 26, 2026
33 checks passed
@jaromil jaromil deleted the fix/zencode-parser-security branch February 26, 2026 21:58
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.

2 participants