Feature/sidebar mode audit#2674
Feature/sidebar mode audit#2674piyalbasu wants to merge 24 commits intofeature/sidebar-mode-featurefrom
Conversation
…eliability improvements Port all sidebar changes from sidebar-mode-audit: add Firefox sidebar_action support, harden trust boundaries (sender validation, port origin checks, isFromExtensionPage guard on OPEN_SIDEBAR), replace broadcast messaging with direct port communication, add safeResolve to prevent double-resolving promises, fix boolean coercion for isOpenSidebarByDefault, reject pending signing requests on sidebar close, remove dead SIDEBAR_REGISTER/UNREGISTER code, and add extension protocol guard to isSidebarMode. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ng flow Prevent rapid-fire signing phishing in sidebar mode by showing an interstitial screen when a new signing request arrives while the user is already reviewing one. Also hardens the SidebarSigningListener with a route allowlist (only known signing routes are navigable) and runtime type guards on port messages. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR improves the browser extension’s “sidebar mode” integration by moving sidebar communication to a long-lived port, strengthening cross-browser (Chrome/Firefox) sidebar behavior, and adding UI + tests to handle concurrent signing flows more safely.
Changes:
- Introduces a long-lived sidebar port connection and uses it for sidebar navigation + cleanup on disconnect.
- Adds Firefox sidebar support (
sidebar_action) and updates popup/sidebar opening logic for both Chrome and Firefox. - Adds a “new signing request” interstitial route/view plus new unit tests for sidebar and settings behavior.
Reviewed changes
Copilot reviewed 22 out of 22 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| extension/src/popup/views/ConfirmSidebarRequest/index.tsx | Adds interstitial screen to gate concurrent signing requests in sidebar mode |
| extension/src/popup/views/ConfirmSidebarRequest/styles.scss | Styles for the new interstitial view |
| extension/src/popup/Router.tsx | Registers the new interstitial route |
| extension/src/popup/metrics/views.ts | Maps new route to a metrics event name |
| extension/src/popup/constants/routes.ts | Adds confirmSidebarRequest route constant |
| extension/src/popup/constants/metricsNames.ts | Adds a metric name for the new interstitial view |
| extension/src/popup/components/SidebarSigningListener/index.tsx | Switches from broadcast messages to port-based sidebar navigation; adds route gating |
| extension/src/popup/components/account/AccountHeader/index.tsx | Shows sidebar option for Chrome sidePanel and Firefox sidebarAction |
| extension/src/popup/helpers/navigate.ts | Ensures sidebar open supports Firefox and closes popup after opening |
| extension/src/popup/helpers/isSidebarMode.ts | Hardens sidebar-mode detection to extension origins only |
| extension/src/popup/locales/en/translation.json | Adds new strings for sidebar mode + interstitial |
| extension/src/popup/locales/pt/translation.json | Adds new strings for sidebar mode + interstitial (but see comment) |
| extension/src/background/index.ts | Adds sidebar port connection listener; rejects/cleans queues on sidebar disconnect; adds Firefox click handling |
| extension/src/background/messageListener/freighterApiMessageListener.ts | Uses port for sidebar navigation; uses browser.runtime.getURL; adds safeResolve guards |
| extension/src/background/messageListener/popupMessageListener.ts | Removes sidebar register/unregister messages; restricts OPEN_SIDEBAR to extension-page senders |
| extension/src/background/messageListener/handlers/loadSettings.ts | Changes parsing of isOpenSidebarByDefault (but see comment) |
| extension/src/background/messageListener/handlers/saveSettings.ts | Changes parsing/return of isOpenSidebarByDefault (but see comment) |
| extension/src/background/messageListener/tests/sidebar.test.ts | Adds tests for sidebar authorization + port/windowId management |
| extension/src/background/messageListener/tests/loadSaveSettings.test.ts | Adds tests for sidebar-by-default setting parsing/behavior (but see comment) |
| extension/public/static/manifest/v3.json | Adds Firefox sidebar_action manifest configuration |
| @shared/constants/services.ts | Removes sidebar register/unregister service types; adds SIDEBAR_NAVIGATE constant |
| @shared/api/types/message-request.ts | Removes SidebarRegister/Unregister message types |
Comments suppressed due to low confidence (3)
extension/src/background/messageListener/freighterApiMessageListener.ts:446
browser.windows.onRemoved.addListenerhere does not filter by the popup window id, meaning unrelated window closes can trigger a declined response. Additionally, the anonymous listener can’t be removed and will accumulate across requests. Filter onpopup.idand remove the listener after resolve.
} else {
browser.windows.onRemoved.addListener(() =>
safeResolve({
// return 2 error formats: one for clients running older versions of freighter-api, and one to adhere to the standard wallet interface
apiError: FreighterApiDeclinedError,
error: FreighterApiDeclinedError.message,
}),
);
extension/src/background/messageListener/freighterApiMessageListener.ts:535
- This
browser.windows.onRemovedlistener is unscoped (nowindowIdcheck) and anonymous (can’t be removed), so it can decline requests when unrelated windows close and will leak listeners over time. Scope it topopup.idand unregister it after resolving.
} else {
browser.windows.onRemoved.addListener(() =>
safeResolve({
// return 2 error formats: one for clients running older versions of freighter-api, and one to adhere to the standard wallet interface
apiError: FreighterApiDeclinedError,
error: FreighterApiDeclinedError.message,
}),
);
extension/src/background/messageListener/freighterApiMessageListener.ts:638
browser.windows.onRemoved.addListeneris added without checking which window was removed, so any window close can cause a decline. Because the callback is anonymous it can’t be removed, causing listener buildup. Use a named handler withremovedWindowId === popup.idand remove it after resolve.
} else {
browser.windows.onRemoved.addListener(() =>
safeResolve({
// return 2 error formats: one for clients running older versions of freighter-api, and one to adhere to the standard wallet interface
apiError: FreighterApiDeclinedError,
error: FreighterApiDeclinedError.message,
}),
);
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
extension/src/background/messageListener/handlers/saveSettings.ts
Outdated
Show resolved
Hide resolved
extension/src/background/messageListener/__tests__/loadSaveSettings.test.ts
Outdated
Show resolved
Hide resolved
extension/src/background/messageListener/freighterApiMessageListener.ts
Outdated
Show resolved
Hide resolved
…form Firefox requires sidebarAction.open() in a synchronous user gesture handler and has no equivalent to Chrome's setPanelBehavior. Remove the broken setPopup/onClicked approach, hide the preference toggle on Firefox, add open_at_install: false to prevent auto-opening, and fix boolean storage reads for isOpenSidebarByDefault. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
browser.storage.local preserves types, so getItem returns boolean true/false, not string "true"/"false". Update test mocks to match. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…after safeResolve Agent-Logs-Url: https://github.com/stellar/freighter/sessions/33844927-58ec-481b-9846-80c0e28a34b4 Co-authored-by: piyalbasu <6789586+piyalbasu@users.noreply.github.com>
Left-align layout, use smaller square icon, pill-shaped buttons, updated button variants and text per new design. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 21 out of 21 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
@copilot apply changes based on the comments in this thread |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…disconnect Agent-Logs-Url: https://github.com/stellar/freighter/sessions/7e15d7bf-0539-4ceb-80a3-eb9ae1ce7a9b Co-authored-by: piyalbasu <6789586+piyalbasu@users.noreply.github.com>
Both issues from that review are addressed in 26200d0 — see replies on the individual comment threads above. |
Code reviewFound 3 issues:
Every other handler (
freighter/extension/src/popup/views/ConfirmSidebarRequest/index.tsx Lines 39 to 42 in 26200d0
Previously freighter/extension/src/popup/helpers/navigate.ts Lines 30 to 35 in 26200d0 🤖 Generated with Claude Code - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
- Add sidebarQueueUuids tracking to setAllowedStatus handler so sidebar disconnect properly rejects the pending promise instead of hanging - ConfirmSidebarRequest "Reject" now extracts UUID from the next route and calls rejectAccess so the dapp promise resolves immediately - Ensure window.close() runs in the catch block of openSidebar() so the popup is closed even when the side panel API throws Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 22 out of 22 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…reload chrome.sidePanel.open() can reload the sidebar page, causing a brief port disconnect/reconnect. The disconnect handler was immediately rejecting all in-flight signing requests before the new port could connect. Use a cancellable deferred cleanup: schedule rejection on disconnect, but cancel it if a new sidebar port connects before it fires. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…mSidebarRequest reject Agent-Logs-Url: https://github.com/stellar/freighter/sessions/da099665-1448-4e91-b2eb-4daa952a09bb Co-authored-by: piyalbasu <6789586+piyalbasu@users.noreply.github.com>
Addressed in c6e0f88:
|
Move sidebarQueueUuids population from freighterApiMessageListener (eager, on openSigningWindow return) into the MARK_QUEUE_ACTIVE handler in popupMessageListener (on signing view mount/unmount), matching how activeQueueUuids is managed. Extract duplicated onWindowRemoved logic into a shared rejectOnWindowClose helper. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
16 tests covering deferred disconnect cleanup (cancellation on reconnect, queue rejection, stale port handling) and sidebarQueueUuids management via MARK_QUEUE_ACTIVE. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 25 out of 25 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
extension/src/background/messageListener/handlers/rejectSigningRequest.ts
Show resolved
Hide resolved
Extract setSidebarPort, clearSidebarPort, getSidebarPort from freighterApiMessageListener into their own module to reduce coupling. Update all imports across source and test files. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…void Agent-Logs-Url: https://github.com/stellar/freighter/sessions/2d47d7af-dc71-4412-8251-1486f2c3b5df Co-authored-by: piyalbasu <6789586+piyalbasu@users.noreply.github.com>
Add CLAUDE.md pointing to specs/ directory and a comprehensive sidebar mode spec covering architecture, signing flow, Firefox limitations, and E2E testing constraints. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 28 out of 28 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…cleanup Two fixes to sidebar queue management: 1. Move sidebarQueueUuids tracking from MARK_QUEUE_ACTIVE (view mount time) to openSigningWindow (routing time). This ensures requests behind the ConfirmSidebarRequest interstitial are properly cleaned up if the sidebar disconnects before the signing view mounts. 2. Only schedule deferred disconnect cleanup when the disconnecting port is the currently active sidebar port. Previously, a stale port disconnecting after a newer port connected would still schedule cleanup, potentially rejecting requests on the live sidebar. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
| @@ -0,0 +1,153 @@ | |||
| # Sidebar Mode Implementation Spec | |||
There was a problem hiding this comment.
I added this spec because there ended up being some "gotchas" with this implementation.
First, there's the difference in browser implementation
Second, the inability to add e2e tests with Playwright
I asked Claude how we can help future agents have an easier time making changes and it guided me towards this solution: this type of spec that is linked in CLAUDE.MD should help make any architectural decisions and prevent agents from going down dead-ends we've already investigated in the future
There was a problem hiding this comment.
It's also nice to have a human readable description of what we're doing here for future human devs! 😄
Includes isSidebarMode() in the view metrics for signTransaction, signAuthEntry, signMessage, grantAccess, and addToken so we can distinguish sidebar vs popup signing requests in analytics. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
@piyalbasu overall I think the PR is looking good! Posting here some findings and suggestions from superpowers for you to take a look - thanks! ImportantI1.
|
| Location | Issue |
|---|---|
ConfirmSidebarRequest/styles.scss:27 |
--sds-clr-amber-* tokens — verify these exist in the SDS token set (CLAUDE.md only documents grayscale and red) |
ConfirmSidebarRequest/styles.scss:37 |
font-weight: 500 → use var(--font-weight-medium) |
ConfirmSidebarRequest/styles.scss:45 |
font-weight: 400 → use var(--font-weight-regular) |
queueCleanup.ts:18-22 |
Comment could link to the actual cleanup code location |
Summary Table
| Category | Count | Items |
|---|---|---|
| Important | 5 | I1-I5 (I4-I5 pre-existing) |
| Code Quality | 4 | C1-C4 |
| Suggestions | 4 | S1-S4 |
| Missing Tests | 3 | rejectSigningRequest, ConfirmSidebarRequest, route allowlist |
| Nits | 4 | Style conventions |
- M1: Make sender required in popupMessageListener so omitting it fails-closed instead of bypassing the extension-page guard - M2: Add sender.id check to sidebar port validation for defense-in-depth alongside the existing tab and URL checks - M3: Add isFromExtensionPage guard to REJECT_SIGNING_REQUEST handler - M4: Add runtime windowId type validation in OPEN_SIDEBAR handler Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This pull request introduces several improvements and bug fixes related to the extension's sidebar functionality, including more robust sidebar connection management, improved message routing, and better handling of signing request rejection. It also adds and updates tests to ensure correct behavior and compatibility across Chrome and Firefox.
Sidebar connection and lifecycle management:
sidebarPort.ts) to manage the sidebar's long-lived port, allowing direct communication and safer handling of sidebar state transitions. [1] [2]sidebarQueueUuidsto track which signing requests are handled by the sidebar. [1] [2]Message types and API changes:
REJECT_SIGNING_REQUESTservice type and message, and removed unused sidebar registration messages. [1] [2] [3]rejectSigningRequestAPI method for programmatically rejecting signing requests.Sidebar user experience and manifest:
sidebar_actionentry for better Firefox support and improved sidebar icon handling.Message routing and navigation:
Testing improvements:
These changes collectively make the sidebar more robust, reliable, and compatible across browsers, while ensuring that signing requests and user actions are handled safely and predictably.
Screen.Recording.2026-04-02.at.1.08.39.PM.mov