OpenCode 安全机制详解:威胁模型、服务器认证配置与安全漏洞报告流程
OpenCode(开源编码智能体)把 Agent 直接放进了本地系统环境——它可以执行 shell、读写文件、访问网络,因此“它的安全边界在哪里”是使用和生产部署前必须弄清的问题。本文以仓库根目录的 SECURITY.md 为主体,完整还原其威胁模型(Threat Model)与“范围之外”清单,并结合 packages/server/src/auth.ts、serve 命令实现 等源码,讲清楚服务器模式的 OPENCODE_SERVER_PASSWORD 认证机制如何落地,最后给出安全漏洞的负责任披露流程与升级路径。
一、安全报告的硬性前提:不接受 AI 生成的报告
SECURITY.md 开头用 "IMPORTANT" 明确划定了一条红线:项目不接受 AI 生成的安全报告。原因很直接——这类报告数量巨大,团队没有资源逐一审查;若提交此类报告,将导致提交者被自动禁止参与项目。这条规则本身也提示了研究者在复现与验证漏洞时的纪律:报告中必须包含人工完成的最小复现步骤与真实危害分析,而非大模型批量扫描产出的低置信度列表。
二、威胁模型:权限系统不是沙箱,这是最重要的设计声明
2.1 总体定位
SECURITY.md 的威胁模型概览指出:OpenCode 是一个运行在用户本机的 AI 编码助手,其 Agent 系统拥有强大工具——shell 执行、文件操作和网页访问。正因工具能力接近“本机用户权限”,文档随后给出了全文最关键的声明:
OpenCode 不会对 Agent 做沙箱隔离。权限系统(permission system)的定位是 UX 功能:在 Agent 执行命令、写文件等动作前提示用户确认,帮助用户保持对 Agent 行为的知情;它并非为安全隔离而设计。
如果需要对 LLM 驱动的操作做真正的隔离,官方给出的建议是:把 OpenCode 放进 Docker 容器或虚拟机中运行。
2.2 从源码看权限系统到底在做什么
权限系统的实现位于 packages/core/src/permission.ts。从源码结构看,其核心对象包括:
Request/Reply:权限请求与用户答复的结构定义;AskResult:一次权限询问的结果;- 答复取值包含一次性(
"once"语义)与持久化("always")两类——源码中当用户选择always时,会把该请求关联的save规则写入持久层(持久化存储分别由 packages/core/src/permission/saved.ts 与 packages/core/src/permission/sql.ts 承担)。
这段实现与 SECURITY.md 的声明互为印证:权限系统做的事情是“拦截动作 → 询问用户 → 记录用户偏好”,整条链路依赖用户在场并作出选择,它没有任何机制阻止一个已获得权限的进程去访问本机资源。这正是“UX 特性而非安全边界”这句话的工程含义,也是威胁模型中“沙箱逃逸(Sandbox escapes)不属于安全漏洞”这一条的范围依据。
三、服务器模式:opt-in 的 HTTP 服务与 Basic 认证
3.1 文档声明的认证要求
SECURITY.md 对 Server Mode 的表述可以归纳为三点:
- 服务器模式是 opt-in(用户主动开启)的,默认不启用;
- 启用后应当设置环境变量
OPENCODE_SERVER_PASSWORD,以要求 HTTP Basic 认证; - 不设置该变量时,服务器将在未认证状态下运行(并打印警告)——而“如何保护这台服务器”是最终用户的责任,服务器一旦启用,其对外提供的任何功能本身都不构成安全漏洞。
3.2 认证逻辑的源码印证
认证配置与校验集中在 packages/server/src/auth.ts,其中 ServerAuth 模块的关键实现与文档声明完全对应:
- 配置读取:
Config服务从环境变量读取OPENCODE_SERVER_PASSWORD(可选)与OPENCODE_SERVER_USERNAME(默认值"opencode"),即“用户名默认opencode、可覆盖”的行为来源于此; - 是否需要认证:
required(config)判断 password 是否被设置且非空字符串; - 凭证校验:
authorized(credentials, config)逐字段比对用户名与密码(密码以Redacted类型承载,避免在类型层面暴露明文语义); - 客户端构造请求头:
header(credentials)/headers(credentials)将username:password编码为Authorization: Basic base64(...)请求头,供opencode attach等客户端连接受保护的服务器。
3.3 未设置密码时的警告行为
opencode serve 命令的实现在 packages/opencode/src/cli/cmd/serve.ts。启动 handler 中有如下检查:
if (!Flag.OPENCODE_SERVER_PASSWORD) {
console.log("Warning: OPENCODE_SERVER_PASSWORD is not set; server is unsecured.")
}
也就是说,警告是打印在日志/终端里、而不是阻止启动——服务器照常监听(源码随后调用 Server.listen(opts) 并输出 opencode server listening on http://host:port)。这与 SECURITY.md “Without this, the server runs unauthenticated (with a warning)” 的表述逐字对应。该环境变量经 packages/core/src/flag/flag.ts 统一读取(OPENCODE_SERVER_PASSWORD: process.env["OPENCODE_SERVER_PASSWORD"]),供 CLI 各命令共用。
仓库内的中文文档 server.mdx 同样给出了可操作的用法示例,认证配置适用于 opencode serve 和 opencode web 两个命令:
OPENCODE_SERVER_PASSWORD=your-password opencode serve
3.4 服务器模式的实践建议
结合上述声明与实现,可以给出三条落地做法:
- 只在本机、仅自己使用时:保持 opt-in 默认状态即可,不启用服务器;
- 需要对外提供服务/多端接入时:务必设置
OPENCODE_SERVER_PASSWORD(并视需要设置OPENCODE_SERVER_USERNAME),同时通过防火墙限制监听地址与来源,Basic 认证只应作为第一道门,不应替代网络层隔离; - 需要与不信任内容/代码交互时:按威胁模型的建议,把整个 OpenCode 放进 Docker 容器或 VM,用操作系统级边界替代权限系统。
四、明确划出“范围之外”的类别
SECURITY.md 用一张表把五类情形声明为安全报告范围之外(Out of Scope)。这是理解该项目安全边界时最有信息量的部分,逐条继承如下:
| 类别 | 官方给出的理由 |
|---|---|
| Server access when opted-in(主动开启服务器后的访问) | 既然你启用了服务器模式,API 被访问就是预期行为 |
| Sandbox escapes(沙箱逃逸) | 权限系统不是沙箱(见上文威胁模型声明) |
| LLM provider data handling(LLM 供应商的数据处理) | 发送给你所配置 LLM 供应商的数据,由该供应商自身的政策管辖 |
| MCP server behavior(MCP 服务器行为) | 你自行配置的外部 MCP 服务器在项目信任边界之外 |
| Malicious config files(恶意配置文件) | 配置由用户自己控制,篡改它不构成攻击向量 |
这张表的隐含逻辑是:OpenCode 的信任边界锚定在“用户自己的机器、用户自己的配置、用户主动启用的功能”之内。把边界内的问题(例如服务器未设密码时暴露 API)与边界外的行为(例如你自己配置了一个不可信的 MCP 服务器)区分开,是撰写高质量安全报告的前置功课。
五、安全漏洞的报告与升级路径
SECURITY.md 的 "Reporting Security Issues" 部分定义了负责任披露(responsible disclosure)流程:
- 提交渠道:通过 GitHub 仓库的 Security Advisory 入口("Report a Vulnerability" 标签页)提交报告,而不是公开 Issue;
- 响应承诺:团队会对报告给出回复,说明处理流程中的下一步;在首次回复之后,安全团队会持续同步修复与公告的进展,并可能请求补充信息或提供进一步指导;
- 升级路径(Escalation):如果报告提交后 6 个工作日内没有收到确认(acknowledgement),可以发送邮件至
security@anoma.ly进行升级。
对报告人而言,结合第一节的硬性前提,一份可被受理的报告应满足:人工完成复现与危害分析、问题落在“范围之内”(不属于上表五类)、通过私密的 Advisory 渠道提交,并在 6 个工作日未获响应时走邮件升级。
六、小结
把 SECURITY.md 的声明与仓库实现对照,OpenCode 的安全模型可以概括为三层:
- 本地 Agent 层:权限系统提供“事前确认 + 偏好记忆”(见 permission.ts 的 Reply 持久化逻辑),但明确不承诺隔离,强隔离请用容器/VM;
- 服务器层:
OPENCODE_SERVER_PASSWORD开启 HTTP Basic 认证(auth.ts 的required/authorized/header),未设置则未认证运行并给出警告(serve.ts),安全责任在使用者; - 披露层:拒绝 AI 生成报告、私密的 Security Advisory 渠道、6 个工作日的确认窗口与
security@anoma.ly升级邮箱,构成完整的漏洞报告闭环。
理解这三层边界后,无论是本地日常使用、团队共享一台服务器,还是撰写安全报告,都能据此作出与项目设计意图一致的选择。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00