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
+22 -6
View File
@@ -28,7 +28,7 @@ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const DIST = path.join(ROOT, "dist");
/** 全局脚本:必须独立成文件,iife 格式,彼此通过 globalThis 通信 */
const GLOBAL_SCRIPTS = ["utils", "env-store", "crypto-store", "key-store"];
const GLOBAL_SCRIPTS = ["utils", "permissions", "env-store", "crypto-store", "key-store"];
/** 独立入口:各自成文件,不做跨文件打包 */
const STANDALONE_SCRIPTS = ["background", "content"];
/** 由 background 通过 chrome.scripting 按需注入的脚本(manifest 里不再静态声明) */
@@ -73,7 +73,9 @@ async function verifyReferences() {
const manifest = JSON.parse(await readFile(path.join(ROOT, "manifest.json"), "utf8"));
const refs = new Set();
const add = (p) => { if (typeof p === "string") refs.add(p); };
const add = (p) => {
if (typeof p === "string") refs.add(p);
};
add(manifest.background?.service_worker);
add(manifest.side_panel?.default_path);
Object.values(manifest.icons || {}).forEach(add);
@@ -101,7 +103,9 @@ async function checkVersionConsistency() {
const manifest = JSON.parse(await readFile(path.join(ROOT, "manifest.json"), "utf8"));
const pkg = JSON.parse(await readFile(path.join(ROOT, "package.json"), "utf8"));
if (manifest.version !== pkg.version) {
console.warn(`[build] 版本号不一致:manifest.json=${manifest.version} package.json=${pkg.version}(执行 npm run release 可同步)`);
console.warn(
`[build] 版本号不一致:manifest.json=${manifest.version} package.json=${pkg.version}(执行 npm run release 可同步)`,
);
}
return manifest.version;
}
@@ -112,7 +116,13 @@ export async function buildAll({ release = false, watch = false } = {}) {
if (watch) {
const contexts = await Promise.all(
entryPoints().map((ep) => esbuild.context({ ...esbuildOptions({ release, format: ep.format, bundle: ep.bundle }), entryPoints: [ep.in], outfile: ep.out }))
entryPoints().map((ep) =>
esbuild.context({
...esbuildOptions({ release, format: ep.format, bundle: ep.bundle }),
entryPoints: [ep.in],
outfile: ep.out,
}),
),
);
await Promise.all(contexts.map((c) => c.watch()));
console.log(`[build] 监听中… v${version}(Ctrl+C 退出)`);
@@ -121,11 +131,17 @@ export async function buildAll({ release = false, watch = false } = {}) {
await rm(DIST, { recursive: true, force: true }); // 清掉历史产物,避免残留旧文件被打进包
for (const ep of entryPoints()) {
await esbuild.build({ ...esbuildOptions({ release, format: ep.format, bundle: ep.bundle }), entryPoints: [ep.in], outfile: ep.out });
await esbuild.build({
...esbuildOptions({ release, format: ep.format, bundle: ep.bundle }),
entryPoints: [ep.in],
outfile: ep.out,
});
}
const refs = await verifyReferences();
console.log(`[build] v${version} ${release ? "release" : "dev"} 构建完成:${entryPoints().length} 个入口,校验 ${refs.length} 处引用`);
console.log(
`[build] v${version} ${release ? "release" : "dev"} 构建完成:${entryPoints().length} 个入口,校验 ${refs.length} 处引用`,
);
return { version, refs };
}