Repository navigation
Add storefront unsafe-eval security check - #9059
hwdavidward wants to merge 6 commits into
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
The unsafe-eval rules produce false positives for unrelated calls and non-executable content while missing dynamic constructors, template interpolations, and Liquid JavaScript blocks.
6 open issues
Exclude function names and unrelated methods from unsafe-eval matches · New Detect constructor calls on async, arrow, and generator functions · New Ignore timer examples inside strings · New Scan executable Liquid JavaScript raw-tag bodies · New Skip inert and commented-out script bodies · New Scan template interpolations for executable eval calls · New
What changed in this PR
Adds STOREFRONT_UNSAFE_EVAL to the deterministic app-security checks, helping developers identify theme extension JavaScript that requires CSP unsafe-eval.
Changes:
- Adds detection for eval-style execution, dynamic constructors, and string-based timers in theme assets and inline scripts.
- Registers the check with severity, remediation, and a documentation link.
- Adds scanner coverage, contract assertions, and a minor-release changeset.
| File | Description |
|---|---|
packages/app/src/cli/services/app-security-engine/tests/scan-contract.test.ts |
Tests theme applicability and end-to-end findings. |
packages/app/src/cli/services/app-security-engine/tests/deterministic-rules.test.ts |
Adds check-version assertions and detection examples. |
packages/app/src/cli/services/app-security-engine/tests/catalog.test.ts |
Adds the expected documentation slug. |
packages/app/src/cli/services/app-security-engine/scanners/index.ts |
Registers the theme-scoped deterministic check. |
packages/app/src/cli/services/app-security-engine/rules/js-rules.ts |
Implements detection, script extraction, and source masking. |
packages/app/src/cli/services/app-security-engine/rules/catalog.ts |
Defines severity, remediation, and documentation metadata. |
.changeset/storefront-unsafe-eval.md |
Announces the new check as a minor release. |
Review effort: Balanced
|
Binks' finding about JavaScript MIME parameters is a false positive. The HTML standard, §4.12.1.2 explicitly says that scripts whose The distinction is between an HTTP Recording this here because the Binks review has not been published as a GitHub thread to reply to or resolve. |

WHY are these changes introduced?
Storefront JavaScript that compiles strings at runtime can break under a CSP that omits
unsafe-eval. Developers need findings that distinguish real calls from comments, strings, and unrelated methods, followed by a review of what actually ships to the storefront.WHAT is this pull request doing?
Adds
STOREFRONT_UNSAFE_EVALto app security checks. Babel parses theme JavaScript/TypeScript and resolves lexical bindings and immutable aliases; the existing Liquid parser extracts executable script bodies and{% javascript %}tags, while skipping comments, inert script types, and inline bodies of external scripts. Candidates include string eval/timer calls and function constructors, with original line and column locations.The deterministic check stays unresolved until the accompanying semantic check verifies production reachability, build configuration, dependencies, and lazy-loaded code. Current semantic results supersede candidates, so a verified fallback or unreachable call can be cleared. Missing production artifacts remain unresolved. Deterministic candidates carry the existing
confidence: needs_reviewthrough stored results and review output; both blocking gates skip these generically. Findings without a confidence value retain their existing blocking behavior. Active agent findings still block under ordinary union or prefer-agent precedence, including stale results that fall back to union. The check itself defines the CSP proof requirements; literal code strings count without attacker-controlled input.Validation: confidence persistence and public JSON projection, mixed-confidence blocking, union and stale-agent blocking regressions, scanner regression cases, the security engine suite and result/artifact integration tests, app lint and type checking, Knip for app, frozen-lockfile installation, CLI bundle build, and a native ESM scanner smoke test.
How to manually test your changes?
In a theme app extension, add an asset containing
const run = window.eval; run("render()")and a Liquid block containing{% javascript %}setTimeout("render()", 1){% endjavascript %}. Runshopify app security check --print-instructions. Both calls should appear as candidates with source locations, and the check should remain unresolved pending semantic review. Runshopify app security review --json --blocking highto seeconfidence: "needs-review"on the candidates without failing the gate.Replace the calls with a normal callback or quoted example text and rerun. Those candidates should disappear, while the semantic review requirement remains. Follow the generated instructions to inspect production usage and record the review with
shopify app security record.Release dependency
Publish the matching shopify.dev check page at
app-security-checks/storefront-unsafe-evalbefore release.Checklist
patchfor bug fixes ·minorfor new features ·majorfor breaking changes) and added a changeset withpnpm changeset add