Skip to content

Security: Fission21/lighthouse

Security

docs/SECURITY.md

安全模型:防什么、怎么防、不保证什么

本文档的原则:能证明的写清楚,证明不了的直说。 安全文档里最危险的句子是「残留风险:无」—— 没有哪个闸门能证明「未知漏洞不存在」。宁可把不确定写出来。

威胁模型(按威胁来源分)

# 威胁 灯塔的对策 已知残留风险
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)**做扎实 (范围、拉黑、脱敏、写锁、提权审批),认证留到下一阶段。补法有两条:

  1. 短期、现成:隧道层加 Cloudflare Access(本 repo 未内置,属于部署侧配置);
  2. 长期、内建:把 principal 引入请求链,审计升级为 principal / tool / resource / policy / decision 的决策记录。计划与顺序见 ROADMAP.md。

为什么这么设计

  1. 不可关的默认拉黑——人最容易犯的错是「这次先放开一下」。规则比记性可靠。
  2. fail-closed——安全组件最危险的失败模式是「静默放行」。判定不了就拒,并留痕。
  3. 写操作可回滚——与其赌模型不犯错,不如保证犯错能退回去(备份 + 哈希)。
  4. 一切留痕——审计不是为了事后追责,是为了让你随时能核对「它到底看了什么」。

怎么验证(别信文档,信测试)

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 快照, 只要有一个字节被动过就会报出来。这是「真的没写进去」的证明,而不是「我以为没写进去」。

There aren't any published security advisories