Skip to content

Feature/sidebar mode audit#2674

Open
piyalbasu wants to merge 24 commits intofeature/sidebar-mode-featurefrom
feature/sidebar-mode-audit
Open

Feature/sidebar mode audit#2674
piyalbasu wants to merge 24 commits intofeature/sidebar-mode-featurefrom
feature/sidebar-mode-audit

Conversation

@piyalbasu
Copy link
Copy Markdown
Contributor

@piyalbasu piyalbasu commented Apr 1, 2026

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:

  • Added a new module (sidebarPort.ts) to manage the sidebar's long-lived port, allowing direct communication and safer handling of sidebar state transitions. [1] [2]
  • Improved sidebar disconnect logic to only reject signing requests handled by the sidebar, preventing accidental cancellation of requests managed by popups.
  • Enhanced logic to differentiate between sidebar reloads and true disconnects, deferring cleanup to avoid prematurely rejecting requests.
  • Introduced sidebarQueueUuids to track which signing requests are handled by the sidebar. [1] [2]

Message types and API changes:

  • Added new REJECT_SIGNING_REQUEST service type and message, and removed unused sidebar registration messages. [1] [2] [3]
  • Added rejectSigningRequest API method for programmatically rejecting signing requests.

Sidebar user experience and manifest:

  • Updated the manifest to add a sidebar_action entry for better Firefox support and improved sidebar icon handling.
  • Clarified and improved logic for the "open sidebar by default" feature, including Chrome/Firefox compatibility notes.

Message routing and navigation:

  • Changed sidebar navigation to use direct port messaging instead of broadcasting, improving reliability and security. [1] [2]

Testing improvements:

  • Added and updated tests for sidebar behavior, settings, and port management to ensure robust handling of edge cases and cross-browser support. [1] [2]

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

piyalbasu and others added 3 commits April 1, 2026 13:12
…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>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.addListener here 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 on popup.id and 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.onRemoved listener is unscoped (no windowId check) and anonymous (can’t be removed), so it can decline requests when unrelated windows close and will leak listeners over time. Scope it to popup.id and 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.addListener is 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 with removedWindowId === popup.id and 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.

@piyalbasu piyalbasu added the don't review yet wip / tests in progress / missing videos or screenshots / pending self code-review label Apr 1, 2026
piyalbasu and others added 3 commits April 1, 2026 17:01
…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>
@piyalbasu piyalbasu removed the don't review yet wip / tests in progress / missing videos or screenshots / pending self code-review label Apr 1, 2026
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>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@piyalbasu
Copy link
Copy Markdown
Contributor Author

@copilot apply changes based on the comments in this thread

piyalbasu and others added 2 commits April 2, 2026 13:38
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 2, 2026

@copilot apply changes based on the comments in this thread

Both issues from that review are addressed in 26200d0 — see replies on the individual comment threads above.

@piyalbasu
Copy link
Copy Markdown
Contributor Author

Code review

Found 3 issues:

  1. setAllowedStatus is missing sidebarQueueUuids tracking, unlike every other signing handler

Every other handler (requestAccess, submitToken, submitTransaction, submitBlob, submitAuthEntry) adds its UUID to sidebarQueueUuids when routed to the sidebar and deletes it in safeResolve. setAllowedStatus was updated with the safeResolve pattern in this PR but omits both sidebarQueueUuids.add(uuid) and sidebarQueueUuids.delete(uuid). If the sidebar disconnects while a setAllowedStatus request is pending, the disconnect handler in index.ts won't find the UUID in sidebarQueueUuids, so the dapp's promise hangs until the TTL timeout instead of being rejected immediately.

await openSigningWindow(`/grant-access?${encodeOrigin}`, 400);
return new Promise((resolve) => {
let resolved = false;
const safeResolve = (value: any) => {
if (resolved) return;
resolved = true;
resolve(value);
};
setTimeout(
() =>
safeResolve({
apiError: FreighterApiDeclinedError,
error: FreighterApiDeclinedError.message,
}),
QUEUE_ITEM_TTL_MS,

  1. ConfirmSidebarRequest "Reject" button navigates home but does not reject the pending signing request

handleReject only calls navigate(ROUTES.account). The new signing request that triggered this interstitial is still sitting in responseQueue with no resolution. The dapp is left with a hanging promise until the 5-minute TTL fires or the sidebar disconnects. All other rejection paths call responseQueueItem.response(undefined) to signal rejection back to the dapp immediately.

};
const handleReject = () => {
navigate(ROUTES.account);
};

  1. openSidebar() in navigate.ts moves window.close() inside the try block, so the popup is never closed on error

Previously window.close() ran unconditionally after the try/catch. Now if chrome.sidePanel.setOptions or .open throws (e.g., API unavailable), execution jumps to catch and window.close() is skipped, leaving the popup orphaned.

await chrome.sidePanel.open({ windowId: win.id! });
}
window.close();
} catch (e) {
console.error("Failed to open sidebar:", e);
}

🤖 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>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
piyalbasu and others added 2 commits April 2, 2026 16:47
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>
Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 2, 2026

Code review

Found 3 issues:...

Addressed in c6e0f88:

  1. setAllowedStatus sidebarQueueUuids tracking — already present in the current code (both sidebarQueueUuids.add and .delete were in place from the previous commit), no change needed.
  2. ConfirmSidebarRequest Reject not rejecting — fixed by introducing REJECT_SIGNING_REQUEST and rejectSigningRequest handler that cleans all five queues; handleReject now calls it.
  3. openSidebar() window.close() — already fixed in the current code (window.close() is in both the try and catch blocks), no change needed.

piyalbasu and others added 2 commits April 2, 2026 16:58
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>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

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>
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>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

piyalbasu and others added 2 commits April 3, 2026 12:06
…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
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
@CassioMG
Copy link
Copy Markdown
Contributor

CassioMG commented Apr 3, 2026

@piyalbasu overall I think the PR is looking good! Posting here some findings and suggestions from superpowers for you to take a look - thanks!

Important

I1. REJECT_SIGNING_REQUEST handler lacks isFromExtensionPage guard

File: extension/src/background/messageListener/popupMessageListener.tsREJECT_SIGNING_REQUEST case (~line 355)

Unlike OPEN_SIDEBAR which explicitly checks if (!isFromExtensionPage) return { error: "Unauthorized" }, REJECT_SIGNING_REQUEST has no sender validation. This handler resolves a dApp's pending signing promise with undefined — defense-in-depth warrants an explicit guard.

Fix: Add if (!isFromExtensionPage) return { error: "Unauthorized" } at the top of the case.

I2. Duplicated queue cleanup logic

Files: extension/src/background/index.ts (disconnect handler, ~lines 121-145) and extension/src/background/messageListener/handlers/rejectSigningRequest.ts

The disconnect handler duplicates the exact same iterate-find-splice-resolve pattern across all 5 queues. If either is updated (e.g., a new queue type), the other must be updated in lockstep.

Fix: Have the disconnect handler call rejectSigningRequest for each UUID, or extract a shared removeFromAllQueues(uuid, queues) helper.

I3. ConfirmSidebarRequest uses raw HTML instead of design system components

File: extension/src/popup/views/ConfirmSidebarRequest/index.tsx — lines 68-76

Uses <h1> with custom CSS font-size/font-weight and <div> for body text. Per project conventions: "Always use Text or Heading from @stellar/design-system — never raw HTML text elements with custom font styles."

Fix:

// Replace:
<h1 className="ConfirmSidebarRequest__title">{t("New Signing Request")}</h1>
<div className="ConfirmSidebarRequest__body">{t("A new signing request...")}</div>

// With:
<Heading as="h1" size="md" addlClassName="ConfirmSidebarRequest__title">{t("New Signing Request")}</Heading>
<Text as="p" size="sm" addlClassName="ConfirmSidebarRequest__body">{t("A new signing request...")}</Text>

Also in styles.scss: replace font-weight: 500 with var(--font-weight-medium), font-weight: 400 with var(--font-weight-regular), and remove the hardcoded font-family: "Inter", sans-serif.

I4. requestAccess and setAllowedStatus missing rejectOnWindowClose for popup windows (pre-existing)

File: extension/src/background/messageListener/freighterApiMessageListener.ts

requestAccess (~line 174) has if (popup === null) (sidebar TTL fallback) but no else if (popup !== null) branch calling rejectOnWindowClose. If the user closes the popup window, the dApp promise hangs until TTL (~5 min). Same for setAllowedStatus (~line 770).

Compare with submitTransaction, submitBlob, submitAuthEntry which all have the three-way branch: undefined (error), null (sidebar TTL), !== null (popup window close via rejectOnWindowClose).

Note: Pre-existing — not introduced by this PR. But since this audit added rejectOnWindowClose as a consolidated helper, it's a good opportunity to fix these two.

Fix: Add else if (popup !== null) { rejectOnWindowClose(popup.id!, safeResolve); } to both flows.

I5. submitToken missing sidebar TTL fallback (pre-existing)

File: extension/src/background/messageListener/freighterApiMessageListener.ts — submitToken (~line 283)

Has popup === undefined (error) and popup !== null (window close via rejectOnWindowClose), but no popup === null (sidebar) branch with TTL fallback. Inconsistent with the other signing flows. The sidebar disconnect cleanup via sidebarQueueUuids covers this, but the explicit TTL fallback is a safety net.

Note: Pre-existing.


Code Quality

C1. import statement placed after function definition

File: extension/src/background/messageListener/freighterApiMessageListener.ts — ~line 126

const rejectOnWindowClose = (...) => { ... };

import { DataStorageAccess } from "background/helpers/dataStorageAccess";

Import placed between the rejectOnWindowClose function and the main export. Works due to hoisting, but imports should be at the top of the file.

C2. safeResolve typed as (value: any) => void

File: extension/src/background/messageListener/freighterApiMessageListener.tsrejectOnWindowClose and all safeResolve closures

Using any loses type safety on the resolve value. Could be typed generically or at minimum use unknown.

C3. SIDEBAR_NAVIGATE exported as plain string constant, not in an enum

File: @shared/constants/services.ts:77

All other service types use the SERVICE_TYPES or EXTERNAL_SERVICE_TYPES enums. A comment explaining why it's separate (only used on ports, not through standard message routing) would help.

C4. safeResolve duplicated 6 times inline

File: extension/src/background/messageListener/freighterApiMessageListener.ts

The identical closure appears in all 6 signing handlers. Consider extracting a makeSafeResolve helper:

const makeSafeResolve = <T>(resolve: (value: T) => void) => {
  let resolved = false;
  return (value: T) => {
    if (resolved) return;
    resolved = true;
    resolve(value);
  };
};

Suggestions

S1. Derive SIGNING_ROUTE_PREFIXES from ALLOWED_NAV_PREFIXES

File: extension/src/popup/components/SidebarSigningListener/index.tsx

Two arrays that must stay in sync. Derive one from the other:

const SIGNING_ROUTE_PREFIXES = [...ALLOWED_NAV_PREFIXES, ROUTES.confirmSidebarRequest];

S2. Extract 500ms debounce to a named constant

File: extension/src/background/index.ts — ~line 147

export const SIDEBAR_DISCONNECT_DEBOUNCE_MS = 500;

S3. Guard ConfirmSidebarRequest route against non-sidebar access

File: extension/src/popup/Router.tsx

The route is registered globally. Consider redirecting to ROUTES.account if !isSidebarMode().

S4. window.close() in navigate.ts — use finally

File: extension/src/popup/helpers/navigate.ts

window.close() now appears in both try and catch. A finally block would be clearer.


Missing Test Coverage

  1. rejectSigningRequest handler — No dedicated tests. Should verify: UUID found in responseQueue -> response called with undefined; UUID not found -> Sentry exception captured; missing UUID -> early return; all queue types cleaned up.

  2. ConfirmSidebarRequest componentisValidNextRoute contains security-sensitive open-redirect validation. Should test edge cases: //evil.com, javascript:alert(1), empty strings, encoded characters, valid routes.

  3. SidebarSigningListener route allowlist — No tests verifying disallowed routes are blocked.


Nits

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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants