fix: improve hostname parsing logic in getSiteName function#1417
Open
Hank076 wants to merge 119 commits into
Open
fix: improve hostname parsing logic in getSiteName function#1417Hank076 wants to merge 119 commits into
Hank076 wants to merge 119 commits into
Conversation
EntryStorage.import() ran parseInt() on the type and algorithm fields,
but both are persisted as their enum *names* ("hotp", "SHA256"), so
parseInt always returned NaN and fell back to the defaults. Every
imported entry therefore became TOTP/SHA1, losing HOTP/Steam/Battle/hex
types and SHA256/SHA512 digests.
Map the stored name back to the enum (case-insensitive for algorithm),
guarding with a typeof-number check so legacy numeric data still falls
back safely.
Refs Authenticator-Extension#1292, Authenticator-Extension#405, Authenticator-Extension#1442, Authenticator-Extension#1294, Authenticator-Extension#1089
second % 60 keeps the sign of the dividend in JS, so a negative clock offset could leave state.second negative; the old "+ 60" only covered offsets down to -60. Use a positive modulo so any offset stays in 0..59. Refs Authenticator-Extension#1310
The otpauth importer rejected periods that were > 60 or not a divisor of 60, silently dropping valid values like 45, 90 or 120 and falling back to 30s, which produces wrong codes. Validate as a positive integer instead. Refs Authenticator-Extension#1271, Authenticator-Extension#1508
Toggling Smart Filter off also fired the "smart filter loosely matches the domain name" notification. Show it only when the toggle is turned on. Refs Authenticator-Extension#1282
An unset autolock falls back to a 30-minute default in setAutolock(), but the advisor treated "unset" the same as "disabled" and kept showing the warning. Only warn when autolock is explicitly set to 0. Refs Authenticator-Extension#1281
The algorithm and digits <select>s used v-model without the .number modifier, so the in-memory entry held string values (e.g. "2"). The generator compares the algorithm with === against the numeric enum, so every non-SHA1 choice fell through to SHA1 until the entry was reloaded from storage (which normalises the type). Add .number like the type select already does. Refs Authenticator-Extension#1184
The OneDrive OAuth code-for-token POST had no body at all, so Microsoft's token endpoint always rejected it and sign-in never completed. Unlike Google's endpoint (which accepts the params in the query string), Microsoft requires them in the x-www-form-urlencoded body. Add client_id, client_secret, code, redirect_uri and grant_type. Refs Authenticator-Extension#1494, Authenticator-Extension#1369, Authenticator-Extension#1408, Authenticator-Extension#1202, Authenticator-Extension#1424
The search box was only revealed by the clearFilter action, so a large account list never showed it on initial load or after unlocking with a passphrase. Reveal it after entries load when there are >= 10 of them and a smart filter isn't currently applied. Refs Authenticator-Extension#1496, Authenticator-Extension#1400
andOTP exports a flat JSON array using a "label" field instead of the keyed object with "account" that the importer expects, so its backups imported as empty/garbled. Detect the array form and map each entry to RawOTPStorage (label -> account with the redundant issuer prefix stripped, type lower-cased; EntryStorage.import normalises type/algorithm names). Refs Authenticator-Extension#1304
getQrUrl encoded the whole label, turning the issuer/account separator into %3A. Several authenticators (including Google Authenticator) split the label on a literal ":" and reject the %3A form, so the exported QR scanned as invalid. Encode issuer and account separately and strip the internal "::host" match suffix, matching the text backup and the otpauth spec. Refs Authenticator-Extension#1302
Autofill collected every text/number/tel/password input regardless of visibility, so the code could be written into a hidden honeypot or an off-screen field instead of the real 2FA box. Filter candidates by checkVisibility() (guarded for browsers without it). Refs Authenticator-Extension#1273, Authenticator-Extension#1136
moveCode received from/to positions in the displayed (pinned-first) order but spliced and re-indexed the raw entries array, so dragging scrambled the list whenever any entry was pinned. Reorder the displayed view first, then write back sequential indices. No change when nothing is pinned. Refs Authenticator-Extension#1312, Authenticator-Extension#1428
The issuer/account edit inputs let keydown events bubble to the list's arrow-key navigation handlers, so pressing left/right to move the text cursor moved focus to another entry, blurred the field and committed the edit (e.g. saving an emptied account). Stop keydown propagation on those inputs. Refs Authenticator-Extension#495
…ding
failedCount filtered Promise.allSettled results with `!res`, but those are
always-truthy {status,value} objects, so the count was always 0 and both
failure branches were dead code — a fully failed otpauth-migration import
still reported "migrationsuccess". Inspect status/value instead.
Refs code review §2.2
chrome.storage.sync.set was awaited with no error handling, so hitting the sync quota (MAX_ITEMS / QUOTA_BYTES_PER_ITEM) rejected as a silent unhandled rejection while the entry stayed in the Vuex view but was never persisted — the long-standing "can't add more than N accounts" data loss. Wrap the write with a clear error, and surface it when adding an account so a save that failed no longer shows a phantom entry. Refs code review §2.1
… codes isMatchedEntry matched the bound host with an unanchored indexOf and matched the page-controlled <title> as a substring, so a hostile page at google.com.attacker.com (or just <title>Google</title>) could be the sole match and receive the user's live OTP via the autofill command. Add a strict mode (used by autofill) that anchors the host match to a real domain boundary and drops the title hint; display filtering stays loose. Refs code review §2.3
nextCode() guarded on this.$store.state.style.hotpDisabled, but the flag lives one level deeper (state.style.style.hotpDisabled), so the guard was always undefined and every click advanced and persisted the HOTP counter with no rate limit. The disabled CSS class also read a misspelled `hotpDiabled` and never applied. Fix both. Refs code review §3.4
…ckup getOneLineOtpBackupFile reassigned otpStorage.issuer/account in place, but that object is the live Vuex export state, so generating a one-line backup corrupted the stored issuer/account (colons stripped, values %-encoded) for any subsequent JSON backup and double-encoded on a repeat download. Compute sanitized values into locals instead. Refs code review §3.3
…tion The hand-rolled protobuf walker trusted every attacker-controlled length byte and read past the buffer (subBytesArray returns undefined), and indexed the algorithm/digits/type lookup tables with raw bytes — out-of-range values produced otpauth://undefined/...&algorithm=undefined, silently importing entries that generate wrong codes. decodeURIComponent on a missing/garbled data= param could also throw and abort the whole import batch. Guard the data= extraction and decode, bounds-check each field before reading, skip entries with out-of-range algorithm/digits/type, and wrap the caller so one bad payload can't abort a mixed import. Refs code review §3.2
The confirm action added an anonymous window "confirm" listener on every dispatch and never removed it, so listeners accumulated and a single confirm event re-fired every stale one, resolving old promises unexpectedly. Use a named handler that removes itself when it fires. Refs code review §3.5
applyPassphrase switches to LoadingPage before decrypting/migrating, which can throw (e.g. "argon2 did not return a hash!"). The caller awaited without catching, so the error became an unhandled rejection and the UI was stuck on LoadingPage with no way back. Catch it, return to EnterPasswordPage and alert. Refs code review §3.6
Each argon2 call added an anonymous window "message" listener that was never removed and resolved on the first message to arrive, with no link between request and reply — overlapping hash/verify calls (e.g. the v3 multi-key unlock loop) could resolve with the wrong response, and listeners piled up. Route every call through one helper that tags the request with a unique id, only accepts the reply from the sandbox iframe matching that id, removes its listener, and times out instead of hanging forever. Collapse the six copied inline postMessage blocks (Accounts, import) onto argonHash/argonVerify. Refs code review §3.1, §3.6 (sandbox timeout)
crypto-js is imported and run at runtime (OTP secret AES encrypt/decrypt in key-utilities, encryption, migration, etc.) but was declared under devDependencies, so an SBOM or `npm install --omit=dev` would drop the actual crypto library. @types/lodash is type-only and was in dependencies. Swap them to the correct sections. Refs code review §5, §6
The base webpack config sets devtool: "source-map" for development; the prod config merged it through, so release artifacts shipped full source maps (and build.sh copies the .map files into the package). Override devtool: false for production. Refs code review §5
The fork notice ended with `read -n1` (Press any key to continue), which hangs any non-interactive/automated build on a fork remote. Keep the notice but drop the blocking read. Refs code review §5
…mport decryptBackupData JSON.parsed the AES-decrypted v2/v3 payload with no guard, so one entry that decrypts to invalid UTF-8/JSON threw and aborted the whole backup import. Wrap it and continue past the bad entry. Refs code review §6
new Date(header).getTime() returns NaN for an unparseable Date header, which made the offset NaN and fell through to "clock_too_far_off" — a misleading state. Return "updateFailure" instead. Refs code review §6
The Dropbox upload handler only special-cased 401 and otherwise JSON-parsed the body, so a 5xx or HTML error page would throw or be misread. Reject non-2xx responses with a clear error. Refs code review §6
New keys introduced by the redesign: theme_auto, settings_appearance/ settings_general, onboarding_*, vault_locked/unlock, qr_transfer_*, backup_on_device/cloud_sync/connected/not_connected, edit_accounts, show_qr, pin_to_top/unpin, showing_codes_for/other_accounts/filter_to_site. Added to en (default_locale) and zh_TW; other locales fall back to en.
…stem Replace the SCSS themify mixin and the 7 named themes with CSS custom-property design tokens (OKLCH accents). Themes are now light / dark / auto (prefers-color-scheme) / accessibility (high contrast) / compact; the redundant simple and flat themes are dropped and mapped to light. Switch to a system font stack (no bundled webfonts) and widen the popup to 360px. Restyle every surface to the new visual language (per the Claude Design spec): rounded cards, monogram avatars, a depleting bottom countdown bar with a seconds badge, a copy toast, an onboarding first-run view, and redesigned unlock / loading / manage-password / backup / add-account / preferences / permissions / options / import screens. Settings use sectioned card rows with pill toggles. Move the pin and Show-QR actions to a right-click context menu; the QR now opens in a bottom sheet showing the account. The smart-filter view groups matched (accent-bordered) vs. other (dimmed) accounts under a toggleable "showing codes for <site>" banner that only appears when it actually segregates the list. Also fix: opening search with "/" no longer breaks the search box layout.
Redesign the extension UI onto a CSS custom-property design system (light/dark/auto/accessibility/compact), restyling every screen and moving pin/QR actions into a right-click menu.
Plan to bind each OTP entry to a host and require a host match before autofilling into a page, closing the popup copyCode path that injected live codes without any origin check.
Bind each OTP entry to a host (new `host` field) and only autofill a live code into a page whose real host matches it. This closes the popup copyCode path that injected codes into any active tab with no origin check, and unifies both autofill paths (popup click and keyboard command) on a strict "no bound host, no injection" rule. - add `host` to OTPEntry / RawOTPStorage; encryption covers it via the serialized JSON, and legacy "issuer::host" encodings migrate on read - isMatchedEntry strict matching now trusts only the bound host - manual add page gains a host field; QR scans pre-bind the scanned tab host; entry edit mode can set or fix the host
Host-bound autofill: bind each OTP entry to a host and require a host match before injecting a live code into a page. Adds a host field to the manual add page, pre-binds the scanned tab host on QR import, and lets entry edit mode set the host.
Block all cloud uploads (Dropbox / Drive / OneDrive) until a master password is set, so plaintext OTP secrets can never leave the device. Without a password the export is unencrypted, and both the scheduled and manual upload paths previously sent it to the cloud as-is. - cloudBackupAllowed() gates every provider's upload() - runScheduledBackup() skips entirely when no password is set - the backup page hides the cloud providers and explains why
Require a master password before any cloud backup so plaintext OTP secrets never leave the device. Blocks every provider's upload(), skips scheduled backups, and hides the cloud providers in the UI until a password is set.
Match the redesign spec: a wide card with a header, underline tabs (Backup file / QR images / Setup codes) that switch panels, and a file panel with a dashed dropzone plus an encrypted-file passphrase state (file card with name + "encrypted" badge, passphrase field, decrypt button). Backing out of the picker cancels the pending read so a re-pick doesn't double-process.
The Backup page's "import" row was a plain in-page link, so clicking it navigated the popup to the full-width import page and broke its layout. Open it in a new tab instead, matching the other import entry points.
Redesign the import page (interactive tabs + file dropzone/passphrase) and fix the Backup page opening import inside the popup.
EntryStorage.get() rebuilt each OTPEntry from an explicit field list that omitted the new host field, so an unencrypted entry's bound host was dropped on every reload (e.g. after exporting a backup, then editing it again). Carry host through, matching import(). Adds a storage round-trip regression test.
Preserve an entry's bound host across a storage reload: EntryStorage.get() dropped the new host field when rebuilding entries, so an unencrypted entry's host was cleared after exporting a backup. Adds a round-trip test.
Rename from the upstream "Authenticator" to a distinct brand so the fork can ship to the Chrome Web Store without tripping the impersonation policy: - extName / extShortName / extDesc in the en locale - new countdown-ring "076 721" app icon (svg master + 16/19/38/48/128 png) - cobalt accent (hue 258) replacing the indigo design token
Fill the previously empty credentials stub with the fork's own Dropbox app key so cloud backup can authenticate. This is a public OAuth client id (it appears in the auth URL and the shipped bundle), not a secret.
uploadBackup runs in the background service worker, where XMLHttpRequest does not exist, so every Dropbox upload threw a ReferenceError before reaching the network. Port the upload to fetch (available in both popup and worker) and surface the Dropbox response body on a non-2xx so scope/path errors are diagnosable instead of a bare HTTP status. Also rebrand the user-facing 401 strings to OTPilot.
The cloud backup pages shared three bugs, all surfaced while bringing Dropbox online; they touch the same components so they land together: - pass the active Encryption instance (encryption.get(defaultEncryption)) to upload() instead of the whole Vuex Map, fixing "getEncryptionStatus is not a function" on Dropbox / Drive / OneDrive - notify an open popup when Dropbox auth finishes or is cancelled, and add a connecting indicator so the page leaves the sign-in view automatically - default cloud backup to encrypted (matching backup.ts upload behaviour) and only show the unencrypted warning after connecting, when the user has explicitly opted out; drop the dead === null lazy-init from the getters - redesign the Dropbox page UI
The rebrand only renamed the en locale, so non-English users still saw the old name (sometimes translated, e.g. de "Authentifizierung"). A brand name must not be translated: set extName/extShortName to "OTPilot Authenticator" / "OTPilot" in the remaining 43 locales. If the locale generator is re-run, it must keep the brand name verbatim.
Replace the upstream Google client_id in the Chrome manifest with the fork's own OAuth client, and add the Web Store public "key" so the unpacked dev build and the published build share one extension id. getAuthToken binds the OAuth client to the extension id, so a stable id is required for Drive to work in both dev and production.
The dropzone was a bare <label> with no drag handlers, so dropping a file did nothing — only click-to-browse worked. Add @dragover.prevent (required for the drop event to fire) and a dropFile handler that reads dataTransfer.files and reuses importFile through a shimmed event.
- fill the zh_TW gap (host, backup_requires_password) - add zh_CN and ja translations for the 35 fork-added strings - replace every standalone "Authenticator" (the app) with "OTPilot" across all locales, keeping "OTPilot Authenticator" and "Google Authenticator" intact
Two UI papercuts found while testing cloud backup: - argon2.verify() rejects on a wrong passphrase (the common case) as well as on a malformed hash; stop console.error-ing every mistyped password. - widen SelectInput's modelValue to include Boolean. DrivePage/OneDrivePage bind a boolean isEncrypted to the encrypted selector, which tripped a Vue prop-type warning.
Google blocks custom-URI-scheme (Chrome App) OAuth clients for new apps, so getAuthToken can no longer be used. Switch Drive to launchWebAuthFlow with a Web-application client and the extension's own chromiumapp.org redirect, and rewrite getToken/refreshToken/getFolder/upload from XMLHttpRequest to fetch — they run in the MV3 background service worker, where XHR is undefined. The background now broadcasts "driveauthdone" so DrivePage leaves the sign-in view automatically, mirroring the Dropbox flow.
Match the redesign spec: a "Sync with Google Drive" connect prompt with the Drive logo, description and encrypted-before-upload note, and a connected state showing the account, an encrypted-backup selector, a risk note, and back-up-now / log-out actions. Reuses the shared cloud-page styles with a Drive brand variant.
- grey out and disable the OneDrive cloud backup row (not implemented yet) with a "not available yet" label (backup_unavailable; translated for en/zh_TW/zh_CN/ja, the rest fall back to en) - use the official multi-colour Google Drive logo in the backup list so it matches the Drive page header - replace the popup header logo with the OTPilot countdown ring to match the app icon
Re-apply the Traditional Chinese wording that was accidentally reverted when committing the privacy policy (0c6c152).
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.
Fix IP address handling priority