diff --git a/lib/common/static/autofill_guard.js b/lib/common/static/autofill_guard.js
new file mode 100644
index 0000000..b016d41
--- /dev/null
+++ b/lib/common/static/autofill_guard.js
@@ -0,0 +1,148 @@
+/**
+ * 全局防浏览器自动填充登录账号/密码进业务输入框.
+ * 跳过真正的登录/改密字段;对划转数量等易中招框用 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;
+})();
diff --git a/lib/hub/hub_bridge.py b/lib/hub/hub_bridge.py
index 4cfb1a5..9c8d0b7 100644
--- a/lib/hub/hub_bridge.py
+++ b/lib/hub/hub_bridge.py
@@ -61,6 +61,7 @@ def install_instance_theme_static(app) -> None:
"records_review_page.js": "application/javascript; charset=utf-8",
"ai_review_render.js": "application/javascript; charset=utf-8",
"form_submit_guard.js": "application/javascript; charset=utf-8",
+ "autofill_guard.js": "application/javascript; charset=utf-8",
"key_monitor_form.js": "application/javascript; charset=utf-8",
"time_close_ui.js": "application/javascript; charset=utf-8",
"manual_order_rr_preview.js": "application/javascript; charset=utf-8",
diff --git a/lib/instance/templates/embed_shell.html b/lib/instance/templates/embed_shell.html
index f285a2b..e2d680b 100644
--- a/lib/instance/templates/embed_shell.html
+++ b/lib/instance/templates/embed_shell.html
@@ -4,6 +4,7 @@
+
@@ -117,7 +118,7 @@ const ORDER_ENTRY_MODEL_CODE_TO_CATEGORY = {{ entry_model_code_to_category | toj
-
+