Skip to content

fix(SSO-2658): bound json-logic map reduce merge in UI-schema conditions - #5983

Open
jmfrancois wants to merge 5 commits into
masterfrom
jmfrancois/fix/30c4c96372
Open

jmfrancois wants to merge 5 commits into
masterfrom
jmfrancois/fix/30c4c96372

Conversation

@jmfrancois

Copy link
Copy Markdown
Contributor

What is the problem this PR is trying to solve?

SSO-2658: shouldRender (packages/forms/src/UIForm/utils/condition.js) evaluates UI-schema condition rules served by the backend with jsonLogic.apply and no cost limit. isSafeCondition only checks operator names, so a ~150 byte rule (a reduce whose accumulator is merged/cated with itself, or map nested ~9 levels deep) costs exponential time/memory on the main thread and freezes or crashes the tab of everyone opening the form.

What is the chosen solution to this problem?

Validate the condition structure before evaluating it (isBoundedCondition): cap node count (500) and depth (20), allow at most 2 nested iteration operators (map, filter, reduce, all, none, some), and forbid merge, cat and arrays of non-literal values inside a reduce logic (they grow the accumulator exponentially). Rejected conditions make the item not rendered, like unsafe conditions already do. Existing conditions keep working; new tests cover the exploit rules (they crash with OOM / RangeError without the fix) and bounded iteration cases.

Not addressed here: memoizing shouldRender across Widget/Fieldset renders (second recommendation of the finding).

Please check if the PR fulfills these requirements

  • The PR have used pnpm changeset to a request a release from the CI if wanted.
  • The PR commit message follows our guidelines
  • Tests for the changes have been added (for bug fixes / features) And non reg done before need review
  • Docs have been added / updated (for bug fixes / features)
  • Related design / discussions / pages (not in jira), if any, are all linked or available in the PR

[ ] This PR introduces a breaking change

Root cause: shouldRender ran UI-schema conditions through jsonLogic.apply with
only an operator allow-list check. A ~150 byte rule (a reduce whose accumulator
is merged/cat with itself, or map nested ~9 levels) costs exponential CPU and
memory on the main thread, freezing or crashing the tab of whoever opens a form
served with such a schema.

Fix: validate the condition structure before evaluating it. Nodes count and depth
are capped, iteration operators (map, filter, reduce, all, none, some) can be
nested at most twice, and a reduce logic cannot grow its accumulator (merge, cat,
arrays of non-literal values). Rejected conditions make the item not rendered.

Recurrence is prevented by the tests reproducing the doubling reduce (merge, cat,
nested arrays) and deeply nested map rules, which crash/hang without the check.
@jmfrancois
jmfrancois deployed to pull_request_unsafe November 3, 2026 10:44 — with GitHub Actions Active
@jmfrancois
jmfrancois deployed to pull_request_unsafe November 3, 2026 10:44 — with GitHub Actions Active
@jmfrancois
jmfrancois deployed to pull_request_unsafe November 3, 2026 10:44 — with GitHub Actions Active
@changeset-bot

changeset-bot Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 37e8306

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@talend/react-forms Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

github-actions Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor
Title Lines Statements Branches Functions
cmf Coverage: 89%
89.53% (1275/1424) 83.99% (656/781) 89.92% (357/397)
cmf-cqrs Coverage: 87%
86.77% (164/189) 71.59% (63/88) 83.05% (49/59)
cmf-router Coverage: 95%
95.31% (224/235) 91.27% (157/172) 90.56% (48/53)
components Coverage: 90%
90.81% (5604/6171) 82.57% (3250/3936) 88.26% (1407/1594)
containers Coverage: 84%
83.55% (1407/1684) 75.85% (732/965) 75.28% (332/441)
dataviz Coverage: 86%
85.93% (330/384) 65.41% (157/240) 75.94% (120/158)
design-system Coverage: 68%
67.92% (1101/1621) 54.06% (612/1132) 55.71% (239/429)
faceted-search Coverage: 85%
85.33% (640/750) 80.82% (295/365) 82.24% (227/276)
flow-designer Coverage: 75%
74.89% (692/924) 77.69% (418/538) 72.88% (207/284)
forms Coverage: 87%
87.24% (1758/2015) 78.89% (1054/1336) 85.73% (481/561)
http Coverage: 97%
98.01% (99/101) 95.52% (64/67) 100% (36/36)
sagas Coverage: 92%
92.3% (24/26) 66.66% (4/6) 50% (2/4)
stepper Coverage: 81%
82.29% (158/192) 61.05% (58/95) 82% (41/50)
utils Coverage: 98%
98.9% (90/91) 90% (18/20) 100% (29/29)

@jmfrancois
jmfrancois deployed to pull_request_unsafe November 3, 2026 11:03 — with GitHub Actions Active
@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

Storybook for this PR deployed on this github page

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

Reducer guards still allow exponential accumulator growth through missing and quadratic nested iteration, leaving critical tab-freeze vulnerabilities unresolved.

2 open issues
What changed in this PR

This PR adds structural limits to backend-provided UI-schema conditions in @talend/react-forms, aiming to prevent expensive JSON Logic evaluation from freezing the browser.

Changes:

  • Caps condition size, depth, and iteration nesting, and rejects selected accumulator-growing expressions inside reducers.
  • Adds regression tests for expensive rules and compatibility tests for bounded iteration.
  • Requests a patch release through Changesets.
File Description
packages/​forms/​src/​UIForm/​utils/​condition.test.js Tests rejection of costly conditions and acceptance of bounded examples.
packages/​forms/​src/​UIForm/​utils/​condition.js Adds structural validation before condition resolution and evaluation.
.changeset/​bound-json-logic-conditions.md Documents the restrictions and requests a forms package patch release.

Review effort: Balanced

@jmfrancois
jmfrancois deployed to pull_request_unsafe November 3, 2026 11:28 — with GitHub Actions Active
@jmfrancois
jmfrancois deployed to pull_request_unsafe November 3, 2026 11:28 — with GitHub Actions Active
@jmfrancois
jmfrancois deployed to pull_request_unsafe November 3, 2026 11:28 — with GitHub Actions Active
@jmfrancois
jmfrancois requested a balanced review from Copilot November 3, 2026 11:28
@jmfrancois jmfrancois added the sso label Nov 3, 2026
@jmfrancois
jmfrancois marked this pull request as ready for review November 3, 2026 11:32

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

The reducer guard still allows accumulator-wide in scans that pass both validators and cause quadratic work during rendering, leaving a critical tab-freeze path.

1 open issue
2 resolved since last review

Review effort: Balanced

@jmfrancois
jmfrancois deployed to pull_request_unsafe November 3, 2026 11:33 — with GitHub Actions Active
@jmfrancois
jmfrancois deployed to pull_request_unsafe November 3, 2026 11:33 — with GitHub Actions Active
@jmfrancois
jmfrancois deployed to pull_request_unsafe November 3, 2026 11:33 — with GitHub Actions Active

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Coercive reducer comparisons still bypass the scan guard, allowing quadratic synchronous work that can freeze the tab.

No open issues 🎉

1 previously missed issue

In code that hasn't changed since last review

High severity Prevent array-wide coercion in reducer comparisons

packages/​forms/​src/​UIForm/​utils/​condition.js:164

Coercive comparisons still bypass the reducer scan guard: with input and initial accumulator both { var: 'items' }, the body { or: [{ '==': [{ var: 'accumulator' }, -1] }, { var: 'accumulator' }] } passes both validators. For zero-filled items, the comparison stringifies the entire array and or preserves it on every step, causing quadratic synchronous work and retaining a tab-freeze path; < permits the same bypass. Bound runtime operand sizes or prevent array-wide coercion in reducer operations, and add regression coverage for both comparison forms.

1 resolved since last review

Review effort: Balanced

@jmfrancois
jmfrancois deployed to pull_request_unsafe November 3, 2026 12:04 — with GitHub Actions Active
@jmfrancois
jmfrancois deployed to pull_request_unsafe November 3, 2026 12:04 — with GitHub Actions Active
@jmfrancois
jmfrancois deployed to pull_request_unsafe November 3, 2026 12:04 — with GitHub Actions Active
@jmfrancois
jmfrancois deployed to pull_request_unsafe November 3, 2026 12:04 — with GitHub Actions Active
@jmfrancois
jmfrancois requested a balanced review from Copilot November 3, 2026 12:04

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

Standard var argument lists with literal paths and defaults are incorrectly rejected as computed paths, hiding widgets for previously valid conditions.

1 open issue

Review effort: Balanced

@jmfrancois
jmfrancois deployed to pull_request_unsafe November 3, 2026 12:13 — with GitHub Actions Active
@jmfrancois
jmfrancois deployed to pull_request_unsafe November 3, 2026 12:13 — with GitHub Actions Active
@jmfrancois
jmfrancois deployed to pull_request_unsafe November 3, 2026 12:13 — with GitHub Actions Active

This branch had an error being deployed

1 failed deployment
pull_request_unsafe — 37e83065 Deployed Nov 3, 2026 by jmfrancois via Build demo on PR #10474
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants