PageSourceSearch

https://contributing-docs.pages.dev/assets/js/e265f9ae.e88dac92.js

js contributing-docs.pages.dev collected 2026-10-03 06:41:49 UTC 36,200 bytes, 1 lines download raw bytes

1"use strict";(globalThis.webpackChunk_bitwarden_contributing_docs=globalThis.webpackChunk_bitwarden_contributing_docs||[]).push([[6160],{83371(e,t,n){n.r(t),n.d(t,{assets:()=>r,contentTitle:()=>a,default:()=>d,frontMatter:()=>l,metadata:()=>i,toc:()=>c});const i=JSON.parse('{"id":"architecture/deep-dives/autofill/autofill-menu","title":"Inline Autofill Menu","description":"The Inline Autofill Menu allows users to access and autofill credentials from their Bitwarden vault","source":"@site/docs/architecture/deep-dives/autofill/autofill-menu.md","sourceDirName":"architecture/deep-dives/autofill","slug":"/architecture/deep-dives/autofill/autofill-menu","permalink":"/architecture/deep-dives/autofill/autofill-menu","draft":false,"unlisted":false,"editUrl":"https://github.com/bitwarden/contributing-docs/tree/main/docs/architecture/deep-dives/autofill/autofill-menu.md","tags":[],"version":"current","sidebarPosition":5,"frontMatter":{"sidebar_position":5},"sidebar":"architecture","previous":{"title":"Shadow DOM","permalink":"/architecture/deep-dives/autofill/shadow-dom"},"next":{"title":"Read-Only Database Replicas","permalink":"/architecture/deep-dives/database-replicas"}}');var s=n(74848),o=n(28453);const l={sidebar_position:5},a="Inline Autofill Menu",r={},c=[{value:"Project structure",id:"project-structure",level:2},{value:"Implementation details",id:"implementation-details",level:2},{value:"Injection of the Autofill Menu",id:"injection-of-the-autofill-menu",level:3},{value:"Rendering Views through Sandboxed iFrames",id:"rendering-views-through-sandboxed-iframes",level:3},{value:"Passing Messages Between the Autofill Menu and the Extension",id:"passing-messages-between-the-autofill-menu-and-the-extension",level:3},{value:"Populating the Autofill Menu with Data",id:"populating-the-autofill-menu-with-data",level:3},{value:"Handling User Interaction",id:"handling-user-interaction",level:3},{value:"Security considerations",id:"security-considerations",level:2},{value:"Clickjacking",id:"clickjacking",level:3},{value:"<strong>Injecting elements as custom elements with a closed shadow DOM</strong>",id:"injecting-elements-as-custom-elements-with-a-closed-shadow-dom",level:4},{value:"<strong>Rendering Autofill Menu UI elements as sandboxed iFrame pages</strong>",id:"rendering-autofill-menu-ui-elements-as-sandboxed-iframe-pages",level:4},{value:"<strong>Establishing a strict content security policy for the Autofill Menu pages</strong>",id:"establishing-a-strict-content-security-policy-for-the-autofill-menu-pages",level:4},{value:"<strong>Reacting to attempts to modify the Autofill Menu elements programmatically</strong>",id:"reacting-to-attempts-to-modify-the-autofill-menu-elements-programmatically",level:4},{value:"Cross-Site Scripting (XSS)",id:"cross-site-scripting-xss",level:3},{value:"<strong>Limiting user input usage in the Autofill Menu</strong>",id:"limiting-user-input-usage-in-the-autofill-menu",level:4},{value:"<strong>Enforcing a strict content security policy to block foreign scripts</strong>",id:"enforcing-a-strict-content-security-policy-to-block-foreign-scripts",level:4},{value:"DOM Clobbering",id:"dom-clobbering",level:3},{value:"<strong>Utilizing the isolated execution context of the content script</strong>",id:"utilizing-the-isolated-execution-context-of-the-content-script",level:4},{value:"<strong>Adhering to OWASP secure coding guidelines</strong>",id:"adhering-to-owasp-secure-coding-guidelines",level:4}];function h(e){const t={a:"a",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",h4:"h4",header:"header",img:"img",li:"li",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,o.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(t.header,{children:(0,s.jsx)(t.h1,{id:"inline-autofill-menu",children:"Inline Autofill Menu"})}),"\n",(0,s.jsx)(t.p,{children:"The Inline Autofill Menu allows users to access and autofill credentials from their Bitwarden vault\ndirectly within webpage forms. Enabled by default with the installation of the Bitwarden extension,\nthis feature can be managed through the autofill settings within the Bitwarden extension. When\nactive, the extension injects the Autofill Menu into the webpage's DOM via the content script\nresponsible for enabling the autofill functionality."}),"\n",(0,s.jsx)(t.h2,{id:"project-structure",children:"Project structure"}),"\n",(0,s.jsxs)(t.p,{children:["The core scripts for the Autofill Menu feature are located within the\n",(0,s.jsx)(t.a,{href:"https://github.com/bitwarden/clients/tree/main/apps/browser/src/autofill",children:(0,s.jsx)(t.code,{children:"/apps/browser/src/autofill"})}),"\ndirectory of the ",(0,s.jsxs)(t.a,{href:"https://github.com/bitwarden/clients",children:[(0,s.jsx)(t.code,{children:"clients"})," repository"]}),". These files form the\nbackbone of the feature's functionality."]}),"\n",(0,s.jsxs)(t.table,{children:[(0,s.jsx)(t.thead,{children:(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.th,{children:"File"}),(0,s.jsx)(t.th,{children:"Responsibility"})]})}),(0,s.jsxs)(t.tbody,{children:[(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{children:(0,s.jsx)(t.a,{href:"https://github.com/bitwarden/clients/blob/main/apps/browser/src/autofill/background/overlay.background.ts",children:(0,s.jsx)(t.code,{children:"overlay.background.ts"})})}),(0,s.jsxs)(t.td,{children:["Orchestrates communication among three key components: the content script containing the ",(0,s.jsx)(t.a,{href:"https://github.com/bitwarden/clients/blob/main/apps/browser/src/autofill/services/autofill-inline-menu
1-content.service.ts",children:(0,s.jsx)(t.code,{children:"AutofillInlineMenuContentService"})}),", the extension pages displaying the Autofill Menu UI, and the extension background script. It facilitates the activation of various logic sequences by transmitting messages between these components."]})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{children:(0,s.jsx)(t.a,{href:"https://github.com/bitwarden/clients/blob/main/apps/browser/src/autofill/services/autofill-inline-menu-content.service.ts",children:(0,s.jsx)(t.code,{children:"autofill-inline-menu-content.service.ts"})})}),(0,s.jsxs)(t.td,{children:["Contains the initialization logic for the content script of the Autofill Menu. It is initialized within the content script encompassing the ",(0,s.jsx)(t.a,{href:"https://github.com/bitwarden/clients/blob/main/apps/browser/src/autofill/content/autofill-init.ts",children:(0,s.jsx)(t.code,{children:"AutofillInit"})})," class. The behavior for the Autofill Menu is setup on each form field of a webpage when the first ",(0,s.jsx)(t.code,{children:"collectPageDetails"})," message is received within the webpage. This service is responsible for creating the custom elements that constitute the Autofill Menu UI and managing the behavior associated with user interactions on the webpage's form fields."]})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{children:(0,s.jsx)(t.a,{href:"https://github.com/bitwarden/clients/blob/main/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe-element.ts",children:(0,s.jsx)(t.code,{children:"autofill-inline-menu-iframe-element.ts"})})}),(0,s.jsxs)(t.td,{children:["Serves as a parent class for initializing the Autofill Menu button and list custom elements. It constructs a closed shadow DOM and initializes the ",(0,s.jsx)(t.a,{href:"https://github.com/bitwarden/clients/blob/main/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe.service.ts",children:(0,s.jsx)(t.code,{children:"AutofillInlineMenuIframeService"})}),". This service is responsible for injecting an iframe, which either displays the Autofill Menu list or button page."]})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{children:(0,s.jsx)(t.a,{href:"https://github.com/bitwarden/clients/blob/main/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-button-iframe.ts",children:(0,s.jsx)(t.code,{children:"autofill-inline-menu-button-iframe.ts"})})}),(0,s.jsxs)(t.td,{children:["Initializes the custom Autofill Menu button element that is injected into the DOM. This script extends the ",(0,s.jsx)(t.a,{href:"https://github.com/bitwarden/clients/blob/main/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe-element.ts",children:(0,s.jsx)(t.code,{children:"AutofillInlineMenuIframeElement"})})," class and initializes properties specific to the Autofill Menu button page."]})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{children:(0,s.jsx)(t.a,{href:"https://github.com/bitwarden/clients/blob/main/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-list-iframe.ts",children:(0,s.jsx)(t.code,{children:"autofill-inline-menu-list-iframe.ts"})})}),(0,s.jsxs)(t.td,{children:["Initializes the custom Autofill Menu list element that is injected into the DOM. This script extends the ",(0,s.jsx)(t.a,{href:"https://github.com/bitwarden/clients/blob/main/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe-element.ts",children:(0,s.jsx)(t.code,{children:"AutofillInlineMenuIframeElement"})})," class, and initializes properties unique to the Autofill Menu list page."]})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{children:(0,s.jsx)(t.a,{href:"https://github.com/bitwarden/clients/blob/main/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe.service.ts",children:(0,s.jsx)(t.code,{children:"autofill-inline-menu-iframe.service.ts"})})}),(0,s.jsx)(t.td,{children:"Handles the functionality of the Autofill Menu page iframes, which are contained within the custom web component injected into a user's page. This script acts as an intermediary for message passing between the extension background and the iframe within the custom web component."})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{children:(0,s.jsx)(t.a,{href:"https://github.com/bitwarden/clients/blob/main/apps/browser/src/autofill/overlay/inline-menu/pages/shared/autofill-inline-menu
1-page-element.ts",children:(0,s.jsx)(t.code,{children:"autofill-inline-menu-page-element.ts"})})}),(0,s.jsx)(t.td,{children:"Serves as the parent class for individual Autofill Menu page scripts. It encapsulates common logic used across Autofill Menu pages, including initialization routines, establishing global listeners, handling window message posting to the parent of the iframe, and managing focus redirection from the iframe element."})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{children:(0,s.jsx)(t.a,{href:"https://github.com/bitwarden/clients/blob/main/apps/browser/src/autofill/overlay/inline-menu/pages/button/autofill-inline-menu-button.ts",children:(0,s.jsx)(t.code,{children:"autofill-inline-menu-button.ts"})})}),(0,s.jsxs)(t.td,{children:["Utilized within ",(0,s.jsx)(t.a,{href:"https://github.com/bitwarden/clients/blob/main/apps/browser/src/autofill/overlay/inline-menu/pages/button/button.html",children:(0,s.jsx)(t.code,{children:"button.html"})})," to facilitate the Autofill Menu button extension page within a sandboxed iframe. It inherits from the ",(0,s.jsx)(t.a,{href:"https://github.com/bitwarden/clients/blob/main/apps/browser/src/autofill/overlay/inline-menu/pages/shared/autofill-inline-menu-page-element.ts",children:(0,s.jsx)(t.code,{children:"AutofillInlineMenuPageElement"})})," class and is specifically tasked with managing the behavior of the Autofill Menu button injected into webpages."]})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{children:(0,s.jsx)(t.a,{href:"https://github.com/bitwarden/clients/blob/main/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts",children:(0,s.jsx)(t.code,{children:"autofill-inline-menu-list.ts"})})}),(0,s.jsxs)(t.td,{children:["Utilized within ",(0,s.jsx)(t.a,{href:"https://github.com/bitwarden/clients/blob/main/apps/browser/src/autofill/overlay/inline-menu/pages/list/list.html",children:(0,s.jsx)(t.code,{children:"list.html"})})," to facilitate the Autofill Menu list extension page within a sandboxed iframe. It inherits from the ",(0,s.jsx)(t.a,{href:"https://github.com/bitwarden/clients/blob/main/apps/browser/src/autofill/overlay/inline-menu/pages/shared/autofill-inline-menu-page-element.ts",children:(0,s.jsx)(t.code,{children:"AutofillInlineMenuPageElement"})})," class and is specifically tasked with managing the behavior of the Autofill Menu list injected into webpages."]})]})]})]}),"\n",(0,s.jsx)(t.h2,{id:"implementation-details",children:"Implementation details"}),"\n",(0,s.jsx)(t.p,{children:"The development of the Autofill Menu necessitated a focus on security to mitigate risks associated\nwith content script injection into unknown webpages. Ensuring the security of user data was a\ncritical factor; thus, efforts were made to obfuscate the content and behavior of the Autofill Menu\nto prevent potential data compromises."}),"\n",(0,s.jsx)(t.p,{children:"The accompanying diagram provides a high-level architectural overview of the Autofill Menu. It\ndetails the interaction mechanisms between the extension, webpage, and user in presenting the\nAutofill Menu, highlighting the structural framework of this feature."}),"\n",(0,s.jsx)(t.p,{children:(0,s.jsxs)(t.a,{target:"_blank","data-noBrokenLinkCheck":!0,href:n(34876).A+"",children:[(0,s.jsx)(t.img,{alt:"Autofill Menu architecture",src:n(33668).A+"",width:"4727",height:"3419"})," ",(0,s.jsx)(t.em,{children:"(A visual representation of the architectural workflow of the Autofill Menu)"})]})}),"\n",(0,s.jsx)(t.p,{children:"In addition to the visible aspects, several underlying elements have been integrated to ensure that\nthe Autofill Menu is both performant and secure. The subsequent sections detail the design and\nimplementation specifics of this feature:"}),"\n",(0,s.jsx)(t.h3,{id:"injection-of-the-autofill-menu",children:"Injection of the Autofill Menu"}),"\n",(0,s.jsx)(t.p,{children:"An important consideration during the development of the Autofill Menu was addressing user concerns\nabout the performance and security of UI elements injected into an unknown webpage. As a result, the\nAutofill Menu has been implemented to ensure that it is injected into a webpage only when a user has\nenabled the feature."}),"\n",(0,s.jsxs)(t.p,{children:["When the Autofill Menu is disabled, the core autofill functionality is enabled via a content script\nnamed\n",(0,s.jsx)(t.a,{href:"https://github.com/bitwarden/clients/blob/main/apps/browser/src/autofill/content/bootstrap-autofill.ts",children:(0,s.jsx)(t.code,{children:"bootstrap-autofill.ts"})}),".\nThis script, utilizing the\n",(0,s.jsx)(t.a,{href:"https://github.com/bitwarden/clients/blob/main/apps/browser/src/autofill/content/autofill-init.ts",children:(0,s.jsx)(t.code,{children:"AutofillInit"})}),"\nclass, initiates the autofill feature and omits the injection of the Autofill Menu code. However,\nwhen a user activates the Autofill Menu, a distinct content script,\n",(0,s.jsx)(t.a,{href:"https://github.com/bitwarden/clients/blob/main/apps/browser/src/autofill/content/bootstrap-autofill-inline-menu.ts",children:(0,s.jsx)(t.code,{children:"bootstrap-autofill-inline-menu.ts"})}),",\nis deployed instead. This script is responsible for initializing both the\n",(0,s.jsx)(t.a,{href:"https://github.com/bitwarden/clients/blob/main/apps/browser/src/autofill/services/autofill-inline-menu
1-content.service.ts",children:(0,s.jsx)(t.code,{children:"AutofillInlineMenuContentService"})}),"\nclass and the ",(0,s.jsx)(t.code,{children:"AutofillInit"})," class."]}),"\n",(0,s.jsxs)(t.p,{children:["This initialization process occurs on each page a user opens. Once the Autofill Menu is set up, the\nsystem awaits a ",(0,s.jsx)(t.code,{children:"collectPageDetails"})," message from the background extension, received by the\n",(0,s.jsx)(t.code,{children:"AutofillInit"})," class. Receipt of this message activates event listeners on all form field elements\nof the webpage. These listeners are essential in controlling the Autofill Menu's display and its\ninteractive behavior as users engage with form fields."]}),"\n",(0,s.jsxs)(t.p,{children:["Whenever a user interacts with a form field, the system generates two\n",(0,s.jsx)(t.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements",children:"custom web components"}),"\nwith randomized names. This randomization, occurring each time the Autofill Menu initializes on a\nwebpage, hinders any programmatic manipulation of the injected Autofill Menu elements. These custom\nelements, created within a\n",(0,s.jsx)(t.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM#element.shadowroot_and_the_mode_option",children:"closed shadow DOM"}),",\nare appended to the bottom of the webpage's body. This approach ensures that the webpage cannot\ndirectly access these elements or their content."]}),"\n",(0,s.jsx)(t.h3,{id:"rendering-views-through-sandboxed-iframes",children:"Rendering Views through Sandboxed iFrames"}),"\n",(0,s.jsxs)(t.p,{children:["The user interface representing the Autofill Menu consists of extension pages rendered within\nsandboxed iframes. These sandboxed iframes are structured within the custom elements injected into a\nuser's webpage and represent the Autofill Menu\n",(0,s.jsx)(t.a,{href:"https://github.com/bitwarden/clients/tree/main/apps/browser/src/autofill/overlay/inline-menu/pages/button",children:"button"}),"\nand\n",(0,s.jsx)(t.a,{href:"https://github.com/bitwarden/clients/tree/main/apps/browser/src/autofill/overlay/inline-menu/pages/list",children:"list"}),"\nUI elements."]}),"\n",(0,s.jsxs)(t.p,{children:["Rendering these views within a sandboxed iframe sets clear limitations on how these pages can\ncommunicate with the extension background. This approach effectively\n",(0,s.jsx)(t.a,{href:"https://developer.chrome.com/docs/extensions/mv3/sandboxingEval/",children:"strips the pages of the ability to directly access the extension API"}),",\nnecessitating communication with the extension background through postMessage calls routed through\nthe iframe element's parent. This ensures that any actions needing to occur within the extension\nbackground script cannot be initiated outside the\n",(0,s.jsx)(t.a,{href:"https://developer.chrome.com/docs/extensions/mv3/content_scripts/#isolated_world",children:"isolated context of the content script"}),"\nthat injects the Autofill Menu UI elements into the DOM."]}),"\n",(0,s.jsx)(t.p,{children:"These pages are rendered with a strict content security policy that prohibits the execution of any\ninline scripts or scripts foreign to the extension. This ensures that the pages cannot execute any\nform of script not explicitly defined within the extension, whether inline or through a script tag."}),"\n",(0,s.jsx)(t.h3,{id:"passing-messages-between-the-autofill-menu-and-the-extension",children:"Passing Messages Between the Autofill Menu and the Extension"}),"\n",(0,s.jsxs)(t.p,{children:["As a consequence of the sandboxed iframe implementation, an indirect messaging system was necessary\nto enable communication between the extension background and the Autofill Menu pages. This messaging\napproach operates through the use of ",(0,s.jsx)(t.code,{children:"postMessage"})," calls exchanged among the Autofill Menu pages,\nthe content script, and the extension background."]}),"\n",(0,s.jsxs)(t.p,{children:["When an Autofill Menu page needs to communicate with the extension background, it initiates a\n",(0,s.jsx)(t.code,{children:"postMessage"})," call to its parent frame, the content script that injected the Autofill Menu into the\nwebpage. This content script verifies the origin of the window message to confirm it originates from\nthe expected source. Upon validation, it relays a message to the extension background via a\nlong-lived port connection. The extension background, upon receiving this message, executes the\nnecessary background logic and responds with a port message back to the content script. This message\nis then transferred to the Autofill Menu page through another ",(0,s.jsx)(t.code,{children:"postMessage"})," call."]}),"\n",(0,s.jsx)(t.p,{children:"Below is an illustration that demonstrates how this messaging system operates when initializing and\nretrieving cipher data for the Autofill Menu list page:"}),"\n",(0,s.jsx)(t.p,{children:(0,s.jsxs)(t.a,{target:"_blank","data-noBrokenLinkCheck":!0,href:n(96107).A+"",children:[(0,s.jsx)(t.img,{alt:"Autofill Menu messaging",src:n(35603).A
1+"",width:"5505",height:"3252"})," ",(0,s.jsx)(t.em,{children:"(A visual representation of the messaging workflow of the Autofill Menu)"})]})}),"\n",(0,s.jsxs)(t.p,{children:["As illustrated in the example above, any messages that need to be relayed from the\n",(0,s.jsx)(t.code,{children:"InlineMenuListPage"})," to the ",(0,s.jsx)(t.code,{children:"InlineMenuBackground"})," script must pass through the\n",(0,s.jsx)(t.code,{children:"AutofillInlineMenuIframe"})," content script. This arrangement ensures that the Autofill Menu pages\ncannot directly access the extension background script and, likewise, that the extension background\nscript is not directly manipulable by the Autofill Menu pages."]}),"\n",(0,s.jsx)(t.h3,{id:"populating-the-autofill-menu-with-data",children:"Populating the Autofill Menu with Data"}),"\n",(0,s.jsx)(t.p,{children:"We exercised considerable selectivity in the types of data passed to the Autofill Menu to safeguard\nagainst compromising a user's vault. Consequently, only the following data types are transmitted to\nthe Autofill Menu UI elements:"}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-typescript",children:"type OverlayCipherData = {\n  id: string;\n  name: string;\n  type: CipherType;\n  reprompt: CipherRepromptType;\n  favorite: boolean;\n  icon: { imageEnabled: boolean; image: string; fallbackImage: string; icon: string };\n  login?: { username: string };\n  card?: string;\n};\n"})}),"\n",(0,s.jsxs)(t.p,{children:["The ",(0,s.jsx)(t.code,{children:"id"})," value in the aforementioned data structure is a generic identifier assigned during the\ncreation of the ",(0,s.jsx)(t.code,{children:"OverlayCipherData"})," data structure. It does not correspond to the actual ",(0,s.jsx)(t.code,{children:"id"})," value\nof a cipher in a user's vault. This precaution ensures that the Autofill Menu cannot be utilized to\npinpoint a specific cipher within a user's vault."]}),"\n",(0,s.jsxs)(t.p,{children:["The ",(0,s.jsx)(t.code,{children:"login.username"})," value is obfuscated by the extension background script prior to being\ntransmitted to the Autofill Menu. To prevent full exposure of the username to the webpage, these\nvalues are partially masked in the Autofill Menu. For instance, a username like\n",(0,s.jsx)(t.code,{children:"[email protected]"})," would be displayed as ",(0,s.jsx)(t.code,{children:"us******[email protected]"})," in the Autofill Menu."]}),"\n",(0,s.jsx)(t.h3,{id:"handling-user-interaction",children:"Handling User Interaction"}),"\n",(0,s.jsxs)(t.p,{children:["The Autofill Menu is positioned using a ",(0,s.jsx)(t.code,{children:"position: fixed"})," CSS value in relation to the form field a\nuser interacts with. The exact position coordinates are calculated by invoking\n",(0,s.jsx)(t.code,{children:"getBoundingClientRect"})," on the field itself. This approach ensures the Autofill Menu aligns with the\nwebpage's viewport, rather than just the form field element. This positioning strategy is effective\nin preventing the Autofill Menu from being obscured by nearby elements positioned close to the form\nfield."]}),"\n",(0,s.jsxs)(t.p,{children:["This approach can lead to misalignment of the Autofill Menu when the page is scrolled or resized. To\ncounter this, we have implemented event listeners that reposition the Autofill Menu whenever a\n",(0,s.jsx)(t.code,{children:"scroll"})," or ",(0,s.jsx)(t.code,{children:"resize"})," event occurs. If the associated form field exits the user's viewport during\nthese events, the Autofill Menu is completely removed."]}),"\n",(0,s.jsx)(t.p,{children:"Users can navigate the Autofill Menu using their keyboard, facilitated by various listeners attached\nto both the form field element and the UI elements of the Autofill Menu. These listeners are\ndesigned to handle the following keyboard interactions:"}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsxs)(t.li,{children:["Pressing ",(0,s.jsx)(t.code,{children:"ArrowDown"})," on a focused input element moves the focus to the Autofill Menu list. Users\ncan then press down further to navigate the list and select a credential for autofill."]}),"\n",(0,s.jsxs)(t.li,{children:["Pressing ",(0,s.jsx)(t.code,{children:"ArrowUp"})," within the Autofill Menu list shifts the focus to the previous list item."]}),"\n",(0,s.jsx)(t.li,{children:"Navigating to the end of the Autofill Menu list automatically redirects the focus to the list's\nfirst element."}),"\n",(0,s.jsxs)(t.li,{children:["Pressing ",(0,s.jsx)(t.code,{children:"Enter"})," while an element of the Autofill Menu list is focused will trigger the autofill\nof the selected credential."]}),"\n",(0,s.jsxs)(t.li,{children:["Pressing ",(0,s.jsx)(t.code,{children:"Escape"})," while focused on the Autofill Menu list element closes the Autofill Menu."]}),"\n",(0,s.jsxs)(t.li,{children:["The Autofill Menu can also be closed by pressing ",(0,s.jsx)(t.code,{children:"Escape"}
1)," when focused on an input element."]}),"\n",(0,s.jsxs)(t.li,{children:["Pressing ",(0,s.jsx)(t.code,{children:"Tab"})," while focused on the Autofill Menu moves the focus to the next focusable element on\nthe webpage, relative to the most recently focused input field."]}),"\n",(0,s.jsxs)(t.li,{children:["Pressing ",(0,s.jsx)(t.code,{children:"Shift + Tab"})," while focused on the Autofill Menu shifts the focus to the previous\nfocusable element on the webpage, relative to the most recently focused input field."]}),"\n"]}),"\n",(0,s.jsx)(t.p,{children:"When triggering autofill, the Autofill Menu utilizes the same autofill logic employed when a user\nselects a cipher within the Bitwarden extension popup. In the Autofill Menu, selecting a cipher\ninvolves sending a message to the background with an 'id' value unique to the list of aggregated\nciphers in the Autofill Menu UI. This enables the identification and autofill of the chosen cipher\nfrom the background. This approach ensures consistency in the autofill process across the extension\nand prevents the introduction of new vulnerabilities through the Autofill Menu."}),"\n",(0,s.jsx)(t.h2,{id:"security-considerations",children:"Security considerations"}),"\n",(0,s.jsx)(t.p,{children:"The Autofill Menu heavily relies on content scripts to inject its UI into webpages. This process,\nwhich entails injecting code and DOM elements into unfamiliar websites, required the implementation\nof stringent security measures. Throughout the design and implementation of this feature we worked\nto mitigate a broad range of security risks. Additionally, we conducted a comprehensive security\nreview with a number of third-party organizations, ensuring the robust security of the feature."}),"\n",(0,s.jsx)(t.p,{children:"The subsequent sections detail various security considerations that were factored in during the\ndesign and implementation of the feature."}),"\n",(0,s.jsx)(t.h3,{id:"clickjacking",children:"Clickjacking"}),"\n",(0,s.jsx)(t.p,{children:"To combat clickjacking, the following defensive measures have been implemented for the Autofill Menu\nfeature:"}),"\n",(0,s.jsx)(t.h4,{id:"injecting-elements-as-custom-elements-with-a-closed-shadow-dom",children:(0,s.jsx)(t.strong,{children:"Injecting elements as custom elements with a closed shadow DOM"})}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsxs)(t.li,{children:["Upon injection, a custom element using a\n",(0,s.jsx)(t.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM#element.shadowroot_and_the_mode_option",children:"closed shadow DOM"}),"\nis created and appended to the bottom of the body element of the target page."]}),"\n",(0,s.jsx)(t.li,{children:"This custom element is named randomly each time the Autofill Menu initializes on a webpage, making\nit difficult for attackers to target the element programmatically."}),"\n",(0,s.jsx)(t.li,{children:"The use of a closed shadow DOM restricts direct access to the element's properties or methods by\nthe page itself."}),"\n",(0,s.jsxs)(t.li,{children:["Notably, malicious extensions can access a closed shadow DOM element via the\n",(0,s.jsx)(t.a,{href:"https://developer.chrome.com/docs/extensions/reference/dom/",children:"extension.dom API"}),". To counter this,\na strict content security policy is applied to the Autofill Menu pages, inhibiting other\nextensions from accessing the execution context of these pages."]}),"\n"]}),"\n",(0,s.jsx)(t.h4,{id:"rendering-autofill-menu-ui-elements-as-sandboxed-iframe-pages",children:(0,s.jsx)(t.strong,{children:"Rendering Autofill Menu UI elements as sandboxed iFrame pages"})}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsx)(t.li,{children:"The custom element contains a sandboxed iframe that displays the extension's UI."}),"\n",(0,s.jsxs)(t.li,{children:["Designating these pages as sandbox pages prevents direct use of the extension API by the page.\nActions requiring the extension background script are relayed through ",(0,s.jsx)(t.code,{children:"postMessage"})," calls to the\nAutofill Menu UI element's parent."]}),"\n",(0,s.jsxs)(t.li,{children:["This setup ensures that actions within the extension background script are only triggered within\nthe\n",(0,s.jsx)(t.a,{href:"https://developer.chrome.com/docs/extensions/mv3/content_scripts/#isolated_world",children:"isolated context of the content script"}),"\nwhich injects the Autofill Menu UI elements."]}),"\n"]}),"\n",(0,s.jsx)(t.h4,{id:"establishing-a-strict-content-security-policy-for-the-autofill-menu-pages",children:(0,s.jsx)(t.strong,{children:"Establishing a strict content security policy for the Autofill Menu pages"})}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsxs)(t.li,{children:["We have followed\n",(0,s.jsx)(t.a,{href:"https://developer.chrome.com/docs/extensions/mv3/sandboxingEval/",children:"Google's guidelines for establishing sandboxed pages"}),"\nin our extension. Although we do not utilize any form of ",(0,s.jsx)(t.code,{children:"eval"})," in the Autofill Menu\nimplementation, establishing limitations for the injected UI elements was still deemed crucial."]}),"\n",(0,s.jsxs)(t.li,{children:["The sandboxed pages for the Autofill Menu UI elements adhere to a content security policy of\n",(0,s.jsx)(t.code,{children:"sandbox allow-scripts; script-src 'self'"}),"."]}),"\n",(0,s.jsx)(t.li,{children:"This policy ensures that only scripts explicitly defined within the extension can be executed by\nthe injected UI elements."}),"\n"]}),"\n",(0,s.jsx)(t.h4,{id:"reacting-to-attempts-to-modify-the-autofill-menu-elements-programmatically",children:(0,s.jsx)(t.strong,{children:"Reacting to attempts to modify the Autofill Menu elements programmatically"})}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsxs)(t.li,{children:["A defensive approach using the\n",(0,s.jsx)(t.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver",children:"MutationObserver API"})," is\nimplemented to guard against modifications to the Autofill Menu UI elements."]}),"\n",(0,s.jsx)(t.li,{children:"The content script monitors for elements trying to append to the bottom of the body, repositioning\nthe UI elements as necessary to prevent allowing the Autofill Menu to be overlaid by potential\nclickjacking elements."}),"\n",(0,s.jsx)(t.li,{children:"It also watches for changes to the styles or attributes of the Autofill Menu UI elements,\nresetting them to their original values if altered by malicious actions."}),"\n"]}),"\n",(0,s.jsx)(t.h3,{id:"cross-site-scripting-xss",children:"Cross-Site Scripting (XSS)"}),"\n",(0,s.jsx)(t.p,{children:"To mitigate the risks of Cross-Site Scripting (XSS), the following defensive measures have been\nimplemented for the Autofill Menu feature:"}),"\n",(0,s.jsx)(t.h4,{id:"limiting-user-input-usage-in-the-autofill-menu",children:(0,s.jsx)(t.strong,{children:"Limiting user input usage in the Autofill Menu"})}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsx)(t.li,{children:"The implementation largely avoids reliance on the injection of external values."}),"\n",(0,s.jsx)(t.li,{children:"User-created cipher values are used to populate the Autofill Menu list with credentials. These\nvalues are carefully sanitized when saved in a user's vault."}),"\n",(0,s.jsx)(t.li,{children:"Collection of foreign input occurs when a user creates a new cipher through the Autofill Menu.\nHowever, these inputs are processed by the extension background script and are never introduced\ninto any executable context."}),"\n"]}),"\n",(0,s.jsx)(t.h4,{id:"enforcing-a-strict-content-security-policy-to-block-foreign-scripts",children:(0,s.jsx)(t.strong,{children:"Enforcing a strict content security policy to block foreign scripts"})}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsx)(t.li,{children:"The execution of inline scripts or scripts external to the extension is prohibited within the\nAutofill Menu pages. This is achieved by implementing a stringent content security policy\nspecifically for the Autofill Menu pages."}),"\n"]}),"\n",(0,s.jsx)(t.h3,{id:"dom-clobbering",children:"DOM Clobbering"}),"\n",(0,s.jsx)(t.p,{children:"To address the risks associated with DOM clobbering, the following defensive strategies have been\nimplemented for the Autofill Menu feature:"}),"\n",(0,s.jsx)(t.h4,{id:"utilizing-the-isolated-execution-context-of-the-content-script",children:(0,s.jsx)(t.strong,{children:"Utilizing the isolated execution context of the content script"})}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsxs)(t.li,{children:["Web extension content scripts operate in a\n",(0,s.jsx)(t.a,{href:"https://developer.chrome.com/docs/extensions/mv3/content_scripts/#isolated_world",children:"unique, isolated context"}),",\nseparate from the global execution context of the webpage into which they are injected."]}),"\n",(0,s.jsx)(t.li,{children:"This isolated execution context guarantees that variables or functions defined within the content\nscript are not accessible to the webpage's scripting environment, thus safeguarding them from\nbeing overwritten by the webpage."}),"\n"]}),"\n",(0,s.jsx)(t.h4,{id:"adhering-to-owasp-secure-coding-guidelines",children:(0,s.jsx)(t.strong,{children:"Adhering to OWASP secure coding guidelines"})}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsxs)(t.li,{children:["In developing our content scripts, we have adhered to the\n",(0,s.jsx)(t.a,{href:"https://cheatsheetseries.owasp.org/cheatsheets/DOM_Clobbering_Prevention_Cheat_Sheet.html#secure-coding-guidelines",children:"OWASP Secure Coding Guidelines"}),"\nto avoid introducing vulnerabilities into the extension."]}),"\n"]})]})}function d(e={}){const{wrapper:t}={...(0,o.R)(),...e.components};return t?(0,s.jsx)(t,{...e,children:(0,s.jsx)(h,{...e})}):h(e)}},34876(e,t,n){n.d(t,{A:()=>i});const i=n.p+"assets/files/autofill-overlay-architecture-fc3d1038fbb8e94b99c414eea72816e5.svg"},96107(e,t,n){n.d(t,{A:()=>i});const i=n.p+"assets/files/autofill-overlay-messaging-960b53cb99897ec85410bc3fc37f85aa.svg"},33668(e,t,n){n.d(t,{A:()=>i});const i=n.p+"assets/images/autofill-overlay-architecture-fc3d1038fbb8e94b99c414eea72816e5.svg"},35603(e,t,n){n.d(t,{A:()=>i});const i=n.p+"assets/images/autofill-overlay-messaging-960b53cb99897ec85410bc3fc37f85aa.svg"},28453(e,t,n){n.d(t,{R:()=>l,x:()=>a});var i=n(96540);const s={},o=i.createContext(s);function l(e){const t=i.useContext(o);return i.useMemo(function(){return"function"==typeof e?e(t):{...t,...e}},[t,e])}function a(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:l(e.components),i.createElement(o.Provider,{value:t},e.children)}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.