Skip to content

fix(v0.44): make SPA page-property projection fail closed for dangerous keys (#1214) - #1249

Merged
SisyphusZheng merged 1 commit into
devfrom
v044/a10.6-spa-projection-guard
Sep 2, 2026
Merged

fix(v0.44): make SPA page-property projection fail closed for dangerous keys (#1214)#1249
SisyphusZheng merged 1 commit into
devfrom
v044/a10.6-spa-projection-guard

Conversation

@SisyphusZheng

@SisyphusZheng SisyphusZheng commented Sep 2, 2026

Copy link
Copy Markdown
Member

Problem

Issue #1214 (umbrella #1155, Alpha.10, audit H4). Page-level property projection bypassed the canonical dangerous-key guard: spa.ts did raw host[key] = value onto the live page host (host['__proto__'] = … re-prototypes the element instance), the default projector projectPageProps passed route params / loader data through unfiltered (own __proto__ keys survive JSON.parse + Object.entries), and the generated server runtime carried its own unfiltered copy. The canonical guarded assigner injectPropsSafe was dead code with stale comments.

Owner

Canonical rule: packages/element/src/internal/core/security.ts (DANGEROUS_KEYS/isDangerousKey/injectPropsSafe). This PR creates NO second rule: the generated server runtime receives a build-time-serialized copy of the canonical list (derived, not re-authored), same pattern as the admitted-tag list.

Before

Three unguarded projection paths; guard existed but was unused on page-projection paths.

After

Every projection channel (default / descriptor props / descriptor error / route params / loader data, SPA + generated server runtime) fails closed on __proto__/constructor/prototype; legitimate keys keep full parity (proven by parity tests + all pre-existing suites green). Guard trio exported from the element root as @experimental (documented in PACKAGE_SURFACE with a B1.2 move condition) so production code uses the workspace specifier — no unpublishable relative deep imports.

Why this is not a second semantic owner

One rule, one home; generated code carries a serialized copy; no new guard logic anywhere.

Evidence

  • Hostile tests (new): spa-projection-guard.test.ts (5) + entry-render-runtime-security.test.ts (4), using JSON.parse('{"__proto__":…}') end-to-end — RED at base SHA (app 4 failed; adapter 3 failed), GREEN after.
  • Gates: scoped suites (app 310 incl. element, adapter-vite 711) green; full deno task test exit 0; fmt/lint/typecheck/arch/docs:truth green; interface snapshot regenerated (delta = exactly the 3 experimental symbols); pack:dry-run + package-artifacts:check + package-surface:check + graph:check all exit 0 (publishability proven).
  • Executed by a fresh kimi-code/k3-256k HIGH implementer session; orchestrator adversarial review caught and repaired the initial relative-deep-import channel (unpublishable), repair re-verified.

Scope / non-goals

No behavior change for legitimate keys; no second dangerous-key rule; no control-plane files.

Risk

Element root gains 3 @experimental symbols (unfrozen; B1.2 decides final home). Rollback: revert this commit.

Amendment authority

This change amends frozen semantics (ADR-0122 §1 loop contract: packages/app/src/authoring.ts). Amendment reference: ADR-0151 (v0.44 release-train re-topology) — Alpha.10 Truth Closure scope, issue #1214, umbrella #1155.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 2, 2026

Copy link
Copy Markdown

Deploying openelement with  Cloudflare Pages  Cloudflare Pages

Latest commit: b5872a3
Status: ✅  Deploy successful!
Preview URL: https://138b7e4e.lessjs.pages.dev
Branch Preview URL: https://v044-a10-6-spa-projection-gu.lessjs.pages.dev

View logs

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

APIError: Insufficient Balance

opencode session  |  github run

loader: () => Promise<unknown>,
): Promise<{ host: Record<string, unknown>; baseline: object }> {
const hosts: Record<string, unknown>[] = [];
let baseline: object = Object.prototype;
…us keys (#1214)

Alpha.10 truth closure (H4): page-level projection paths bypassed the
canonical dangerous-key guard — raw host[key] = value in the SPA
bootstrap, unfiltered default projector, and an unfiltered copy in the
generated server runtime.

Amendment authority: ADR-0151 (touches ADR-0122 §1 frozen path
packages/app/src/authoring.ts; Alpha.10 truth-closure scope, umbrella
#1155).

- route the SPA write boundary through the canonical injectPropsSafe
  (previously dead code), covering default/props/error projector channels
- filter projectPageProps with isDangerousKey at construction
- serialize canonical DANGEROUS_KEYS into the generated server runtime
  at build time (derived copy, not re-authored)
- export the guard trio from the element root as @experimental
  (B1.2 will adjudicate the final home); correct stale guard comments
- hostile tests: JSON.parse own-__proto__ end-to-end across SPA and
  generated server paths; legitimate-key parity proven

Implemented by fresh kimi-code/k3-256k HIGH session
(.agents/v044-kimi-implementer.md), packet A10.6 + orchestrator-reviewed
import-channel repair. No publish, no tag.
@SisyphusZheng
SisyphusZheng force-pushed the v044/a10.6-spa-projection-guard branch from 5cfa45f to b5872a3 Compare September 2, 2026 05:05
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

APIError: Insufficient Balance

opencode session  |  github run

@SisyphusZheng
SisyphusZheng merged commit 42f9421 into dev Sep 2, 2026
9 checks passed
@SisyphusZheng
SisyphusZheng deleted the v044/a10.6-spa-projection-guard branch September 3, 2026 09:50
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.

2 participants