Repository navigation
fix(web): warn against approving a confirmation someone else started - #359
Merged
Merged
Conversation
The device page warned against approving a CLI sign-in someone else started, but not a "confirm it's you" request from a CLI or token that is signed in already, which any of them can start and send as a link. With a leaked CLI or access token, someone could send its owner that link: one approval gave the token 15 minutes of sensitive actions, such as making an access token that outlives revoking it. The page now says what approving allows and to deny what you didn't ask for yourself.
This was referenced Oct 30, 2026
kalaspuff
approved these changes
Oct 30, 2026
kalaspuff
pushed a commit
that referenced
this pull request
Oct 30, 2026
## Summary What a linked Linear account delegates to fctl, or mentions fctl in, starts sessions as its fctl person, on their Claude subscription, and its replies reach their sessions' agents. With a stolen browser session or token, someone could link their own Linear account to the victim's fctl account, with nothing to confirm and nothing said to the person, and go on starting the victim's sessions from Linear, reading what they do there, long after the stolen token was revoked or the password changed. Linking a Linear account is now a sensitive action (`Sensitive::LinkLinear`), as connecting a Claude subscription became in #326: where the primary instance owner turned on confirming it's you, it takes a passkey or a code. The app's "Link your Linear account" buttons (Settings and a project's Tickets tab) confirm before they go on to Linear; a link opened elsewhere (Linear's own button in an agent session, `fctl account linear connect`) goes back to Settings with "Linking a Linear account takes confirming it's you", where linking again confirms first. A new link also emails the person when email is set up, naming the Linear account and saying what it does. Verified in `cargo xtask dev`: with confirming turned on and an authenticator app on the account, an unconfirmed session's `GET /api/v1/linear/link` goes back to `/settings?linear=confirm#linear`, and after `POST /api/v1/auth/verify` it goes on to Linear's sign-in. The web tests for the Tickets tab and the outcomes pass, and `cargo xtask check --quick` passes. The email needs an instance with email, which the dev stack isn't. The privacy policy lists the new email since: - hyperstructure/fctl-site#97 ## Worth confirming - **Decision:** like every sensitive action, this asks only where confirming it's you is turned on, which is off by default; elsewhere the email is what tells the person. - **Decision:** a link from Linear's own button that needs confirming lands on Settings rather than taking up the agent session that asked for it; after linking from Settings, fctl still takes up the delegations that waited for that Linear account. ## Stacked pull requests The fixes before this one in this security pass are on main; the next ones build on this one: - #358 - #359 - #360 [fctl session](https://app.fctl.sh/s/ses_01M677K9YMM5WPZBTAR2KKXMSZ) <!-- FCTL_REVIEW_SUMMARY --> --- > [!NOTE] > **Medium risk.** It adds a confirm-it's-you check to linking a Linear account, which changes an authentication-adjacent flow for everyone who links one; a mistake could block linking or let a stolen session link an account. > > **What it does.** Linking a Linear account lets Linear delegations start sessions as the person, so `GET /api/v1/linear/link` now requires the same sensitive-action confirmation as connecting a Claude subscription, where the instance asks for it. The web app confirms before sending people there, and Linear's own button is redirected back to Settings to confirm. A notice email is also sent when an account is linked. > > **New outcome and email.** Linking now has a `confirm` outcome shown in Settings and sends a best-effort email to the person; docs/security.md and docs/architecture.md are updated. > > <sub>[fctl review](https://app.fctl.sh/s/ses_01M67D37HF57SP912A3YYJK0WN)'s first look at [`393e78c`](393e78c) · 10 files, +173 −11 · [Review agent settings](https://app.fctl.sh/p/fctl?tab=reviews)</sub> <!-- /FCTL_REVIEW_SUMMARY --> --------- Co-authored-by: fctl-sh[bot] <343971779+fctl-sh[bot]@users.noreply.github.com>
kalaspuff
pushed a commit
that referenced
this pull request
Oct 30, 2026
…360) ## Summary JSON bodies, GitHub's and Linear's webhook deliveries, avatars, and the files people attach to sessions or agents publish were read whole before anything checked their size, and refused early only when the client said how large the body was (`Content-Length`). A client that doesn't, sending `Transfer-Encoding: chunked`, could send a body without end, without signing in: on the standalone server, whose proxy passes bodies on chunked and caps none, a `curl -T /dev/zero` to the sign-in endpoint would have the Worker keep it all in memory until the instance fell over, with every session socket on it. On Cloudflare the platform's own limits bound it. Bodies are now read as they come (`http::read_upto`) and refused once they pass their limit, with the same messages as before. Verified in `cargo xtask dev`: 200 MB of zeros sent chunked to `POST /api/v1/auth/login` is refused with "The request body is too large" after about 9 MB had gone out, within a second, and signing in, uploading an avatar and an attachment, an agent's published screenshot and a Linear delegation through the stand-in's webhook all work as before. `cargo xtask check --quick` passes. ## Stacked pull requests Based on the one before it in this security pass's stack: - #359 [fctl session](https://app.fctl.sh/s/ses_01M677K9YMM5WPZBTAR2KKXMSZ) <!-- FCTL_REVIEW_SUMMARY --> --- > [!NOTE] > **Medium risk.** It changes how every JSON body, webhook delivery, avatar and file upload is read, which all requests go through, though only the reading changes and not what is accepted. > > **What it does.** Adds `http::read_upto`, which reads a request body chunk by chunk and stops once it holds more than the limit, and uses it for JSON bodies, GitHub and Linear webhooks, avatars, and the file uploads and published screenshots in the files object. Before, a body was read whole before its size was checked, so a chunked request without `Content-Length` could use up the Worker's memory on the standalone server. The callers' own size checks and messages stay as they were, and `docs/security.md` describes the behavior. > > <sub>[fctl review](https://app.fctl.sh/s/ses_01M67D4Q8YHM4BW3MWK538PWGS)'s first look at [`11b23e0`](11b23e0) · 6 files, +36 −8 · [Review agent settings](https://app.fctl.sh/p/fctl?tab=reviews)</sub> <!-- /FCTL_REVIEW_SUMMARY --> --------- Co-authored-by: fctl-sh[bot] <343971779+fctl-sh[bot]@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The device page warns against approving a CLI sign-in someone else started, but its other use had no such warning: a "confirm it's you" request, which any CLI or unlimited access token that is signed in already can start (
POST /api/v1/auth/verify/device) and send as a link. With a leaked CLI or access token, on an instance that asks to confirm sensitive actions, someone could send its owner that link: one approval gave the token 15 minutes of sensitive actions, such as making an access token that outlives revoking the leaked one, which the confirmation is there to stop. The page now says what approving allows and to deny a request you didn't make yourself, in a terminal, just now.Verified in
cargo xtask devwith a confirmation code started from the dev token: the screenshots below are of/device?code=…, main against this branch.npm run checkinweb/passes.Screenshots
At e6ecbc0, the device page for a confirmation request, 900 px wide.
At a phone's width, dark:
Stacked pull requests
Based on the one before it in this security pass's stack:
fctl session
Note
Low risk. It only changes the wording of a warning on the device page; no logic, API or stored data is touched.
What it does. The device page already warned against approving a CLI sign-in that someone else started, but not against approving a confirm-it's-you request, which a signed-in CLI or token can start and send as a link. The confirmation text now says what approving allows (15 minutes of sensitive actions, such as making access tokens and exporting data) and tells people to approve only what they started themselves in a terminal, just now.
fctl review's first look at
bb36116· 1 file, +3 −1 · Review agent settings