# anti-debug

浏览器端**防调试 / 降低窥探便利度**的轻量工具库（TypeScript）。通过启发式检测「调试器暂停」与「DevTools 可能已打开」，并在命中时回调业务逻辑；可选弱化 `console` 输出。

> **重要**：前端代码**无法**做到不可被逆向或不可被阅读。本库仅**提高成本**，不能替代服务端鉴权、密钥不下发、构建混淆、关闭生产 Source Map 等工程措施。

---

## 功能概览

| 能力 | 说明 |
| --- | --- |
| **定时守护** | `startAntiDebugGuard` 按间隔执行多项检测，命中时调用 `onThreat(reason)` |
| **debugger 耗时** | `detectDebuggerTiming`：利用 `debugger` 断点处前后时间差异常 |
| **窗口 / 视口** | `detectDevToolsByDimensions`：`outer-inner` + 根元素 / `visualViewport` 差值（**不依赖断点**） |
| **console 耗时（可选）** | `detectDevToolsByConsoleTiming`：DevTools 打开时常更慢（默认关，防误报） |
| **本机跳过** | `skipInDevelopment` + `isLikelyLocalDevHost`，避免本地开发时被频繁打断 |
| **noop console** | `installNoopConsole`：可选替换常见 `console.*` 为空实现 |
| **快捷键 / 右键** | `installDevToolsUiBlock` 或 `startAntiDebugGuard({ uiBlock: true })`：拦 F12、Ctrl+Shift+I/J/C 等；可选拦右键、Ctrl+U |

---

## 安装

在 monorepo 中依赖工作区包（示例）：

```json
{
  "dependencies": {
    "anti-debug": "workspace:*"
  }
}
```

```bash
pnpm add anti-debug@workspace:* -F your-app
```

---

## 快速开始

```ts
import { startAntiDebugGuard, installNoopConsole } from "anti-debug"

// 可选：弱化控制台（记得在需要调试时注释掉或条件编译）
const restoreConsole = installNoopConsole()

const guard = startAntiDebugGuard({
  intervalMs: 2500,
  // 本地 localhost 不跑守护，正式域名照常检测
  skipInDevelopment: true,
  // 拦 F12、Ctrl+Shift+I/J/C 等；默认不拦整页右键，需则传 { blockContextMenu: true }
  uiBlock: true,
  onThreat(reason) {
    // reason: 'debugger-timing' | 'devtools-dimensions' | 'devtools-console-timing'
    // 自行实现：跳转、清 DOM、上报等（本库不内置网络请求）
    window.location.replace("/")
  },
})

// 单页应用卸载或不再需要时
// guard.stop();
// restoreConsole();
```

---

## API 说明

### `startAntiDebugGuard(options?)`

- **环境**：仅在存在 `window` 的浏览器中启动 `setInterval`；Node / 无 `window` 时返回 `{ stop: () => {} }`，不抛错。
- **`intervalMs`**：轮询间隔，默认 `2000`。
- **`immediate`**：是否在挂载后立即执行第一次检测，默认 `true`。
- **`skipInDevelopment`**：为 `true` 且当前 hostname 为常见本机地址（见下）时，**不启动**守护。
- **`checks`**：可关闭 `debuggerTiming` / `devToolsDimensions`；`devToolsConsoleTiming` 默认**关闭**，需显式 `true`。
- **`devToolsThresholds`**：微调 `widthGap` / `heightGap` / `layoutGap` / `consoleTimingMs`（漏报时可略调低前两项）。
- **`uiBlock`**：`true` 或 `DevToolsUiBlockOptions` 对象；与 `skipInDevelopment` 同时生效时，本机 hostname 下**不**安装（与轮询一致）。`stop()` 时会一并卸载快捷键监听。
- **`onThreat(reason)`**：有检测为阳性时调用；未传则仅轮询，不执行任何默认破坏操作。

### `AntiDebugReason`（`reason` 取值）

| 值                        | 含义                                                     |
| ------------------------- | -------------------------------------------------------- |
| `debugger-timing`         | `detectDebuggerTiming` 认为执行在断点/调试器下被显著拖慢 |
| `devtools-dimensions`     | 视口 / 尺寸启发式认为像已打开 DevTools（dock 场景为主）  |
| `devtools-console-timing` | 可选；`console` 单次调用耗时偏高（与「停用断点」无关）   |

### `detectDebuggerTiming(thresholdMs?)`

- 默认 `thresholdMs = 120`。耗时超过该值返回 `true`。
- 内含 **`debugger` 语句**：若被 ESLint 禁止，需在项目规则中对本库或本行做例外。

### `detectDevToolsByDimensions(options?)` 或 `(widthGap, heightGap)`

- **对象形式**：可传 `widthGapThreshold`、`heightGapThreshold`、`layoutGapThreshold`（默认 `160` / `300` / `100`），并包含对 `innerWidth - clientWidth`、`visualViewport` 与内视口的辅助判断。
- **两数字形式**：与旧版兼容，等价于仅调整 outer-inner 阈值。
- **「停用断点」**：不会执行 `debugger`，故 **`debugger-timing` 会漏报**；可依赖本项与可选的 `detectDevToolsByConsoleTiming`。
- DevTools **独立窗口 undock** 时主页面尺寸往往不变 → 纯前端**无法可靠**检测，只能依赖服务端或其它手段。

### `detectDevToolsByConsoleTiming(thresholdMs?)`

- 默认 `5` ms；仅建议在 `checks: { devToolsConsoleTiming: true }` 时由守护调用；单独使用亦可。
- 可能有误报，请结合业务与 `devToolsThresholds.consoleTimingMs` 调整。

### `isLikelyLocalDevHost()`

在 `location` 存在时，匹配：`localhost`、`127.0.0.1`、`[::1]`、`0.0.0.0`，以及以 `.local` 结尾的主机名。

### `installNoopConsole(options?)`

- 默认替换常见输出类方法；返回**卸载函数**，务必在测试或需要控制台时调用恢复。

### `installDevToolsUiBlock(options?)`

- 在 `window` 上注册**捕获阶段** `keydown`，默认拦截：**F12**；**Ctrl+Shift+I/J/C/K/E**（Windows/Linux）；**Cmd+Option+I/J/C**、**Cmd+Shift+C**（Mac 常见）。
- 可选：`blockContextMenu: true` 拦截整页右键；`blockViewSource: true` 拦截 **Ctrl+U** 查看源代码。
- **无法**拦截：浏览器菜单「更多工具 → 开发者工具」、扩展、远程调试、从书签/其它页已打开的 DevTools 等。
- 返回卸载函数；也可通过 `startAntiDebugGuard({ uiBlock: true })` 自动安装并在 `stop()` 时卸载。

---

## 局限与误报

1. **「Deactivate breakpoints / 停用断点」与「Resume」**：`debugger` 不暂停时，`debugger-timing` **必然漏报**——这是浏览器行为，不是库 bug。请依赖 **`devtools-dimensions`**，并视情况开启 **`devToolsConsoleTiming`** 或调低 `devToolsThresholds.widthGap` / `heightGap`。
2. **DevTools 独立窗口**：主页面 `outer/inner` 可能不变 → 尺寸类检测**常漏报**；纯前端无完美对策。
3. **尺寸 / console 检测**：分屏、缩放、扩展、省电模式等 → 误报或漏报均可能存在。
4. **性能**：轮询与 `debugger` / `console` 检测占用主线程，间隔不宜过小。
5. **合规与体验**：过度惩罚可能伤害正常用户；请谨慎设计 `onThreat`。
6. **快捷键拦截**：仅挡常见快捷键；无障碍与高级用户仍可通过菜单或其它方式打开 DevTools，勿当作安全边界。

---

## 构建

```bash
pnpm --filter anti-debug build
pnpm --filter anti-debug check-types
```

产物目录 `dist/` **仅两个文件**（单文件分发）：

- `index.js`：单入口 `rolldown` 打成一个 chunk，再经 `javascript-obfuscator` 处理；
- `index.d.ts`：`dts-bundle-generator` 自 `src/index.ts` 合并生成的单文件类型声明（与 `package.json` 的 `types` / `exports` 对齐）。

构建开始时会清空 `dist/`，避免历史构建遗留的多份 `.d.ts`。

若根目录 `.gitignore` 忽略了 `dist/`，则构建产物不入库；克隆后需先执行 `pnpm --filter anti-debug build`，依赖方才能解析类型与运行时代码。

### 压缩与混淆说明

1. **Rolldown（Oxc Minifier）**：`minify` 开启完整压缩与名称混淆；**必须**设置 `compress.dropDebugger: false`，否则默认会删除源码中的 `debugger`，`detectDebuggerTiming` 会失效。
2. **javascript-obfuscator**：对打包结果做字符串阵列、Base64 编码等增强混淆；未开启控制流平坦化/自防御等，以降低体积与误伤风险。

若只需 Rolldown 压缩、不要第二层混淆，可将 `package.json` 中 `build` 改为仅 `rolldown -c`。

---

## 与其他措施的关系

| 措施 | 说明                                     |
| ---- | ---------------------------------------- |
| 本库 | 运行时启发式，可组合埋点与温和响应       |
| 构建 | 压缩、混淆、关闭生产 Source Map          |
| 架构 | 敏感逻辑与密钥放在服务端；前端仅展示结果 |
| 合规 | 遵循当地法律与用户协议，勿用于恶意目的   |

---

## 许可证

ISC（与 `package.json` 保持一致）。
