Skip to content

Exempt inert schema-typed Liquid settings from LIQUID_UNSAFE_RENDER - #8935

Merged
jplhomer merged 6 commits into
mainfrom
jplhomer/liquid-unsafe-render-schema-types
Oct 21, 2026
Merged

jplhomer merged 6 commits into
mainfrom
jplhomer/liquid-unsafe-render-schema-types

Conversation

@jplhomer

@jplhomer jplhomer commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

WHY are these changes introduced?

Fixes https://github.com/shop/issues-develop/issues/24098

LIQUID_UNSAFE_RENDER flagged every block.settings.* / section.settings.* output the same way, including select/checkbox settings whose values are fixed by the extension schema (e.g. a layout select with left|right options, or a boolean toggle used as a class). Those closed, developer-defined value domains cannot carry merchant-injected markup in HTML text or ordinary attribute contexts.

WHAT is this pull request doing?

  • liquid-rules.ts: extract the {% schema %} JSON from the same file, map setting id -> type, and thread it into liquidUnsafeRenderVisitor. Exempts only exact block.settings.<id> / section.settings.<id> (no property access) in html_text / non-URL html_attribute contexts for checkbox, range, and select/radio where every options[].value passes a conservative inert charset (no <, >, &, quotes, backtick, whitespace, or =). Missing, unparseable, or duplicated schema, unsafe option values, and all other types fall through to previous behaviour (fail-closed).
  • Untouched: liquidExecutableContextVisitor (UNSAFE_INNERHTML), JavaScript/executable contexts, URL attributes, metafields.
  • Bumps LIQUID_UNSAFE_RENDER v1 -> v2 in scanners/index.ts and checks/LIQUID_UNSAFE_RENDER.md, regenerates checks/embedded.ts, adds the missing version-pin assertion, and adds the targeted test matrix (inert select clean in text/attribute, bad option flagged, checkbox/range clean, text/richtext flagged, script/onclick flagged, missing/unparseable schema flagged, escaped stays clean).

Follow-up (out of scope): update the shopify.dev app-security check page for LIQUID_UNSAFE_RENDER in shop/world to describe the typed exemption.

How to manually test your changes?

In a theme app extension block, add a {% schema %} with a select setting (inert options), a checkbox, and a text setting; render each unescaped in element text and in a class attribute, then run the app-security scan. The select/checkbox outputs should no longer raise LIQUID_UNSAFE_RENDER, while the text output still should.

@github-actions github-actions Bot added the no-changelog This PR doesn't include a changeset entry. Is an internal only change not relevant to end users. label Oct 16, 2026
@jplhomer

Copy link
Copy Markdown
Contributor Author

/snapit

1 similar comment
@jplhomer

Copy link
Copy Markdown
Contributor Author

/snapit

@github-actions

Copy link
Copy Markdown
Contributor

🫰✨ Thanks @jplhomer! Your snapshot has been published to npm.

Built from 1d93a8213a71a11b9451e0e7e5fd08806bc82622. Workflow run.

Test the snapshot by installing your package globally:

pnpm i -g --@shopify:registry=https://registry.npmjs.org @shopify/cli@0.0.0-snapshot-20261020135348

Caution

After installing, validate the version by running shopify version in your terminal.
If the versions don't match, you might have multiple global instances installed.
Use which shopify to find out which one you are running and uninstall it.

…teral filter args

In an app block, section.settings refers to the host section, so it is no longer checked against the block schema. Filter arguments are rendered too, so a filter argument that references a setting or variable (for example default: block.settings.title) now disqualifies the exemption.
@jplhomer

Copy link
Copy Markdown
Contributor Author

/snapit

@github-actions

Copy link
Copy Markdown
Contributor

🫰✨ Thanks @jplhomer! Your snapshot has been published to npm.

Built from fb8c31f0bcf81bede1d964ca053eeb5d1cadebca. Workflow run.

Test the snapshot by installing your package globally:

pnpm i -g --@shopify:registry=https://registry.npmjs.org @shopify/cli@0.0.0-snapshot-20261020141829

Caution

After installing, validate the version by running shopify version in your terminal.
If the versions don't match, you might have multiple global instances installed.
Use which shopify to find out which one you are running and uninstall it.

@jplhomer
jplhomer marked this pull request as ready for review October 20, 2026 14:54
@jplhomer
jplhomer requested a review from a team as a code owner October 20, 2026 14:54
Copilot AI balanced review requested due to automatic review settings October 20, 2026 14:54

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The exemption guards can suppress valid findings for transformed values, rebound block variables, and omitted URL contexts.

3 open issues
What changed in this PR

Reduces false positives in LIQUID_UNSAFE_RENDER by using app-block schemas to identify inert settings.

Changes:

  • Adds schema-based exemptions for selected setting types in HTML contexts.
  • Adds regression tests for exemptions and fallback behavior.
  • Updates the check to version 2 and refreshes embedded guidance.
File Description
packages/​app/​src/​cli/​services/​app-security-engine/​tests/​deterministic-rules.test.ts Adds exemption tests and version assertion.
packages/​app/​src/​cli/​services/​app-security-engine/​scanners/​index.ts Bumps the deterministic check version.
packages/​app/​src/​cli/​services/​app-security-engine/​rules/​liquid-rules.ts Implements schema parsing and exemption guards.
packages/​app/​src/​cli/​services/​app-security-engine/​checks/​LIQUID_UNSAFE_RENDER.md Documents version 2 behavior.
packages/​app/​src/​cli/​services/​app-security-engine/​checks/​embedded.ts Refreshes embedded check guidance.

Review effort: Balanced

Comment thread packages/app/src/cli/services/app-security-engine/rules/liquid-rules.ts Outdated
Comment thread packages/app/src/cli/services/app-security-engine/rules/liquid-rules.ts Outdated
…filters

The v2 exemption could still suppress findings for output that is not inert:

- Attribute contexts used a URL denylist that missed object data, srcset, and imagesrcset. Exempt attribute values are now an allowlist (class, id, title, alt, role, width, height, lang, dir, aria-*), and only in quoted values with a fully static attribute name.
- Liquid directly in an attribute list, in an attribute or tag name, in an unquoted value, or inside raw-text elements such as style is no longer exempt.
- Literal filter arguments did not prove inert output (url_decode, t, script_tag). Only argument-less case and whitespace filters, escape, and default with an inert literal fallback are allowed.
- A file that rebinds block (assign, capture, for, tablerow, increment, decrement) loses the exemption, since block.settings may no longer refer to the schema-owning block.

Co-authored-by: Qlaw <noreply@qlaw.quick.shopify.io>
Add cases for duplicate schema blocks, duplicate setting ids, empty select options, and the inert radio exemption. Each fails when its guard is removed.

Co-authored-by: Qlaw <noreply@qlaw.quick.shopify.io>
@jplhomer
jplhomer requested a review from jek October 20, 2026 16:39
jplhomer and others added 2 commits October 20, 2026 13:33
…e-render-schema-types

# Conflicts:
#	packages/app/src/cli/services/app-security-engine/tests/deterministic-rules.test.ts
@jplhomer jplhomer removed the no-changelog This PR doesn't include a changeset entry. Is an internal only change not relevant to end users. label Oct 20, 2026
@github-actions github-actions Bot added the Area: @shopify/cli @shopify/cli package issues label Oct 20, 2026
@jplhomer
jplhomer added this pull request to the merge queue Oct 21, 2026
Merged via the queue into main with commit 8fa26af Oct 21, 2026
30 checks passed
@jplhomer
jplhomer deleted the jplhomer/liquid-unsafe-render-schema-types branch October 21, 2026 19:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area: @shopify/cli @shopify/cli package issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants