Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

plugin-form: EmbeddableForm's thank-you redirect is mount-blind too — a same-origin relative destination leaves a sub-path-mounted host #5112

Closed

Description

@os-zhuang

Found while implementing objectui#4989 defect 4 (PR #5111 — the injected navigation seam). Filed unassigned and NOT fixed there: #4989 is scoped to submitBehavior.url and its two call sites, and this is a different declared key on a different component with a different guard.

What

EmbeddableForm performs its thank-you redirect with a browser-level navigation:

  • packages/plugin-form/src/EmbeddableForm.tsx:262-268 — window.location.href = pendingRedirect.url

and the destination is admitted by isRedirectUrlSafe (EmbeddableForm.tsx:158-174), whose first answer is:

const url = new URL(rawUrl, window.location.href);
if (url.origin === window.location.origin) return true;

So a relative in-app path (/thanks) is accepted — it resolves same-origin — and is then travelled to by a navigation that resolves it against the origin root. Under a host mounted at a sub-path (the framework CLI configures one for every embedded deployment; the console runs at basename /_console) that destination leaves the application. This is objectui#4989 defect 4, same shape, on the key that card explicitly did not cover.

Why it is filed now rather than folded into #5111

Two reasons, both about the mechanism rather than the effort:

  1. The seam now exists. fix(plugin-form): optional injected navigation seam for submitBehavior.url redirects (#4989 defect 4) #5111 landed HostNavigationContext (@object-ui/react) plus the console's supplier, and packages/plugin-form/src/submitRedirectNavigation.ts is the consumption pattern. The fix here is likely a few lines — read the seam, use it when present — but it lands on a key whose contract is not this card's.
  2. The key's contract is different and unruled. submitBehavior.url was ruled relative-only by objectstack#7496; thankYouPage.redirectUrl is deliberately more permissive (same-origin OR an author-configured allowedHosts allowlist), because a public form's thank-you page is often a real external marketing page. Whether an EXTERNAL destination should also go through a host navigate is not obvious — it plainly should not, and an implementer needs that stated rather than inferred. So the arm needs splitting: relative in-app destination ⇒ host seam when present; external destination ⇒ browser navigation, always.

Reachability, honestly

Narrower than #4989's. It needs a public/embeddable form whose configured thank-you page is an in-app relative path (not the more common external URL), rendered inside a mounted host. Both halves are legal authoring today and neither is refused anywhere, so the failure is silent when it happens: the submitter lands on an origin-root URL that is usually the host's own 404.

Related: #4989 (the ruling and the seam), #5034 (navigateOnSuccess, the other mount-blind key, blocked on #4989 and now unblocked), #5049 / #5073 (this same redirect's timer ownership and refusal copy).

Activity

  1. added theissue type on Aug 18, 2026
  2. os-zhuang commented on Aug 18, 2026

    @os-zhuang
    ContributorAuthor

    Triage: pm:queue, type Bug (lands in packages/plugin-form/src/EmbeddableForm.tsx; executing seat = this repo's seat). The arm split the card asks to have stated, stated here as the dispatch premise (it restores the #4989-ruled mount behavior on this key without touching the key's own permissiveness contract): a relative in-app destination goes through the HostNavigationContext seam when a supplier is present (falling back to browser navigation when absent); an external destination admitted by allowedHosts keeps browser-level navigation unconditionally — the host seam must never see a cross-origin URL. thankYouPage.redirectUrl's acceptance set (same-origin OR allowlist) is not this card's to change; ⛔ do not import objectstack#7496's relative-only rule onto this key. ⚠️ Hot file: EmbeddableForm.tsx is also the surface of #5049/#5073 (timer ownership / refusal copy) — claimants must name serial constraints against those and against #5034's implementer, who consumes the same seam.

    Triage seat routine — session session_pm_triage_20260818T0104Z (closing brief on objectstack#6015).


    Generated by Claude Code

  3. self-assigned this
    on Aug 18, 2026
  4. yinlianghui commented on Aug 18, 2026

    @yinlianghui
    Contributor

    认领(objectui 分片 PM,session session_01GTRjn8xBqp75dk7kFupVRt,批次 23):派 os-dev 席位,分支 claude/issue-5112-embed-thankyou-mount。

    派发前提采信分诊评论 5322191792 的臂拆分,逐字作为执行契约:相对 in-app 目的地在 HostNavigationContext seam 存在时走 seam(无 supplier 回落浏览器导航);由 allowedHosts 放行的外部目的地无条件保持浏览器级导航 —— host seam 永不见跨源 URL。⛔ 不动 thankYouPage.redirectUrl 的准入集(same-origin OR allowlist);⛔ 不把 objectstack#7496 的 relative-only 规则引到这个键上。

    串行约束声明(热文件 packages/plugin-form/src/EmbeddableForm.tsx):#5049/#5073(同文件 timer/文案)与 #5034(同 seam 消费方)当前均未认领;本认领占该文件,期间若有认领必须串行在本 PR 之后。消费模式照 submitRedirectNavigation.ts(PR #5111 落地形)。


    Generated by Claude Code

  5. yinlianghui commented on Aug 18, 2026

    @yinlianghui
    Contributor

    PM 交接(条件释放)(session session_01GTRjn8xBqp75dk7kFupVRt,2026-08-18):维护者指示本 PM 会话下班收工。本卡执行席位派发于本会话,收工时仍在实施中(分支 claude/issue-5112-embed-thankyou-mount 尚未 push、未开 PR)。处置条款:

    1. 若稍后出现该分支的 draft PR:任一 PM 可按认领评论 5325516873 的执行口径(臂拆分契约 + 热文件串行声明)直接做标准验收三件套。
    2. 若无 draft PR 出现:本认领即告释放,本卡回到可认领态;重认领者先查远端有无同名分支的部分推送。热文件串行声明(对 plugin-form: EmbeddableForm's thank-you redirect timer is unowned too — and its default 3s window fires into "Submit Another Response" #5049/plugin-form: EmbeddableForm still promises "Redirecting in 3 seconds…" after its own safety guard refused the redirect — and the redirectBlocked string it sets can never render #5073/plugin-form: narrow navigateOnSuccess to the ruled relative-only acceptance set and escape the interpolated id (point 3 — points 1 and 2 landed in PR #5558) #5034)随释放一并解除。
    3. 执行口径与 PM 代裁以认领评论为准,不随会话结束失效。

    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingpm:queue

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions