diff --git a/extension/content/content.js b/extension/content/content.js index c1882af..34c795b 100644 --- a/extension/content/content.js +++ b/extension/content/content.js @@ -18,6 +18,10 @@ const PK_DROPDOWN_ID = "__pk_dropdown__"; const VAULT_URL = "https://pwkeeper.ngodanguyen.tech/vault"; + // Never inject on the PassKeeper vault itself — our own inputs would get decorated. + const OWN_ORIGINS = ["pwkeeper.ngodanguyen.tech"]; + if (OWN_ORIGINS.includes(location.hostname)) return; + let _bannerEl = null; let _hasNotifiedForm = false; let _formObserver = null; @@ -107,7 +111,41 @@ el.getAttribute("aria-label") || "", ].join(" "); - return CRED_HINTS.test(attrs); + if (!CRED_HINTS.test(attrs)) return false; + + // Final gate: require a password field to be nearby (same form, or within + // 5 ancestor levels) — this prevents hooking standalone search / filter + // inputs that happen to carry a name like "user" or "email". + return _hasPasswordSibling(el); + } + + /** + * Returns true when `el` shares a form (or close ancestor) with at least one + * visible password input. This is the key signal that we are on a login form, + * not a generic site-search or profile page. + */ + function _hasPasswordSibling(el) { + // 1. Prefer the explicit