feat(ext/popup): snapshot activeTab at popup-open for fill_credentials (audit M5)
Extend PopupState with {capturedTabId, capturedUrl} populated via
chrome.tabs.query({active: true, currentWindow: true}) in init().
These are later passed with fill_credentials so the SW can verify
the captured tab's hostname hasn't changed out from under the user
before forwarding credentials. Combined with expectedHost in the
forwarded payload + content-side re-check in fill.ts, this closes
the TOCTOU window on the popup → SW → content fill path.
popup.ts stays under @ts-nocheck (Slice 6 removes it alongside the
item-* rewrites).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -33,6 +33,12 @@ export interface PopupState {
|
||||
activeGroup: string | null;
|
||||
error: string | null;
|
||||
loading: boolean;
|
||||
// Captured tab snapshot taken at popup-open. Used by fill_credentials
|
||||
// to guard against TOCTOU navigation — the SW re-checks this URL's
|
||||
// hostname against the tab's live URL before forwarding fill_credentials
|
||||
// to the content script. See router/popup-only.ts#handleFillCredentials.
|
||||
capturedTabId: number | null;
|
||||
capturedUrl: string;
|
||||
}
|
||||
|
||||
let currentState: PopupState = {
|
||||
@@ -45,6 +51,8 @@ let currentState: PopupState = {
|
||||
activeGroup: null,
|
||||
error: null,
|
||||
loading: false,
|
||||
capturedTabId: null,
|
||||
capturedUrl: '',
|
||||
};
|
||||
|
||||
export function getState(): PopupState {
|
||||
@@ -103,6 +111,13 @@ function render(): void {
|
||||
// --- Init ---
|
||||
|
||||
async function init(): Promise<void> {
|
||||
// Snapshot the active tab at popup-open — the fill path uses this
|
||||
// tabId/url pair so the SW can verify the tab hasn't navigated before
|
||||
// forwarding credentials (audit M5 + TOCTOU close via expectedHost).
|
||||
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
|
||||
currentState.capturedTabId = tab?.id ?? null;
|
||||
currentState.capturedUrl = tab?.url ?? '';
|
||||
|
||||
// Check if extension is configured.
|
||||
const setupResp = await sendMessage({ type: 'get_setup_state' });
|
||||
if (setupResp.ok) {
|
||||
|
||||
Reference in New Issue
Block a user