Repository navigation
Conversation
A `json` node accepts any value, so a ~30 KB / ~5k-level payload passed validation with no errors; the block fingerprints (`JSON.stringify`) and the JSON tree recursion then overflowed the stack at render time. Check the value iteratively (a recursive depth check would just move the overflow into the guard) and cap it at GENUI_LIMITS.maxJsonDepth = 32, well inside what the JSON tree can draw. Over-deep values fail validation with a teaching diagnostic and repair drops the node, so nothing unserializable reaches the renderer. The same helper guards the object-option label fallback in repairStrings, which had the identical unbounded JSON.stringify and threw out of processGenuiSpec itself.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A
jsonnode accepts any value (src/client/guard.ts, repair casejson). A value nested a few thousand levels deep therefore passes validation with no errors, and the first serialization at render time overflows the stack:src/client/fence-render.tsx:194— the fence state key callsJSON.stringify(spec)src/client/GenuiBlock.tsx:86— the streaming memo fingerprint stringifies the specsrc/client/blocks/advanced.tsx:102— the JSON tree recurses over the valueRepro on the base commit (~30 KB body, 5 000 levels):
In jsdom the whole fence then fails to render (
[dsh-genui] render failed: RangeError) and the reader gets the error boundary instead of a code block. The same pattern exists inrepairStrings: a deep object used as aselect/radio/segmented-controloption falls back toJSON.stringify(item)and throws out ofprocessGenuiSpecitself.Fix
GENUI_LIMITS.maxJsonDepth = 32, checked by an iterativejsonValueTooDeephelper (a recursive check would just move the overflow into the guard):json.valueis a validation error —items[0].value nesting depth exceeds JSON limit 32; flatten or split the value(same shape as the SVG length diagnostic from [Bug] svg 组件 code 超过 maxCode(12,000)时被静默截断 → XML 解析失败 → 降级为显示源码;validate_dsh_ui 仍返回 valid #378);32 is far inside what the JSON tree can draw and what V8 can stringify (a few thousand levels), and well above a readable JSON payload. Values at 32 still round-trip verbatim; 33 is the first rejected level.
Tests
tests/genui-guard.spec.ts(+3):JSON.stringify(processed.spec)does not throw; 32-level value round-trips;validateGenuiSpecproduces the diagnostic andrepairGenuiSpecreturns{items: []}; shallow payloads keep their exact shape;processGenuiSpec, and labelled object options still become labels.On the base commit all 3 fail (two with
RangeError); with the patchnpx vitest run tests/genui-guard.spec.tsis 52 passed, full suite 2109 passed / 152 files,npx tsc -p tsconfig.json --noEmitclean.Not covered here: the
render_uitool wrapper-unwrapping depth (src/plugin/tool.ts), which is a separate path.