shambhavi-1204/screen-trust

GitHub: shambhavi-1204/screen-trust

ScreenTrust 通过原生 macOS Agent 和服务端确定性规则引擎,实时检测技术面试中隐藏覆盖应用与屏幕外 AI 助手等作弊迹象,并通过 React 仪表盘向面试官推送证据支撑的警报。

Stars: 0 | Forks: 0

# ScreenTrust **一个实时的面试诚信监控平台。** 一个轻量级的 macOS 伴侣 Agent 会在实时的技术面试中监视隐藏的覆盖应用和屏幕外的 AI 辅助工具的迹象,一个确定性的规则引擎 在服务器端关联信号,面试官仪表盘会实时展示 有证据支撑的警报 —— 不是事后审查的录像, 而是在面试过程*中*实时的、可解释的信息流。 端到端构建:原生 macOS Agent、Spring Boot 后端、Postgres、 确定性检测规则引擎、JWT 认证、用于实时推送的 Server-Sent Events、 React 仪表盘、LLM 关联层,以及 Python 评估测试工具 —— 所有这些都连接在一起,并针对真实的对抗性应用进行了演示, 而不仅仅是单元测试。

ScreenTrust dashboard live demo

## 为什么会有这个项目 居家和实时代码面试越来越多地通过屏幕共享进行, 这带来了一个明显的漏洞:候选人可以运行 AI 助手,或者使用 带有隐藏答案的第二个屏幕,这种方式在面试官的 肉眼看来是不可见的,但从操作系统的角度来看在技术上是可检测的。ScreenTrust 直接探索了 这个检测面 —— 不是探讨“候选人是否在作弊”, 没有任何工具能诚实地回答这个问题,而是探讨“存在哪些操作系统级别的证据,以及 其置信度如何”。 核心假设(在构建任何其他内容之前,首先通过一次性的 Swift 探索性实验进行了验证):**`kCGWindowSharingState == 0`** —— 应用为了将 自身窗口从屏幕录制/共享 API 中排除而设置的标志 —— 正是隐藏的覆盖层所需要的特征,并且无需任何特殊授权即可从 `CGWindowListCopyWindowInfo` 读取。这一观察结果是整个检测管道构建的 基础。 ## 它的实际实时功能 1. 面试官在仪表盘中创建一个会话 → 获得一个 6 字符的加入代码。 2. 候选人将代码粘贴到 ScreenTrust 菜单栏 Agent 中 → 会话状态变为 `ACTIVE`。 3. 该 Agent 每 5 秒扫描一次正在运行的应用程序和窗口元数据 — 签名状态、TCC(屏幕录制 / 辅助功能 / 输入监控) 授权、窗口层级和覆盖率、Dock 存在情况、进程相对于会话开始的启动时间、 活动显示器数量 —— 并将其流式传输到后端。 4. 确定性规则引擎重放每个会话的事件时间线,并在 模式超过其阈值的瞬间, 提出有证据支撑的发现(而不是黑盒分数)。 5. 仪表盘通过 Server-Sent Events 接收推送并即时更新 — 无需轮询,无需刷新。

Live session detail with evidence-backed alerts

上面的截图来自一次真实的运行:一个合成的“覆盖层对抗”应用 (无边框、浮动、`sharingType = .none`、无 Dock 图标、ad-hoc 签名)在 运行真实 Agent 的同一台机器上启动,并且仪表盘 在一个扫描周期内就实时捕捉到了它。 ## 架构 ``` ┌─────────────────────┐ HTTPS/JSON ┌──────────────────────────┐ │ macOS Agent │ ─────────────────────────▶│ Spring Boot Backend │ │ (Swift, AppKit, │ POST /v1/scan │ - JWT auth │ │ MenuBarExtra) │ POST /v1/sessions/:id/* │ - Deterministic rule │ │ │◀───────────────────────── │ engine (timeline │ │ - Window/app scan │ join / end responses │ replay pattern) │ │ - Code sign checks │ │ - Optional LLM │ │ - TCC grant reads │ │ correlation layer │ │ - Offline buffering │ │ - SSE broadcaster │ │ with retry+backoff │ │ │ └──────────────────────┘ └────────────┬─────────────┘ │ Postgres ▼ ┌──────────────────────┐ │ React Dashboard │ │ - Session list │ │ - Live alert feed │ │ - Timeline view │ │ - fetch-based SSE │ │ (header auth, no │ │ query-param token) │ └──────────────────────┘ ``` ### 为什么使用确定性规则引擎,而不仅仅是调用 LLM 每一个发现都需要是可解释和可复现的 —— “触发这个是因为 扫描 #14 显示了一个无签名的、无 Dock 的进程,具有高层级窗口, 持续了 7 次连续扫描”,这是面试官实际可以 评估的东西。规则重放完整的会话时间线(而不是增量状态 突变),因此发现始终可以从存储的证据中推导出来。LLM 关联层位于规则输出*之上*,用于在多个发现之间添加自然语言 叙述 —— 它是第二意见,而不是事实来源,并且默认是关闭的 (每次关联过程都会产生真实的、计费的 API 调用)。 ## 检测规则 | 规则 | 严重程度 | 信号 | |---|---|---| | `unsigned-hidden-app` | 低 | 无签名或仅有 ad-hoc 签名的无 Dock 进程 | | `launched-after-session` | 低 | 启动时间在会话开始之后的无 Dock 进程 | | `floating-overlay-window` | 低 | 具有大型高层级窗口的无 Dock 进程(单次扫描) | | `screen-recording-hidden-app` | 中 | 拥有屏幕录制 TCC 授权的无 Dock 进程 | | `display-count-increased` | 中 | 会话中期活动显示器数量增加(例如镜像第二个屏幕) | | `persistent-overlay` | 高 | 浮动覆盖层条件保持 6 次或以上连续扫描(约 30 秒) | | `suspicious-overlay-pattern` | 高 | 复合条件:覆盖层 + 隐藏的签名/TCC 信号同时发生 | Apple 自身的系统 UI(Dock、通知中心、Spotlight、控制中心) 被明确排除在覆盖层启发式规则之外 —— 这是通过在真实桌面上 运行真实 Agent 而捕捉到的误报,而不仅仅是依靠单元测试。 ## 评估 一个仅使用标准库的 Python 测试工具(`eval/`)针对实时后端运行 带标签的合成场景,并报告召回率、误报率和 延迟 —— 不是“它能编译”,而是实际测量的检测准确率: - **召回率:100%**(6/6 可疑场景准确产生了预期的规则集) - **误报率:33%**(1/3 —— 一个已知的、有文档记录的限制: `screen-recording-hidden-app` 目前无法区分合法的 截图工具和读取屏幕的覆盖层;我们如实 报告了这一点,而不是隐瞒它) - **摄取延迟:** 平均 10ms,最大 56ms - **警报延迟**(扫描 → 决策可见):平均 19ms,最大 128ms - **重复事件处理:** 在重复的 `scanNumber` 上保持幂等性 ## 可靠性,而不仅仅是检测 一个默默停止报告的检测系统比一个偶尔出错的系统 更糟糕。因此,面试官视图还会跟踪监控 管道本身: - **Agent 心跳** — 如果在 15 秒内没有接收到扫描, 后端会将该会话标记为“Agent 未响应”,并通过 SSE 实时推送。 - **序列间隙检测** — 后端跟踪每个会话的扫描编号,并在 任何扫描缺失时发出警告,这可能意味着网络中断*或* 受到了主动干扰。 - **离线缓冲** — Agent 将扫描任务在内存中排队,并以限制指数退避的方式 按顺序清空,这样会话中途的 Wi-Fi 闪断就不会 悄悄丢弃证据或导致扫描序列不同步。 ## 技术栈 | 层级 | 技术栈 | |---|---| | Agent | Swift, AppKit/SwiftUI (`MenuBarExtra`), `CGWindowListCopyWindowInfo`, `SecStaticCode`, TCC.db 读取 | | 后端 | Spring Boot 4, Spring Security 7, Hibernate 7, PostgreSQL, JWT (`jjwt`), Server-Sent Events | | 检测 | 确定性 Java 规则引擎、时间线重放评估、可选的基于 Claude 的关联层 | | 仪表盘 | React 19, TypeScript, Vite, Tailwind CSS v4, React Router, 基于的 fetch SSE 客户端 | | 评估 | Python 3, 仅标准库 — 无需安装依赖 | ## 项目布局 ``` backend/ Spring Boot API — auth, sessions, scan ingestion, rule engine, SSE dashboard/ React interviewer dashboard ScreenTrustMacOS/ Native macOS candidate agent (Xcode project) eval/ Labeled scenario evaluation harness + latest results ``` ## 本地运行 ``` # 1. Backend(需要本地 Postgres;自动创建 schema) createdb screentrust cd backend && ./gradlew bootRun # 2. Dashboard cd dashboard && npm install && npm run dev # 3. Agent — 在 Xcode 中打开 ScreenTrustMacOS/ScreenTrustMacOS.xcodeproj,运行。 # 出现提示时授予 Screen Recording + Full Disk Access 权限。 # 4. Evaluation harness(backend 必须处于运行状态) cd eval && python3 run_eval.py ``` LLM 关联层**默认禁用**(`screentrust.llm.enabled=false`), 因为它会发起真实的、计费的 Claude API 调用 —— 仅在你想试用它时,才设置 `ANTHROPIC_API_KEY` 并开启 该标志。 ## 诚实的局限性 - `screen-recording-hidden-app` 目前无法区分合法的 截图/录制工具和读取屏幕的隐藏覆盖层 — 在评估结果中被记录为已知的误报,而不是 掩盖过去。 - 检测规则不容忍闪烁 —— 对手在扫描之间快速切换 窗口的覆盖层属性可能会逃避持久性 阈值。在最初的检测 探索阶段就被记录为已知的局限性,而不是后来才发现的。 - 离线缓冲的范围仅限于在正在运行的 会话中挺过网络闪断,而不是完整的 Agent 重启 —— 这是一个深思熟虑的范围决定,而不是 疏忽。
标签:macOS原生开发, React, Spring Boot, Syscalls, 云计算, 在线面试防作弊, 域名枚举, 客户端监控, 测试用例, 规则引擎, 逆向工具