@@ -5,6 +5,7 @@ import { Observable } from "rxjs";
55import { BrowserClientVendors } from "@bitwarden/common/autofill/constants" ;
66import { BrowserClientVendor } from "@bitwarden/common/autofill/types" ;
77import { DeviceType } from "@bitwarden/common/enums" ;
8+ import { LogService } from "@bitwarden/logging" ;
89import { isBrowserSafariApi } from "@bitwarden/platform" ;
910
1011import { TabMessage } from "../../types/tab-messages" ;
@@ -32,8 +33,20 @@ export class BrowserApi {
3233 return BrowserApi . manifestVersion === expectedVersion ;
3334 }
3435
35- static senderIsInternal ( sender : chrome . runtime . MessageSender | undefined ) : boolean {
36- if ( ! sender ?. url ) {
36+ /**
37+ * Helper method that attempts to distinguish whether a message sender is internal to the extension or not.
38+ *
39+ * Currently this is done through source origin matching, and frameId checking (only top-level frames are internal).
40+ * @param sender a message sender
41+ * @param logger an optional logger to log validation results
42+ * @returns whether or not the sender appears to be internal to the extension
43+ */
44+ static senderIsInternal (
45+ sender : chrome . runtime . MessageSender | undefined ,
46+ logger ?: LogService ,
47+ ) : boolean {
48+ if ( ! sender ?. origin ) {
49+ logger ?. warning ( "[BrowserApi] Message sender has no origin" ) ;
3750 return false ;
3851 }
3952 const extensionUrl =
@@ -42,23 +55,28 @@ export class BrowserApi {
4255 "" ;
4356
4457 if ( ! extensionUrl ) {
58+ logger ?. warning ( "[BrowserApi] Unable to determine extension URL" ) ;
4559 return false ;
4660 }
4761
48- if ( ! sender . url . startsWith ( extensionUrl ) ) {
62+ // Normalize both URLs by removing trailing slashes
63+ const normalizedOrigin = sender . origin . replace ( / \/ $ / , "" ) ;
64+ const normalizedExtensionUrl = extensionUrl . replace ( / \/ $ / , "" ) ;
65+
66+ if ( ! normalizedOrigin . startsWith ( normalizedExtensionUrl ) ) {
67+ logger ?. warning (
68+ `[BrowserApi] Message sender origin (${ normalizedOrigin } ) does not match extension URL (${ normalizedExtensionUrl } )` ,
69+ ) ;
4970 return false ;
5071 }
5172
52- // these are all properties on externally initiated messages, not internal ones
53- if (
54- "tab" in sender ||
55- "documentId" in sender ||
56- "documentLifecycle" in sender ||
57- "frameId" in sender
58- ) {
73+ // We only send messages from the top-level frame, but frameId is only set if tab is set, which for popups it is not.
74+ if ( "frameId" in sender && sender . frameId !== 0 ) {
75+ logger ?. warning ( "[BrowserApi] Message sender is not from the top-level frame" ) ;
5976 return false ;
6077 }
6178
79+ logger ?. info ( "[BrowserApi] Message sender appears to be internal" ) ;
6280 return true ;
6381 }
6482
0 commit comments