a1abe159fa
Add deploy/manage.sh bootstrap for git.bz121.com/dekun/crypto_okx and point docs at this repo. Co-authored-by: Cursor <cursoragent@cursor.com>
149 lines
4.9 KiB
JavaScript
149 lines
4.9 KiB
JavaScript
/**
|
|
* 全局防浏览器自动填充登录账号/密码进业务输入框.
|
|
* 跳过真正的登录/改密字段;对划转数量等易中招框用 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;
|
|
})();
|