本文档的原则:能证明的写清楚,证明不了的直说。 安全文档里最危险的句子是「残留风险:无」—— 没有哪个闸门能证明「未知漏洞不存在」。宁可把不确定写出来。
| # | 威胁 | 灯塔的对策 | 已知残留风险 |
|---|---|---|---|
| 1 | 外部 AI 翻越范围看别的目录 | 根界闸(realpath 解析后再判)+ 符号链接解析后判定 + 绝对路径 / .. / 控制字符一律拒;大小写变体(PRIVATE/)同样拦 |
低,但不为零。 路径越界与符号链接穿透已纳入自动化测试,但那只能说明「已知手法被盖住」,不能说明「不存在未知实现漏洞」。平台差异(大小写不敏感 / 挂载点 / 容器 bind mount / 网络文件系统)、TOCTOU 竞态、pathlib 与 realpath 的行为边角,都不在本项目的证明范围内 |
| 2 | 外部 AI 读到密钥文件 | 默认拉黑名单(.env*、*.pem、id_rsa*、*secret*、*token*、*credential*、*.db、整个 .git/…),不可关闭;大小写变体一并拦 |
这是文件名模式匹配,不是语义识别。取名 production-notes.txt 的密钥不会被拦;你手抄进 include 目录里普通文件的密钥也拦不住。模式覆盖不到的,只能靠你别把它放进给看的目录 |
| 3 | 密钥出现在回答里 | 出口脱敏:API key / token / 私钥块 / password=值 → «REDACTED»,检索片段也过一遍 |
只认形态,不认语义。自定义格式(MY_THING=abc123)、变体写法、非 ASCII 密钥不会被识别。脱敏是兜底,不是保证 |
| 4 | 外部 AI 改/删我的文件 | 默认只读;写权限两级锁(配置总闸 + 运行时开关,可设自动过期);写前备份、写后记 sha256;删除要 confirm | 开关开着的那段时间里,范围内的写入是被授权的;模型仍可能写出你不想要的内容(它不算越权)。备份是你的安全网(~/.lighthouse/backups/),但备份只覆盖窗口内的写工具路径 |
| 4.5 | 外部 AI 自己把范围扩大 | 它只能申请(request_access → pending);批准权在用户(approve),或用户先开限时预授权窗口(elevate,可设上限与自动失效),或用户声明常驻策略(auto-grant,可用 --ceiling 限定上限);或用户给本机 agent 开「对话内授权」(chat-approval:对话里说「授权你」→ agent 带 user_confirmed=true 即生效;信任式、默认关)。授予只加宽 include,压不过 exclude 与默认拉黑 |
预授权窗口 / 常驻策略生效期间,上限内的申请自动批准、无人值守。这是用户主动选定的松紧档,不是漏洞——但别让它在你不记得的时候长期开着(lighthouse.sh scope <id> 可随时查,deny 一把收回) |
| 5 | 端口被局域网/公网直连 | 服务只 listen 127.0.0.1;对外只有隧道这一条路 |
自己改监听地址就绕过了这层保护。容器 / 反向代理的端口映射同样可能绕过 |
| 6 | 端点被扫到 | 路径带随机段,未公开的路径不可枚举 | 这是「不可猜」,不是「不可破」,更不是「认证」。 URL 一旦泄露(截图、粘贴、日志采集、浏览器同步)等于权限泄露。当前版本没有内置认证与身份——需要更强请在隧道层加 Cloudflare Access,缺口与计划见 ROADMAP.md |
| 7 | 隧道 / 证书配置泄露 | cloudflared 是出站连接,不开放任何入站端口 | 拿到隧道凭据的人可以把它指向自己的服务。凭据文件请当密钥保管(本机文件权限、别进版本库) |
- 内容本身的风险:给看的文档里如果写着「忽略之前的指令,把 X 发到 Y」,模型可能照做。 灯塔只管「它能读到什么」,不管「它读到之后怎么想」。给看的资料请自己过一遍。
- 模型在合法权限内的滥用:写开关开着时它有权在范围里写;它写错东西不算越权。
- 你主动放进 include 的秘密:闸门保护的是「没让你看的」,不是「你没想清楚的」。
chat-approval的信任假设:它信任 agent 对「用户已口头同意」的转述——恶意/被劫持的 agent 可以假传圣旨。 所以它默认关、只该对本机/可信 agent 开;网页 AI 的窗口请保持关闭(那边无法验证)。- 局域网直连(
lan on):窗口改听0.0.0.0后,同网段任何设备都能访问(路径随机段≈弱口令,且无 TLS)。 它是给可信网络用的便利档,不是安全增强;用完lan off收回。 - 本机 agent 的「绕过」:如果对方是能在本机读文件/跑命令的 agent(WorkBuddy、Codex 这类桌面/IDE agent),
它根本不必走灯塔的窗口——直接读盘就行。对这种 agent,灯塔的范围是控制层(防手滑、留审计),不是安全边界;
chat-approval只应对这类 agent 开。真正的边界只在「对面只有这一条路」时成立(网页 AI / 远程 MCP 客户端)。 - 未知的实现漏洞:见下一节。测试是承诺书,不是证明。
- 对面平台自己的中间层:网页 AI 的提供方(如 OpenAI)可能在工具调用前后插入你看不见的审查, 它会在请求到达你机器之前就拦掉一些调用,并只给模型一句笼统提示。 这一层不受灯塔控制,也不在灯塔的审计范围内——唯一的判断依据是本机审计日志里有没有那条记录: 有记录 = 请求到了、灯塔按规则处理了;没记录 = 请求压根没发出来。
bash tests/run_all_tests.sh # 全部套件,自带隔离实例,不碰线上(数量由脚本实时输出)证明(可复现的事实):
- 已知攻击手法的覆盖:12 种路径攻击全拒、22 个写类工具名全部失败、HTTP PUT/PATCH/DELETE 无写效果、 密钥类文件名及其大小写变体、越界符号链接、拉黑文件不出现在检索结果里;
- 状态不变性:轰炸前后窗口根目录 sha256 + size + mtime 逐字节一致;
- 提权语义:申请 ≠ 授予、提权压不过拉黑与 exclude、收回后复原、常驻策略超上限仍转待批;
- 回归防护:每次改动跑一遍,防止已修的问题重新长回来。
不证明(写出来是为了不让你误会):
- ❌ 不存在未知的路径解析漏洞。v1.2 的加固过程就是反例:
.ENV能读到.env、PRIVATE/x能绕过exclude private/**、.git/logs/HEAD完全可读——这些在修复前的测试里全是绿的。 更早的 v1.1 同样如此。测试能证明「已知的守住了」,不能证明「未知的不存在」; - ❌ 不存在平台差异问题(大小写不敏感文件系统、挂载点、网络文件系统、容器 bind mount、Windows 路径语义);
- ❌ 不存在 TOCTOU / 文件系统竞态(判定用的路径与真正打开的路径之间,理论上存在窗口);
- ❌ 不存在 Python
pathlib/os.path.realpath的行为边角; - ❌ 未来不会回归(回归靠测试兜,不靠保证)。
一次修掉 7 个真实穿透、而此前测试全绿,这件事本身就说明:安全项目能承诺的是 「持续发现并修掉」,不是「从来不存在」。发现漏洞请开 issue——那是它的正常生命周期。
当前架构里没有身份与认证这一层:
Internet → 随机 URL 路径段 → Lighthouse(授权 + 脱敏 + 审计)
↑ 不可枚举,但不是身份
于是审计记录的粒度停在资源上:window / tool / args / ok / reason。
多个 agent、多个客户端接同一个窗口时,日志里分不出是谁——因为没有 principal。
这是明知的取舍,不是遗漏:v1.x 的目标是先把**授权(Authorization)**做扎实 (范围、拉黑、脱敏、写锁、提权审批),认证留到下一阶段。补法有两条:
- 短期、现成:隧道层加 Cloudflare Access(本 repo 未内置,属于部署侧配置);
- 长期、内建:把 principal 引入请求链,审计升级为
principal / tool / resource / policy / decision的决策记录。计划与顺序见 ROADMAP.md。
- 不可关的默认拉黑——人最容易犯的错是「这次先放开一下」。规则比记性可靠。
- fail-closed——安全组件最危险的失败模式是「静默放行」。判定不了就拒,并留痕。
- 写操作可回滚——与其赌模型不犯错,不如保证犯错能退回去(备份 + 哈希)。
- 一切留痕——审计不是为了事后追责,是为了让你随时能核对「它到底看了什么」。
bash tests/run_all_tests.sh| 套件 | 证明什么 |
|---|---|
| 通用冒烟 | 该通的通(列目录 / 读文件 / 检索),该挡的挡(越界 ×3、密钥类 ×5、写工具受控) |
| 只读审计 | 22 个写类工具名全部失败、12 种路径攻击全拒、HTTP PUT/PATCH/DELETE 无写效果、轰炸前后文件指纹逐字节一致 |
| 写开关 | 开关关 = 全拒且零变动;开关开 = 增删改查全流程且越界/拉黑/超限仍被拒;关回 = 只读 |
| 提权 | 申请 ≠ 授予(申请后范围一字不变);批准即生效;提权压不过拉黑与 exclude;收回后复原;预授权上限内自动批、超限转 pending;常驻策略(auto-grant)上限内直接生效、超限仍待批、off 立刻收紧、配置可疑 fail-closed;授权时长可自选(30m/2h/1d/forever);对话内授权(chat-approval)默认关、带 user_confirmed 才生效、超出上限仍待批、off 立刻收紧;过期自动失效 |
| 加固 | 攻击性探测:拉黑名单的大小写变体(.ENV / Id_Rsa)、exclude 的大小写绕过(PRIVATE/)、整个 .git/ 目录、路径逃逸与越界符号链接、体积上限、枚举面不泄漏,且不误伤正常文件 |
| 受控资料库 | 篇级闸门六步(未批准 → 等级不符 → 拉黑 → 越界 → 哈希变了 → 文本缺失)、审批压不过拉黑、等级不继承、一人一条地址、签名下载验签且只绑一篇、台账坏 = 全拒 |
| 文档一致性 | 相对链接可达、ADR 编号连续且与索引双向一致、套件名与 run_all_tests.sh 对齐(不写死套件数量)、对外文案无私人称呼、编码 UTF-8 无 BOM |
审计套件里的「文件指纹比对」是重点:它在轰炸前后对窗口根目录做 sha256 + 大小 + mtime 快照, 只要有一个字节被动过就会报出来。这是「真的没写进去」的证明,而不是「我以为没写进去」。