fix(ui): tighten clipboard prefix matching to prevent sibling row lea…#16742
Open
dawndarkness wants to merge 1 commit into
Open
fix(ui): tighten clipboard prefix matching to prevent sibling row lea…#16742dawndarkness wants to merge 1 commit into
dawndarkness wants to merge 1 commit into
Conversation
…kage on copy/paste
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.
What?
mergeFormStateFromClipboardand its helperreduceFormStateByPathusekey.startsWith(prefix)at three callsites without enforcing a path boundary. This leaks unrelated form-state keys into the clipboard on copy and into the merge target on paste.Most visible failure mode: array fields with 10 or more rows. Copying row 1 silently bundles rows 10, 11, 12 into the clipboard (their paths all start with
children.1). On paste, those leaked rows get rewritten to out-of-bounds indices like.50,.51,.52, surfacing as phantom rows the editor cannot remove. The field then fails validation and blocks save.The same loose match also lets textual sibling fields cross-contaminate (
childrenvschildrenOther) in thefromRowToFieldcleanup branch and inreduceFormStateByPath.Why?
'children.1'.startsWith('children.1')matches'children.10','children.11', etc.'children'.startsWith('children')matches'childrenOther'. Three callsites are affected:reduceFormStateByPath's filter loop. Builds the clipboard payload, so the leak originates here.mergeFormStateFromClipboard's paste loop predicate. Should drop unrelated keys; instead rewrites them to out-of-bounds paths.mergeFormStateFromClipboard'sfromRowToFieldcleanup branch. Can delete unrelated fields whose name shares a textual prefix.How?
Added a small private helper requiring the candidate path to either equal the prefix or be followed by
.:Replaced startsWith at all three callsites. No behavior change for non-colliding paths.
Tests
New describe('prefix collision with multi-digit sibling indices') block in mergeFormStateFromClipboard.spec.ts covers:
All four fail on main, all pass after the fix. Existing tests in the spec are unchanged and continue to pass.
Manual repro
After the fix: paste updates only the target row.
Fixes #16741