Skip to content

review pass: load-boundary hardening + codegen dedup#64

Merged
avanelsas merged 1 commit into
mainfrom
fix/code-review-pass
May 26, 2026
Merged

review pass: load-boundary hardening + codegen dedup#64
avanelsas merged 1 commit into
mainfrom
fix/code-review-pass

Conversation

@avanelsas
Copy link
Copy Markdown
Owner

Summary

Code-review pass focused on load-boundary correctness, export-codegen safety, and shared-helper dedup. All ten findings from the review thread are addressed; no behavioural change for clean inputs.

Load boundary (storage)

  • IDB autosave restore now goes through the same checks as file-open. idb/restore! returns the parsed payload only; pf/apply-autosave! runs classify-payload + sanitize-doc and refuses on spec / unsafe-findings failure. A corrupted or hand-tampered IDB entry can no longer bypass validation and silently install a malformed or hostile document.
  • idb/deserialize strict-rejects unknown :version. Missing or future versions return nil so a schema bump fails loud instead of partially installing the wrong shape. New idb/supported-version constant.
  • apply-loaded-project! cancels the watcher-armed autosave timer. The 750ms timer the swap synchronously arms is dropped so a stale save-now! can't fire after clear-autosave! completes.
  • Autosave failures surface in UI. save-now! sets/clears [:ui :autosave-failed?]; toolbar renders a hidden that unhides on failure (instead of a silent console.warn).
  • main/init has a .catch. An unexpected rejection in the init chain seeds an empty document and installs the watcher rather than leaving a blank page.

Codegen safety (export)

  • Identifier-shape scanner in sanitize.cljs. unsafe-findings now flags attr keys, binding prop names, binding :field, trigger :action-ref, payload :field, field-def :name, and action :name that contain characters unsafe for codegen. Closes the JS / CLJS string-injection vectors a tampered project file could otherwise smuggle into emitted artifacts.

Dedup (export)

  • em/action-ref-canonical-ns + em/action-ref-alias extracted to bareforge.export.model. Both cljs_project and vanilla_js/codegen now delegate instead of carrying parallel subs / name->ns-segment dances.
  • cljs_project/emit-sub-group-child now routes through em/resolve-template-source (the legacy fallback re-deriving stateful-host-for-template is gone).

Other

  • dnd/drag/marquee-hits volatile!(into [] (keep …) (range n)) per the CLAUDE.md PLOP rule.

Tests

  • Identifier scanner: 5 new cases (attr key with \", binding key with (, action-ref with \", field-def name with ], positive control).
  • idb rejects future version + missing version; references supported-version constant.
  • em/action-ref-canonical-ns + action-ref-alias (lowercase passthrough, raw user-typed group normalisation, alias strips app. prefix).
  • toolbar-state :autosave-failed? slice.

Total: 856 tests / 2476 assertions (+12 assertions vs. main).

Test plan

  • clj-kondo --lint src test scripts — 0 errors, 0 warnings
  • cljfmt check — clean
  • npx shadow-cljs compile test — 0 failures, 0 errors
  • npx shadow-cljs release app — 0 warnings
  • ./scripts/check.sh — all four gates green
  • Manual: open a project file → confirm load still works, no console errors
  • Manual: trigger an IDB quota error (e.g., fill quota in DevTools) → confirm ⚠ indicator appears in toolbar

🤖 Generated with Claude Code

Route IDB autosave restore through `project-file/classify-payload` +
`sanitize-doc` so a corrupted or tampered autosave can't bypass the
checks the file-open path uses. Strict-reject unknown `:version` so a
future schema bump fails loud instead of partially installing the
wrong shape. Cancel the watcher-armed 750ms timer on file load so a
stale `save-now!` can't fire after `clear-autosave!`. Surface autosave
write failures as `[:ui :autosave-failed?]` with a hidden ⚠ indicator
in the toolbar.

Extend `unsafe-findings` with an identifier-shape scanner so attr
keys, binding prop names, binding `:field`, trigger `:action-ref`,
payload `:field`, field-def `:name`, and action `:name` that carry
characters unsafe for codegen are refused at the load boundary —
closes the JS / CLJS string-injection vectors in both export plugins.

Extract `action-ref-canonical-ns` + `action-ref-alias` to
`export.model` (single source of truth shared by both plugins). Route
`cljs_project/emit-sub-group-child` through `em/resolve-template-source`
so the legacy fallback that re-derived `stateful-host-for-template` is
gone. Replace the `volatile!` in `dnd/drag/marquee-hits` with an
`into` + `keep` pipeline.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@avanelsas avanelsas merged commit 970e6d6 into main May 26, 2026
1 check passed
@avanelsas avanelsas deleted the fix/code-review-pass branch May 26, 2026 14:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant