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;
|
activeGroup: string | null;
|
||||||
error: string | null;
|
error: string | null;
|
||||||
loading: boolean;
|
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 = {
|
let currentState: PopupState = {
|
||||||
@@ -45,6 +51,8 @@ let currentState: PopupState = {
|
|||||||
activeGroup: null,
|
activeGroup: null,
|
||||||
error: null,
|
error: null,
|
||||||
loading: false,
|
loading: false,
|
||||||
|
capturedTabId: null,
|
||||||
|
capturedUrl: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getState(): PopupState {
|
export function getState(): PopupState {
|
||||||
@@ -103,6 +111,13 @@ function render(): void {
|
|||||||
// --- Init ---
|
// --- Init ---
|
||||||
|
|
||||||
async function init(): Promise<void> {
|
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.
|
// Check if extension is configured.
|
||||||
const setupResp = await sendMessage({ type: 'get_setup_state' });
|
const setupResp = await sendMessage({ type: 'get_setup_state' });
|
||||||
if (setupResp.ok) {
|
if (setupResp.ok) {
|
||||||
|
|||||||
Reference in New Issue
Block a user