Repository navigation
feat(extension): save prompt for logins typed on a page - #12
Merged
Merged
Conversation
After a trusted sign-in submit that follows a user gesture, the content script reads the form's username and password and the service worker keeps the pair per tab in chrome.storage.session, stamped with the submit's sender.url and a random id. The next page load in that tab, or the same page once the password has left its form, asks for the prompt: the new check-login action compares the pair with the submitted host's exact-host entries (new, changed, unchanged) and answers other-site when the asking page is not on the submit's site by the Domain rule. It never returns a password. The prompt is the inline picker in the viewport's top-right corner, never focused, with Add, Update <title> (or a list of the host's entries) and Cancel. Add and Update pass the six guards and send save-login with via: "inline" and the pair's id, so they save only the pair the prompt shows; a new submit closes a prompt that is showing. No prompt when the pair is already stored, the vault is locked or the app is not running. A pending pair is dropped once answered, when its tab closes, after 3 page loads, or on the first page load after 5 minutes. Card codes and one-time codes in password fields are not captured; a password field toggled to text before the submit still counts. Docs: SECURITY.md (web pages), KNOWN-LIMITS.md, ARCHITECTURE.md flow 5, SUBCLAVE.md extension rules, CHANGELOG.md. Closes #9.
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.
What
The extension now offers to save a login typed on a page. After a sign-in
submit, a small prompt in the page's top-right corner offers Add,
Update
<title>(or "Update an existing login" for a list of the host'sentries) and Cancel. It never takes focus and is not modal.
Why
Closes #9.
Until now the extension only wrote to the vault through "Generate for this
site", so a login typed and submitted by hand never reached the vault.
How
extension/src/content/inline.ts,wireForm,submittedLogin): each detected login field's form listens forsubmitandfor clicks on its submit buttons. A capture needs
isTrustedand an activeuser gesture (
navigator.userActivation), becauserequestSubmit()from pagescript also fires a trusted
submit. It needs a username next to thepassword too: a password-only form would blank the stored username on Update.
A change-password form gives its new password. Card codes and one-time codes
(
cc-*,one-time-code) are skipped, and a password field toggled to textbefore the submit still counts.
extension/src/background.ts):inline-submittedstores{ id, url, username, password }per tab inchrome.storage.session, withurltaken from the browser-stampedsender.url, never from the page. Thepair survives a service worker restart. It is dropped once answered, when the
tab closes, after 3 page loads, or on the first page load after 5 minutes.
Every read-then-write of these pairs goes through one promise chain, because
storage has no transactions.
src-tauri/src/modules/browser/actions.rs,check_login): the newread-only
check-loginaction compares the pair with the submitted host'sexact-host entries (the inline rule) and answers
new,changedorunchanged, plus those entries' id, title and username. It never returns apassword. It answers
other-sitewhen the page asking to show the prompt isnot on the submit's site by the vault's Domain rule. So
login.example.comto
www.example.comstill prompts, and an unrelated site never shows anothersite's logins.
get_logins' URL loop moved intopage_match, which bothactions share; no behaviour change.
promptSave,showPrompt): the existing picker is opened with noanchor, so it reuses the closed shadow root, rendering and keyboard handling.
Add and Update are guarded rows and pass all six clickjacking guards. "Update
an existing login" and Cancel write nothing and need a trusted event only.
Add and Update send the pair's id with
inline-save, and the worker refuseson a mismatch, so a click saves only the pair the prompt shows. A new submit
closes a prompt that is showing. The save itself is the existing
save-loginwith
via: "inline", so an update still requiressame_hostin Rust and theold password lands in history.
the app is not running; the last two keep the pair for a later page load. A
page load with nothing pending never connects to the app.
SECURITY.md(new trustboundary and residual risks), five new entries in
KNOWN-LIMITS.md,ARCHITECTURE.mdflow 5 and the extension rules inSUBCLAVE.md.Testing
cargo test --workspace --locked(325 passed),cargo clippy --workspace --all-targets --locked -- -D warnings,cargo fmt --all -- --check.pnpm run verify(17/17),pnpm run format:check,pnpm --filter subclave-extension typecheck, the chrome and firefox builds(
content.jsis 18,096 bytes, under the 30 KB cap).pnpm --filter subclave-extension test: 83 passed. Newsave.spec.ts(17tests) covers Add, Update, picking the entry from a list, a stored login
getting no prompt, the locked vault, Cancel, the 3-load limit, a
change-password form that stays on the page, an untrusted submit, a submit
without a user gesture, a show-password toggle click, a service worker
restart, a stale prompt after a newer submit, a new submit closing the
prompt, another site, a checkout form's card code, a password shown as text,
and the prompt staying in view when the window narrows.
guard.spec.tsgainsone guard test each for the Add and Update rows.
check_login_statesin Rust covers all states, Trash, Domain-only entries,a sibling host of the same site, another site, and a missing parameter.
Each rule above was checked by breaking it in the production code: every
such change turned its test red, and the suite went green again once it was
restored.
By hand on Windows with
pnpm tauri:devand the built extension: the prompton Firefox, a sign-in that posts and redirects, and one that stays on the
page, each after the service worker had gone idle for more than 30 s. All
worked.
pnpm exec tsc --noEmitcleanManual smoke-test of the affected feature
(If you touched
src-tauri/)cargo checkclean(If UI) tested in
pnpm tauri:dev(isolated dev data dir)Screenshots / GIFs
None attached.
Notes for reviewer
SECURITY.md: after a user gesture, a page can plantvalues in its own form and submit them, which tells it whether that pair is
stored for its own exact host (whether the prompt shows). And another host of
the same site shows the prompt with the submitted host's entry titles and
usernames; its Add and Update can only save the pair the user typed to the
submitted host.
captured values, skipping the prompt while another top-layer element is
open, the
one-time-codeskip, and the guard delay restart when the cornerprompt moves.
save.spec.tsruns on Chromium only; Firefox was checked by hand.