/** * 全局防浏览器自动填充登录账号/密码进业务输入框. * 跳过真正的登录/改密字段;对划转数量等易中招框用 readonly 到聚焦. */ (function () { "use strict"; var GUARD_ATTRS = { autocomplete: "off", autocorrect: "off", autocapitalize: "off", spellcheck: "false", "data-lpignore": "true", "data-1p-ignore": "true", "data-bwignore": "true", "data-form-type": "other", }; function looksLikeUsername(v) { return /^[a-z][a-z0-9._-]{1,31}$/i.test(String(v || "").trim()); } function isAuthField(el) { if (!el || !el.getAttribute) return true; var t = String(el.type || "").toLowerCase(); if (t === "hidden" || t === "checkbox" || t === "radio" || t === "file" || t === "submit" || t === "button") { return true; } if (el.getAttribute("aria-hidden") === "true") return true; if (el.tabIndex === -1 && String(el.getAttribute("autocomplete") || "").toLowerCase() === "username") { return true; // 诱饵账号框 } var idName = String(el.id || "") + " " + String(el.name || ""); if (/^(pwd-|hub-pwd-|login-)/i.test(String(el.id || ""))) return true; if (el.closest) { if (el.closest(".login-form, #login-form, form.login-form, .password-settings, [data-password-settings]")) { return true; } } // env API Key 等 type=password 仍要防登录密码灌入,不在此跳过 if (t === "password" && /^(username|password)$/i.test(String(el.name || ""))) { if (el.closest && el.closest("form[method='post'], form[method='POST']")) return true; } return false; } function isAmountLike(el) { var key = String(el.id || "") + " " + String(el.name || "") + " " + String(el.placeholder || ""); return /amount|xfer|transfer|划转|数量|金额/i.test(key); } function wipeBad(el) { if (!el || isAuthField(el)) return; var v = String(el.value || "").trim(); if (!looksLikeUsername(v)) return; var t = String(el.type || "text").toLowerCase(); if (t === "number" || isAmountLike(el) || /price|sheets|qty|sl|tp|target|entry|strike/i.test(String(el.id || "") + String(el.name || ""))) { el.value = ""; } } function harden(el) { if (!el || el.nodeType !== 1) return; if (isAuthField(el)) return; if (el.getAttribute("aria-hidden") === "true") return; if (el.dataset && el.dataset.autofillGuarded === "1") { wipeBad(el); return; } if (el.dataset) el.dataset.autofillGuarded = "1"; Object.keys(GUARD_ATTRS).forEach(function (k) { var cur = el.getAttribute(k); if (k === "autocomplete" && cur && /^(username|current-password)/i.test(cur)) { return; } // env 密钥框用 new-password 更抗登录密码灌入 if (k === "autocomplete" && String(el.type || "").toLowerCase() === "password") { el.setAttribute(k, "new-password"); return; } if (!cur || cur === "on") el.setAttribute(k, GUARD_ATTRS[k]); }); if (String(el.type || "").toLowerCase() === "password" || isAmountLike(el)) { el.setAttribute("readonly", "readonly"); el.addEventListener("focus", function () { el.removeAttribute("readonly"); }); el.addEventListener("blur", function () { if (!el.value) el.setAttribute("readonly", "readonly"); }); } wipeBad(el); setTimeout(function () { wipeBad(el); }, 250); setTimeout(function () { wipeBad(el); }, 900); setTimeout(function () { wipeBad(el); }, 2000); } function scan(root) { var scope = root && root.querySelectorAll ? root : document; var list = scope.querySelectorAll( 'input[type="text"], input[type="number"], input[type="search"], input[type="url"], input[type="email"], input[type="tel"], input[type="password"], input:not([type]), textarea' ); for (var i = 0; i < list.length; i++) harden(list[i]); } function boot() { scan(document); if (typeof MutationObserver === "undefined") return; var obs = new MutationObserver(function (mutations) { for (var i = 0; i < mutations.length; i++) { var m = mutations[i]; if (m.type === "childList") { for (var j = 0; j < m.addedNodes.length; j++) { var n = m.addedNodes[j]; if (!n || n.nodeType !== 1) continue; if (n.matches && n.matches("input, textarea")) harden(n); else if (n.querySelectorAll) scan(n); } } else if (m.type === "attributes" && m.target) { harden(m.target); } } }); obs.observe(document.documentElement, { childList: true, subtree: true, attributes: true, attributeFilter: ["value"], }); } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", boot); } else { boot(); } window.cmAutofillGuardScan = scan; })();