feat: 按域名授权、iframe 多帧填充、自动提交限流修正,并补齐工程规范与文档

安全与权限:
- 站点访问权限改为 optional_host_permissions,保存配置时逐域名授权;移除 host_permissions 与 tabs,安装提示不再出现全站数据访问与浏览记录
- 主口令不再写入 storage.session,改用 IndexedDB 中不可导出的 CryptoKey 句柄恢复解锁,锁定即丢弃
- content script 注入范围只注册已授权域名,并随权限变化即时收敛

自动登录:
- 支持 iframe 内的登录表单:脚本注入所有帧,逐帧按自身地址匹配配置
- 手动填充改为逐帧探测,定向发送到真正含密码框的帧
- 限流修正:只在真正触发提交后计数;判定登录成功后立即清零;达到上限时页面给出可见提示;重置判断只看启用中的配置
- SPA 重试改为 DOM 变更门控 + 退避,并单独监听已发现的 Shadow Root

工程化与文档:
- 引入 ESLint(扁平配置)与 Prettier,CI 增加 lint 与 format:check
- 信息类日志改为 debugLog(默认静默,chrome.storage.local.debugLog 开关)
- 测试 123 → 128 用例(新增权限、iframe、限流相关用例)
- README / PRIVACY / CHANGELOG 同步
This commit is contained in:
陈银军
2026-09-18 00:05:20 +08:00
parent 8883a5c506
commit 1b0c3c012c
48 changed files with 3364 additions and 680 deletions
+19 -7
View File
@@ -25,7 +25,7 @@ function createFakeIndexedDB() {
return req;
}
function makeDb(name) {
function makeDb() {
const stores = new Map();
return {
objectStoreNames: { contains: (n) => stores.has(n) },
@@ -37,9 +37,17 @@ function createFakeIndexedDB() {
const data = stores.get(storeName);
const tx = { error: null, onabort: null };
tx.objectStore = () => ({
put: (value, key) => makeRequest(() => { data.set(key, value); return key; }),
put: (value, key) =>
makeRequest(() => {
data.set(key, value);
return key;
}),
get: (key) => makeRequest(() => data.get(key)),
delete: (key) => makeRequest(() => { data.delete(key); return undefined; }),
delete: (key) =>
makeRequest(() => {
data.delete(key);
return undefined;
}),
});
return tx;
},
@@ -49,14 +57,18 @@ function createFakeIndexedDB() {
return {
open(name) {
const req = {
result: undefined, error: null,
onsuccess: null, onerror: null, onupgradeneeded: null, onblocked: null,
result: undefined,
error: null,
onsuccess: null,
onerror: null,
onupgradeneeded: null,
onblocked: null,
};
queueMicrotask(() => {
let db = databases.get(name);
const isNew = !db;
if (isNew) {
db = makeDb(name);
db = makeDb();
databases.set(name, db);
}
req.result = db;
@@ -112,7 +124,7 @@ describe("key-store - 密钥句柄存取", () => {
const ciphertext = await crypto.subtle.encrypt(
{ name: "AES-GCM", iv },
key,
new TextEncoder().encode("secret")
new TextEncoder().encode("secret"),
);
await K.saveKey(key);