The connect flow page goes on only to the authorize URL stored on the flow, so a crafted link cannot choose the client or the callback #69
No reviewers
Labels
No labels
agent-work
agent-working
ultracode
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
trevato-apps/forge!69
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "tdobbertin-espo/forge:fix/connect-flow-stored-target"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Security. The connect flow page (
/settings/origins/flows/:id) took its authorize URL from its own query string (?to=). It checked only two things: that the URL was on the instance's origin, and that it had a PKCE challenge. So a crafted link to a forge's flow page could choose another OAuth application's client id, with that application's callback, on the same instance. The page then offered it as "Continue to the instance". A person who trusted the forge's page could approve an application that the forge did not start. The flow row already seals the authorize URL (target_enc, for the short link), so the query string was never necessary.Now
flowTarget(src/origins/oauth.ts) opens the URL sealed on a pending flow, and the page links to that URL and to no other. The page checks the URL again first: the instance's own/login/oauth/authorize, the flow'sclient_idandredirect_uri, and acode_challenge. Starting a flow redirects to the bare flow page, withthen=migratewhen it started on the migrate page. Atothat an older link still has must be exactly the stored URL. Any othertogets "This connect cannot be continued from here". A pending flow past its ten minutes says "This connect link expired" and shows a fresh Connect for the same host. That button keepsthen=migrate.The device flow, the short link (
/go/:code), the poll and the callback are unchanged. Nothing new is stored, and there is no migration.Tests
bun 1.3.13, a scrubbed environment (no agent variables), Chromium 151:
test/origin-flow-page.test.ts: 5 pass, 0 fail. One of them is new here: a connect started from migrate keepsthen=migrateand noto, and its expired page starts again from migrate.test/oauth-connect-browser.test.ts(FORGE_CHROMIUMset, so it ran and did not skip): 1 pass, 0 fail. It clicks Authorize and Continue and checks that the consent screen is asked for with no CSP refusal.bun test --timeout 60000call: 432 pass, 0 fail across 33 files.bun scripts/gate.tsandtsc --noEmit: clean.the 33 files
origin-flow-page, oauth-connect-browser, origins, origins-oauth, origins-operator, origins-reconnect, origins-security-auth, origins-security-provider, origins-security-push, origins-sync, origins-sync-settled, web-origins, web-origin-link, mcp-origins, mcp-origin-apps, push-through, trusted-hosts, trusted-hosts-ui, steps-lists, no-polling, refresh-meta, mirror-outbound, mirror-out, carried, migrate, migrate-after, tui-migrate, apps-feedback-mirror, and the five cross files reserved-names, studio, shards, check-list, hollow (each
test/<name>.test.ts).🤖 Generated with Claude Code
View command line instructions
Manual merge helper
Use this merge commit message when completing the merge manually.
Checkout
From your project repository, check out a new branch and test the changes.Merge
Merge the changes and update on Forgejo.Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.