Skip to content
KitploitKITPLOIT
工具漏洞利用博客
提交
工具漏洞利用博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
cplt — 用于 AI 编码代理的沙箱。在 kernel-level 沙箱内运行 Copilot CLI、Claude Code、OpenCode、Gemini CLI、Antigravity、Pi、goose 或普通 shell,并带有 git 和 gh 防护,且沙箱策略已提交到仓库中。 | Kitploit
工具/GitHubGitHub/navikt/cplt
防御工具动态分析 (沙盒)配置审计安全虚拟化DevSecOps命令与控制实用工具与框架秘密检测供应链安全AI 安全
GitHubnavikt/cplt

cplt

12123861天前Kitploit 审核通过

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

用于 AI 编码代理的沙箱。在 kernel-level 沙箱内运行 Copilot CLI、Claude Code、OpenCode、Gemini CLI、Antigravity、Pi、goose 或普通 shell,并带有 git 和 gh 防护,且沙箱策略已提交到仓库中。

查看仓库网站

cplt

CI Release License: MIT macOS Linux

面向 AI 编码代理的内核级强制沙箱。 cplt 封装 GitHub Copilot CLI、OpenCode、Gemini CLI、Antigravity CLI、Pi、Claude Code、goose、DeepSeek Harness 或任意 shell,使代理可以编写代码,但无法窃取凭据、推送到 main、合并 PR 或外泄机密。

  • macOS:通过 sandbox-exec 使用 Apple Seatbelt/SBPL
  • Linux:Landlock LSM + seccomp-BPF + 可选的 Bubblewrap 命名空间隔离(内核 5.13+,6.7+ 支持完整网络过滤)
  • Windows:无原生支持。没有 Windows 沙箱后端。请在 WSL2 中运行 cplt,在那里它就是一个普通的 Linux 安装,且 Microsoft 内核自带 Landlock。参见 Windows (WSL2) 设置。

cplt banner

为什么选择 cplt?

AI 代理会执行任意代码。一个被攻陷的代理,无论是通过提示注入、供应链攻击还是恶意 MCP 服务器,都可能读取 ~/.ssh、推送到 main、合并 PR 或外泄你的代码——除非操作系统本身予以拒绝。

cplt 为你提供内核级强制约束,并支持团队可配置的策略:

  • 在 .cplt.toml 中按仓库配置策略,并提交到版本控制,因此防篡改且可审计
  • 对凭据、机密和敏感文件默认拒绝
  • 命令级 git 和 gh 拦截:推送到默认分支、强制推送、合并和发布均被阻止,功能分支保持开放
  • 带审计日志的出站网络过滤
  • 无需 Docker,无需虚拟机。一个二进制文件即可在锁定加固的笔记本电脑上运行
  • 为开发者提供零配置启动,并在构建确实需要时提供逃生通道

目录

  • 快速开始
  • 它阻止什么
  • cplt 如何比较
  • 安装
  • 用法
  • 配置
  • 架构
  • 安全
  • 网络与代理
  • 命令守卫
  • 已知影响
  • 限制
  • 贡献
  • 参考资料

详细文档: 配置 · 代理与域名过滤 · gh 命令守卫 · git 命令守卫 · 已知影响 · 安全细节 · 安全模型

快速开始```bash

brew install navikt/tap/cplt # macOS. On Debian or Ubuntu, see apt below cplt --shell-install # make 'copilot' run sandboxed (persistent) # --agent opencode for any other agent cplt doctor # check your environment cplt -- -p "fix the tests" # run Copilot in sandbox

root@kitploit:~
其他代理和沙箱命令:```bash
cplt --agent opencode                       # OpenCode (Copilot subscription)
cplt --agent opencode --pass-env ANTHROPIC_API_KEY  # third-party provider
cplt --agent shell                          # interactive sandboxed shell (no AI)
cplt exec -- npm install                    # sandbox any command directly
cplt exec -c "npm install && npm test"      # compound commands in sandbox
alias npm="cplt exec -- npm"               # sandboxed npm for every invocation

团队推广```bash

1. Generate per-repo policy

cplt init --write

2. Developers approve on first run

cplt trust accept --all

3. Tune the command guards (both block by default)

cplt config set git_guard.protect_default_branch_only false # block every push, not just main cplt config set git_guard.mode warn # observe instead of blocking

root@kitploit:~
## 它阻止什么

沙箱在内核中阻止对凭据和密钥的访问。命令守卫阻止破坏性操作。每项限制都适用于智能体及其派生的每个进程。

| 资源 | 状态 | 备注 |
| --- | --- | --- |
| 读写项目目录 | ✅ 允许 | |
| 读取/写入/删除项目中的 `.env*`、`.pem`、`.key` | 🔒 内核阻止 | 防止密钥外泄和破坏。`--allow-env-files` 可覆盖 |
| 写入 `.git/hooks`、`.git/config`、`.gitmodules` | 🔒 内核阻止(macOS),⚠️ Linux 上部分阻止 | 防止通过 git hooks、hooksPath 重定向、子模块劫持实现持久化。**Linux:** Landlock 无法拒绝已允许树内的子路径,因此在仅 Landlock 路径上这些文件仍可写。`bwrap` 将 `.git/hooks` 重新绑定为只读,但有意保留 `.git/config` 和 `.gitmodules` 可写,因此 `core.hooksPath` 仍是一条持久化途径,参见 [Linux 限制](https://github.com/navikt/cplt/blob/main/docs/security.md#linux)。适用于**每个**可写根目录、项目以及每个 `allow.write` 授权,包括被授权的工作树或裸仓库,其真实 hooks 位于 `<root>/.git` 之外 |
| 从 `/tmp`、`/var/folders` 执行 | 🔒 内核阻止 | 防止先写后执行。scratch 目录将 TMPDIR 重定向到安全位置,默认开启 |
| 写入 PATH 解析的 bin/shim 目录(`~/.bun/bin`、`~/.deno/bin`、`$PNPM_HOME`、mise `shims/` 以及整个 `installs/`) | 🔒 内核阻止(macOS),⚠️ Linux 上 mise 部分阻止 | 防止木马化你的下一条*非沙箱*命令通过 PATH 解析到的二进制文件。这也是 `~/.cargo/bin` 和 `~/go/bin` 一直只读的原因。会故意破坏 cplt 内的 `bun install -g`、`deno install`、`pnpm add -g`、`mise install`、`mise upgrade`、`mise use -g`,并且固定了未安装工具链的仓库不再能自动引导。项目本地安装不受影响。**Linux:** mise 的两项依赖 `bwrap` 只读覆盖层;其余原生保持。参见 [全局工具安装](https://github.com/navikt/cplt/blob/main/docs/known-impacts.md#global-tool-installs) |
| 从 `~/Library/Caches` 执行 | 🔒 默认内核阻止 | 防止二进制投放暂存。Copilot 原生模块通过例外豁免。使用 `--allow-cache-exec <SUBDIR>` 添加定向豁免,例如 `ms-playwright` |
| 修改 `.vscode/tasks.json`、`launch.json` | ⚠️ 允许,已知风险 | IDE 信任边界。缓解措施参见 [SECURITY.md](https://github.com/navikt/cplt/blob/main/SECURITY.md) |
| 读取/写入 `~/.copilot`(认证、设置) | ✅ 允许 | 包括用于 `keytar.node`、`pty.node`、`computer.node` 的 `file-map-executable` |
| 写入 `~/.copilot/pkg`(原生模块) | 🔒 内核阻止 | 防止通过替换原生模块实现持久化 |
| 环境变量 | 🔒 净化 + 加固 | 仅安全允许列表可通过。生命周期脚本被阻止。`--pass-env VAR` 可加回一个 |
| 读取 `~/.config/gh/hosts.yml` + `config.yml` | ✅ 允许(只读) | 仅这两个文件。`.config/gh` 的其余部分被阻止 |
| 读取 `~/.config/mise` | ✅ 允许(只读) | 工具版本和 PATH,无密钥 |
| 读取 `~/.gitconfig`、`~/.config/git/config` | ✅ 允许(只读) | dotfiles 符号链接会跟随到其目标,因此 stow 管理的 `~/.gitconfig` 可正常工作 |
| 读取 `~/.git-credentials` | 🔒 内核阻止 | `credential.helper = store` 将明文令牌保存在此处。任何 `--allow-read` 都无法重新打开它,类似 `~/.netrc`。**Linux:** 对*祖先*(`$HOME` 本身)的授权仍会暴露它,因为 Landlock 无法拒绝已允许树内的子路径 |
| 读取全局 git hooks(`core.hooksPath`) | ✅ 允许(只读,拒绝写入) | 自动检测。必须位于 `$HOME` 下且深度 ≥3。写入被明确阻止 |
| 提交/标签签名(`commit.gpgsign`、`tag.gpgsign`) | 🔒 已禁用 | `~/.ssh` 和 `~/.gnupg` 中的私钥被阻止,因此通过环境变量覆盖禁用签名 |
| 读取 `~/Library/Application Support/Microsoft` | ✅ 允许(只读) | 用于遥测的设备 ID |
| 访问 macOS Keychain | ⚠️ 允许(读+写)用于在此存储认证的智能体 | 该授权无法限定到单个条目,因此会触及智能体可解锁的每个钥匙串条目。选择启用 `sandbox.keychain_substitute`(实验性,默认关闭)以在智能体无需它即可认证的运行中放弃它——Claude Code 使用 `CLAUDE_CODE_OAUTH_TOKEN`,Antigravity 使用现有的回退令牌文件。参见 [SECURITY.md](https://github.com/navikt/cplt/blob/main/SECURITY.md#keychain-access-is-all-or-nothing) |
| 出站网络(端口 443) | ✅ 允许 | 其他所有端口被阻止。使用 `--allow-port` 添加额外端口 |
| 本地主机出站 | 🔒 内核阻止(macOS),⚠️ Linux 上基于端口 | 防止访问本地服务。入站仍可用于代理。**Linux:** Landlock 规则仅为端口号,无法区分 `localhost:443` 和 `remote:443`,因此允许端口上的本地服务可被访问,且没有针对 localhost 的特定拒绝。使用 `--with-proxy` 获得 SSRF 保护,参见 [Linux 限制](https://github.com/navikt/cplt/blob/main/docs/security.md#linux) |
| SSH 代理(unix socket) | 🔒 内核阻止(macOS),⚠️ Linux 上仅环境变量 | 防止签名 git 操作或 SSH 到主机。**Linux:** unix socket 的 `connect()` 不受门控,因此被扣留的 `SSH_AUTH_SOCK` 是唯一屏障,自行设置它的智能体可以使用已加载的密钥。`bwrap` 隐藏 `/tmp` 下的标准 OpenSSH socket,但不隐藏 `$XDG_RUNTIME_DIR` 下的 gnome-keyring/gcr 或 systemd 代理。参见 [Linux 限制](https://github.com/navikt/cplt/blob/main/docs/security.md#linux) |
| 开发者工具(`~/.cargo`、`~/.gradle`、`~/.m2`、`~/.sdkman`、`~/.jenv`、`~/.pyenv`、`~/.konan` 等) | ✅ 允许(缓存读+写) | 仅磁盘上存在的目录。运行时根据 `cplt doctor` 检测结果收紧 |
| 注册表凭据文件(`~/.m2/settings.xml`、`~/.gradle/gradle.properties`、`~/.cargo/credentials`) | 🔒 macOS 上内核阻止。Linux 上父工具目录保持可读 | 使用 `--allow-read` 覆盖。参见 [私有注册表](https://github.com/navikt/cplt/blob/main/docs/known-impacts.md#private-registries) |
| 读取 `~/.npmrc` | 🔒 内核阻止(两个平台) | 使用 `--allow-read` 覆盖。破坏 yarn 1,参见 [yarn 1](https://github.com/navikt/cplt/blob/main/docs/known-impacts.md#yarn-1-and-unreadable-home-rc-files) |
| Go 源代码(`~/go/src`) | 🔒 内核阻止 | 仅 `~/go/bin` 和 `~/go/pkg` 可读 |
| 读取 `~/.ssh`、`~/.gnupg`、`~/.aws`、`~/.azure` | 🔒 内核阻止 | |
| 读取 `~/.kube`、`~/.docker`、`~/.nais` | 🔒 内核阻止 | |
| 读取 `~/.password-store`、`~/.terraform.d` | 🔒 内核阻止 | |
| 读取 `~/.config/gcloud`、`~/.config/op` | 🔒 内核阻止 | 单个文件可用 `--allow-read` 覆盖。参见 [云凭据](https://github.com/navikt/cplt/blob/main/docs/known-impacts.md#cloud-credential-directories) |
| 读取或写入 `~/.config/cplt`、`~/.nav-pilot` | 🔒 内核阻止 | 决定*下一次*启动可做什么的工具状态。`~/.config/cplt` 作为整个子树不可覆盖;在 `~/.nav-pilot` 内,命名路径仍可授权,以便读取固定的 agentpakke 载荷 |
| 读取 `~/.netrc`、`~/.pypirc`、`~/.vault-token` | 🔒 内核阻止 | 两个平台上均不可覆盖。在 `allow.read` 中命名其中一个是启动错误 |
| 读取 `~/.gem/credentials` | 🔒 内核阻止 | 两个平台上均不可覆盖。在 `allow.read` 中命名其中一个是启动错误 |
| `gh` CLI 破坏性操作(merge、delete、release) | 🔒 命令门控(默认开启) | 使用 `--no-gh-guard` 选择退出。参见 [gh guard](https://github.com/navikt/cplt/blob/main/docs/gh-guard.md) |
| `git push` 到默认分支 | 🔒 命令门控(默认开启) | 阻止推送到 `main`/`master`;功能分支推送仍可工作。`protect_default_branch_only = false` 阻止所有推送,`git_guard.mode = "warn"` 仅警告,`--no-git-guard` 选择退出 |
| 子进程继承 | ✅ 所有限制适用于子进程 | |

该表是摘要。沙箱还允许访问系统文件(SSL 证书、`/etc/hosts`)、临时目录(读和写,不可执行)以及系统工具路径(`/usr/bin`、`/opt/homebrew`)。运行 `cplt --print-profile` 获取完整的 SBPL 规则。

有关完整安全模型、威胁分析和测试策略,请阅读 [SECURITY.md](https://github.com/navikt/cplt/blob/main/SECURITY.md)。

## cplt 如何比较

### Codex CLI 的沙箱

| 领域 | cplt | Codex CLI 沙箱 |
| --- | --- | --- |
| 出站网络控制 | 带域名允许/阻止列表的 CONNECT 代理 | 无域名级过滤 |
| 环境处理 | 允许列表加加固环境注入 | 更基础的透传模型 |
| 密钥文件保护 | 拒绝仓库内的 `.env*`、`.pem`、`.key` 等模式 | 主要基于目录范围的访问 |
| 仓库策略 | [`.cplt.toml`](https://github.com/navikt/cplt/blob/main/docs/configuration.md#per-repo-configuration-cplttoml) 带显式信任/批准流程 | 无仓库级策略文件 |
| 智能体支持 | Copilot、OpenCode、Gemini CLI、Antigravity CLI、Pi、Claude Code、goose、DeepSeek Harness 或 shell | 仅 Codex |

cplt 并非在所有方面都更强。Codex CLI 目前已有 Linux 命名空间隔离,并且已暴露只读和 workspace-write 等显式沙箱模式。cplt 尚无该模式矩阵。

### 基于 Docker 的沙箱

| 领域 | cplt | 基于 Docker 的沙箱 |
| --- | --- | --- |
| 启动时间 | 正常 CLI 使用几乎即时 | 容器启动通常较慢 |
| 网络控制 | 通过代理进行每请求出站过滤 | 通常为全有或全无的网络访问 |
| 文件控制 | 按路径和按模式规则 | 按挂载控制 |
| 主机要求 | 单个二进制文件 | 需要 Docker 守护进程 |
| 企业笔记本适用性 | 在 Docker 不可用或受限处可工作 | 常被本地策略阻止 |

Docker 在某些环境中仍提供更强的隔离,尤其是当你想要完全独立的文件系统和进程命名空间时。cplt 以更轻量的设置和与你已用于开发的机器更紧密的集成来换取这一点。

### VS Code 智能体模式权限

VS Code 智能体模式等工具主要依赖 UI 权限。cplt 在内核中强制执行其限制,因此智能体无法通过提示或修改指令绕过它们。这对 CLI 智能体和凭据暴露最为重要:

- cplt 在 IDE 之外工作
- 环境变量在智能体启动前被过滤
- 敏感文件即使位于仓库内也可被阻止
- 相同的限制适用于子进程

### Claude Code 的沙箱(Anthropic Sandbox Runtime)

[Anthropic Sandbox Runtime](https://github.com/anthropic-experimental/sandbox-runtime)(`srt`)是 Claude Code 使用的沙箱层。与 cplt 相同的高层方法,macOS Seatbelt 加内核级 Linux 强制执行加 HTTP 代理,实现不同。

| 领域 | cplt | Anthropic srt |
| --- | --- | --- |
| 语言 / 交付 | 单个 Rust 二进制文件 | Node.js + npm 包 + 外部依赖 |
| Linux 后端 | Landlock LSM(无依赖,无命名空间) | bubblewrap(通过用户命名空间的容器) |
| 环境过滤 | 严格允许列表 + 后缀拒绝(`_TOKEN`、`_SECRET`) | 继承完整父环境(密钥透传) |
| 凭据目录保护 | 默认拒绝 15+ 个目录 | 用户必须手动配置 |
| DNS 重绑定保护 | ✅ DNS 后 IP 对照私有范围检查 | ❌ 未实现 |
| 网络代理 | HTTP CONNECT + 域名允许/阻止 | HTTP + SOCKS5 + 实验性 TLS MITM |
| SSH git | macOS 上内核阻止(代理 socket 被拒绝);Linux 上仅扣留 `SSH_AUTH_SOCK` | 通过 SOCKS5 代理 |
| 包管理器脚本 | 默认阻止(`npm_config_ignore_scripts`) | 未阻止 |
| 智能体支持 | Copilot、OpenCode、Gemini、Antigravity、Pi、Claude Code、goose、DSH、Shell | Claude Code |
| 配置 | TOML(全局 + 每仓库) | JSON(仅全局)+ `--control-fd` 实时更新 |
| 库 API | ❌ 仅二进制 | ✅ 可嵌入的 TypeScript 库 |

cplt 开箱即用更安全:环境过滤、凭据保护、DNS 重绑定检查、生命周期脚本阻止。srt 更灵活:SOCKS5、TLS 检查、每请求回调、库嵌入。Linux 后端选择很重要。由于 AppArmor userns 限制,bwrap 在 Ubuntu 24.04+ 上需要变通方案,而 Landlock 需要内核 5.13 或更新版本,但零外部依赖。

### GitHub Copilot CLI 自带的沙箱

Copilot CLI 自 2026 年 6 月起随附本地沙箱,包含在标准席位中。它通过 Microsoft MXC 运行 shell 命令,在 macOS、Linux 和 Windows 上具有受限的文件系统、网络和系统访问。`/sandbox enable` 可开启它。

如果这已满足你的需求,就用它。它不额外收费,并且可在 Windows 上运行,而 cplt 不能。

它不做两件事。

策略随管理员而非仓库。企业通过 Intune 或其他 MDM 设置沙箱策略。代码旁边没有任何东西,因此对一个仓库重要的规则无法跟随它到贡献者、CI 或 MDM 不管理的笔记本。在 cplt 中,策略是仓库中的 `.cplt.toml`。审查者在拉取请求中看到对它的更改,该文件可以收紧开发者自己的配置,但绝不能放松它。

它限制进程,而非进程对其持有的凭据做什么。`/sandbox` 标签涵盖文件系统、网络和系统能力,在 Git 仓库内,智能体默认被授予对 `.git` 的读写权限。沙箱化的智能体仍拥有你的 `gh` 令牌和推送访问权限。推送分支、合并拉取请求和删除仓库都是来自授权客户端的格式良好的 API 调用,文件系统或网络规则对它们没有意见。cplt 改为包装 `git` 和 `gh`。智能体可自由提交、分支和变基。`gh pr merge`、`gh repo delete` 和 `gh release create` 默认被阻止。`git push` 到 `main`/`master` 也是如此;功能分支推送仍可工作,因为 `protect_default_branch_only` 已开启。将其设为 `false` 以阻止所有推送,或 `git_guard.mode = "warn"` 仅警告。

同时运行两者是合理的。MXC 限制进程。守卫决定智能体可以对其持有的凭据做什么。

### 诚实的差距

- macOS 目前拥有最强的文件级强制执行。Linux 覆盖正在改善但尚不相同。
- cplt 尚未提供简单的只读 / workspace-write / 完全访问策略预设。
- 如果你想要完整的容器隔离,cplt 并不试图取代 Docker。

## 安装

### Homebrew(推荐)```bash
brew install navikt/tap/cplt

mise```bash

mise use -g 'github:navikt/cplt@'

root@kitploit:~
mise 会为你的平台选择正确的发布资产,并验证其构建来源证明。

固定版本。我们的版本字符串不是可比较的 semver——它们带有前导零和两个连字符——因此 `mise latest` 可能会解析到比最新版本更旧的发布版本([navikt/copilot#818](https://github.com/navikt/copilot/issues/818))。

### apt(Debian/Ubuntu,在 Linux 上推荐)

[navikt/apt](https://navikt.github.io/apt/) 是一个通过 GitHub Pages 提供的签名归档,包含适用于 amd64 和 arm64 的 cplt 和 nav-pilot:```bash
curl -fsSL https://navikt.github.io/apt/keyring/navikt-archive-keyring.gpg \
  | sudo tee /usr/share/keyrings/navikt-archive-keyring.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/navikt-archive-keyring.gpg] https://navikt.github.io/apt stable main" \
  | sudo tee /etc/apt/sources.list.d/navikt.list
sudo apt update && sudo apt install cplt

它是一个普通的 apt 仓库,用于镜像我们的发布版本,而不是一个拥有自己维护者的发行版软件包。它的发布任务每小时运行一次,从每个工具的最新发布中拉取最新的 .deb,因此几分钟前刚发布的版本最多需要一小时才能通过这种方式安装。

该软件包将二进制文件放在 /usr/bin/cplt,此后升级通过 sudo apt upgrade 进行。cplt update 拒绝触碰 apt 安装,并指向 sudo apt upgrade:在 dpkg 背后替换二进制文件会被下一次 apt 运行撤销。

如果没有该归档,同样的 .deb 就是一个发布资产:```bash arch=$(dpkg --print-architecture) # amd64 or arm64 gh release download --repo navikt/cplt --pattern "${arch}.deb" sudo apt install ./cplt_"${arch}".deb

root@kitploit:~
### curl | bash

对于非 Debian 衍生版发行版,以及用于 CI 的场景:```bash
curl -fsSL https://raw.githubusercontent.com/navikt/cplt/main/install.sh | bash

选项:```bash

Install a specific version

curl -fsSL ... | bash -s -- --version 2026.05.05-174753-75bae5b

Install to a custom directory

curl -fsSL ... | bash -s -- --dir ~/.local/bin

Skip Homebrew (force direct download)

curl -fsSL ... | bash -s -- --no-brew

root@kitploit:~
### 从 releases 下载

从 [GitHub Releases](https://github.com/navikt/cplt/releases/latest) 获取适用于你平台的最新构建版本:```bash
# macOS, Apple Silicon (M1/M2/M3/M4)
curl -fsSL https://github.com/navikt/cplt/releases/latest/download/cplt-aarch64-apple-darwin.tar.gz | tar xz
sudo mv cplt /usr/local/bin/

# macOS, Intel
curl -fsSL https://github.com/navikt/cplt/releases/latest/download/cplt-x86_64-apple-darwin.tar.gz | tar xz
sudo mv cplt /usr/local/bin/

# Linux, x86_64
curl -fsSL https://github.com/navikt/cplt/releases/latest/download/cplt-x86_64-unknown-linux-gnu.tar.gz | tar xz
sudo mv cplt /usr/local/bin/

# Linux, ARM64
curl -fsSL https://github.com/navikt/cplt/releases/latest/download/cplt-aarch64-unknown-linux-gnu.tar.gz | tar xz
sudo mv cplt /usr/local/bin/

每个发布二进制文件都附带构建来源证明。验证方法:```bash gh attestation verify cplt -o navikt

root@kitploit:~
### 从源码构建```bash
git clone https://github.com/navikt/cplt.git && cd cplt
cargo build --release
sudo cp target/release/cplt /usr/local/bin/

或使用 mise:```bash mise run install

root@kitploit:~
`mise run install` 和手动构建会将 cplt 安装到 `/usr/local/bin/cplt`。如果你同时还有位于 `/opt/homebrew/bin/cplt` 的 Homebrew 构建版本,请将 `/usr/local/bin` 放在 `PATH` 的最前面,这样你的开发构建版本会优先使用:```bash
# Check which cplt is active
which cplt

# If it shows /opt/homebrew/bin/cplt, reorder your PATH:
export PATH="/usr/local/bin:$PATH"

或者直接显式运行 /usr/local/bin/cplt,完全跳过 PATH 解析。

Windows (WSL2)

cplt 没有 Windows 沙箱后端。强制隔离在 macOS 上是 Apple Seatbelt,在 Linux 上是 Landlock LSM,因此在 Windows 上没有任何可原生运行的内容。受支持的途径是 WSL2,在其中 cplt 是普通的 Linux 安装,沙箱由内核强制执行。每个 Microsoft 内核分支都构建了 CONFIG_SECURITY_LANDLOCK=y,并在 CONFIG_LSM 中将 landlock 列在首位(config-wsl),自内核 5.15.57.1 起发布,且 WSL 的默认内核命令行未设置任何 lsm= 覆盖。

在 PowerShell 中,执行一次:```powershell wsl --install # WSL2 + the default distro (now Ubuntu 26.04 LTS), then reboot wsl --install -d Ubuntu-24.04 # ...or pin an older release wsl --update # keep the Microsoft kernel current, see the ABI note below

root@kitploit:~
以下所有内容都在**发行版内部**运行(`wsl`,或 Windows Terminal 中的 Ubuntu 配置文件),而不是在 PowerShell 中:```bash
# 1. Node. Copilot CLI requires Node 22+
#    Ubuntu 26.04 ships 22.x, so apt is enough:
sudo apt update && sudo apt install -y nodejs npm
#    Ubuntu 24.04 ships Node 18, too old. Use nvm, fnm, or NodeSource there instead.

# 2. GitHub CLI, and log in. Ubuntu's universe package works but lags
#    (2.45 on 24.04); add GitHub's apt repo if you want a current gh:
#    https://github.com/cli/cli/blob/trunk/docs/install_linux.md
sudo apt install -y gh
gh auth login

# 3. The agent, installed in the distro, never on the Windows side
npm install -g @github/copilot

# 4. cplt, from the apt archive. The default distro is Ubuntu, so this is
#    the same route as on any other Debian derivative.
curl -fsSL https://navikt.github.io/apt/keyring/navikt-archive-keyring.gpg \
  | sudo tee /usr/share/keyrings/navikt-archive-keyring.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/navikt-archive-keyring.gpg] https://navikt.github.io/apt stable main" \
  | sudo tee /etc/apt/sources.list.d/navikt.list
sudo apt update && sudo apt install cplt

# 5. Check the result
cplt doctor

不要在 Windows 侧安装 Copilot CLI。 在互操作开启(默认)的情况下,Windows 的 PATH 会被追加到发行版的 PATH 之后,因此在 Windows 侧执行 npm install -g @github/copilot 后,会在发行版内以 /mnt/c/Users/<user>/AppData/Roaming/npm/copilot 的形式出现。那是通过互操作访问到的 Windows 安装。它无法在 Linux 沙箱中运行,而且 npm 的 shim 会执行一个 node,除非你在发行版里也装了一个,否则发行版中不会有这个 node。过去的表现是一个看似无关的运行时解压错误。现在,当 cplt 在 /mnt/<drive>/ 下解析到某个 agent 并且 它运行在 WSL 下时,会指出原因,并且 cplt doctor 会将其报告为一项失败的检查,而不是通过(#188)。WSL 的检测基于内核拥有的状态,即 /run/WSL 或 /proc/sys/kernel/osrelease 与 /proc/version 中的内核名称,而不是 WSL_DISTRO_NAME——后者在 sudo 下和 systemd 单元中不存在,而且任何进程都可以设置它。在普通 Linux 机器上, 不会被处理。在那里它只是一个普通的挂载点。

该检查有两个限制,都是有意为之。它依据的是默认自动挂载根目录,因此如果你将其迁移了(/etc/wsl.conf 中的 [automount] root),Windows 侧的安装就不会被识别,你会得到旧的、帮助较小的失败信息,其中包含路径。而关闭互操作会阻止 Windows 的 PATH 泄漏进来,但不会卸载 /mnt/c。

内核与 Landlock ABI。 当前的 WSL(2.7.x 及更高版本)搭载 Linux 6.18,提供 Landlock ABI 7——cplt 使用的所有功能都可用,除了 unix-socket 的 connect() 权限,它需要 ABI 9(内核 7.1)。仍停留在 6.6 内核线上的安装得到的是 ABI 3:文件系统规则会被强制执行,但 TCP 端口规则(ABI 4)、ioctl 限制(ABI 5)以及信号/抽象套接字作用域(ABI 6)不可用,网络过滤会回退到 CONNECT 代理。wsl --update 可以让你向前推进。cplt doctor 会打印内核版本以及它检测到的 ABI,这是在你机器上真正重要的检查。

不要在 .wslconfig 中禁用 Landlock。 如果 [wsl2] kernelCommandLine 中的 lsm= 列表省略了 landlock,或者自定义的 [wsl2] kernel= 在构建时没有 CONFIG_SECURITY_LANDLOCK,就会移除 cplt 所依赖的内核强制执行,cplt doctor 会报告 Landlock 不可用。

将项目放在 Linux 文件系统中。 在发行版内的 ~/src/... 中工作,而不是 /mnt/c/Users/...。微软自己的指导是,跨操作系统的文件访问明显更慢,而且从 WSL 2.9.x 起,/mnt/c 默认通过 9p 提供(virtiofs 需通过 [wsl2] virtiofs=true 选择启用)。更重要的是,我们尚未验证 Landlock 在该挂载点上如何执行规则。内核文档中没有针对网络或 FUSE 支持的文件系统的排除说明,只有管道、套接字和 nsfs,而且 Landlock 自己的测试套件覆盖了 9p 和 FUSE,因此我们预期它能工作。这里没有人确认过。请将 /mnt/c 下的项目视为未经证实,而非受支持。

Bubblewrap。 Ubuntu 23.10+ 通过 kernel.apparmor_restrict_unprivileged_userns 阻止非特权用户命名空间,这会破坏 bwrap。该 sysctl 来自一个 Ubuntu 内核补丁,而微软内核中没有这个补丁,因此可选的 Bubblewrap 层预期在 WSL2 下的 Ubuntu 上可以工作。这是从内核源码推断的,并非我们实际运行过的。如果 bwrap 在那里失败,请在 #189 中说明。cplt 自己的 seccomp 过滤器是一个普通的 PR_SET_SECCOMP BPF 程序,它会叠加在 WSL 为每个进程安装的过滤器之上。

尚未在真实的 WSL2 安装上验证。 已从源码验证:Landlock 已编译进微软内核,并且在 CONFIG_LSM 中排在首位;/mnt/<drive>/ 检测、它使用的 WSL 信号及其错误文本;cplt doctor 在此类 agent 上会失败并打印内核 + Landlock ABI;5.13+/6.7+ 的要求;以及 install.sh 会安装 Linux 发行版二进制文件。这里仍无人验证:Landlock 在 /mnt/c 上的行为、Bubblewrap 在 WSL2 下是否工作、你的发行版版本所附带的确切软件包版本,以及上述流程的端到端运行。如果你运行了它,请在 #189 中报告实际发生的情况。

Shell 设置(推荐)

默认情况下,你通过输入 cplt 来获得沙箱。要让普通的 copilot 也以沙箱方式运行:```bash cplt --shell-install

root@kitploit:~
它会检测你的 shell,将别名追加到你的 rc 文件中,并打印它所做的操作。你可以随意运行多次,它不会添加重复项。

`--agent` 选择哪个命令获得别名,cplt 能启动的每个 agent 都可用:```bash
cplt --shell-install --agent opencode   # 'opencode' runs sandboxed
cplt --shell-install --agent claude     # and 'claude', alongside the others

每次安装都会追加到你的 rc 文件,而不是替换其中的内容,因此你可以为你使用的任意多个 agent 创建沙箱。不使用 --agent 时,你会得到 copilot,这也是该标志一直以来所安装的内容。

--agent antigravity 会同时为 antigravity 和 agy 安装别名,因为这两个名称启动的是同一个 agent。

重启你的 shell 或 source 该文件以使其生效。

--agent shell 没有别名:不存在可供遮蔽的 shell 二进制文件。输入 cplt --agent shell 可获得沙箱 shell,或输入 cplt exec -- <command> 执行单条命令。

手动设置(替代方案)

如果你不想使用 --shell-install,可以自行添加该行:```bash

zsh / bash

eval "$(cplt --shell-setup --agent opencode)"

fish

alias opencode 'cplt --agent opencode'

root@kitploit:~
mise、direnv 和 starship 也使用相同的模式。
</details>

**为什么每个别名都指定其 agent。** `alias opencode=cplt` 不会达到看起来的效果。直接运行 `cplt` 时,它会先从 `--agent` 选择 agent,然后是配置文件,最后是 PATH 中找到的内容——而 PATH 检测会优先选择 `copilot`。输入 `opencode` 反而会沙箱化 Copilot,而屏幕上不会有任何提示。别名会传递 `--agent`,因此你输入的命令就是你得到的 agent。

**为什么用别名而不是符号链接?** cplt 和 Copilot CLI 安装到同一个 Homebrew bin 目录(`/opt/homebrew/bin/`),而那里只能有一个名为 `copilot` 的文件,因此符号链接会产生冲突。别名可以避开这个问题。真正的 `copilot` 二进制文件保留在 PATH 中,cplt 可以找到并包装它,而别名会重定向你的命令。

> **注意:** cplt 拒绝嵌套。如果它检测到自己已经在沙箱内运行(通过 `__CPLT_WRAPPED` 环境变量),它不会再次启动。只读子命令如 `--print-profile` 和 `cplt doctor` 在现有沙箱内仍然可用。

## 用法```
cplt [OPTIONS] [-- <AGENT_ARGS>...]

-- 之后的所有内容都会直接传递给 agent 进程(copilot、opencode、gemini、antigravity、pi、claude、goose、dsh 或 shell)。

策略预设

预设用一个标志为五个主要沙箱开关设置基线,而无需逐一列出它们。单独的标志仍然优先于预设,因此 --preset permissive --no-allow-tmp-exec 会按字面意思执行。也可在配置中通过 [sandbox] preset = "..." 设置。

下载工具
/mnt/c
Shell修改的文件添加的内容(针对 --agent opencode)
zsh(macOS 默认)~/.zshrceval "$(cplt --shell-setup --agent opencode)"
bash~/.bashrceval "$(cplt --shell-setup --agent opencode)"
fish~/.config/fish/conf.d/cplt.fishalias opencode 'cplt --agent opencode'
标志作用
--preset strict完全网络锁定。五个开关全部关闭,同时开启 gh_guard、git_guard、proxy.forced(强制代理出口)和 proxy.default_allowlist(故障关闭域名允许列表)。逃生通道:--allow-all-domains 仅禁用允许列表
--preset standard当前默认值。五个全部关闭,scratch 目录保持开启。等同于不传预设
--preset permissive开启 allow_localhost_any、allow_tmp_exec 和 allow_lifecycle_scripts
--preset full-trust⚠️ 危险。开启全部五个,并额外开启 allow_env_files 和 allow_docker

完整预设矩阵与解析顺序:docs/configuration.md。

文件访问

项目目录是可写工作区,外加一个用于认证、运行时和工具链的狭窄允许列表(见上表)。内核会阻止其他一切,包括 SSH 密钥和云凭证。

标志作用
-d, --project-dir <DIR>Copilot 可以在哪个目录中工作。默认为当前 git 仓库根目录
--allow-read <PATH>允许 Copilot 以只读方式读取项目外的文件。可重复
--allow-write <PATH>允许 Copilot 读写项目外的文件。请谨慎使用。可重复。该目录树可写但不可执行——既可写又可执行的目录树是一条二进制投放路径,因此对 ~/.cargo 的 allow.write 也会阻止 ~/.cargo/bin 运行。当你需要两者时,请对单独且不重叠的目录树使用 --allow-exec
--allow-exec <PATH>⚠️ 危险。允许 agent 从默认工具目录之外的目录树执行二进制文件——例如迁移过的 Homebrew 或工具链前缀。授予读取和执行权限,绝不授予写入权限。可重复。对于不安全的根目录(/、/tmp、$HOME 及其父目录、平台系统目录)以及与可写目录树重叠的任何目录树会被拒绝——项目目录、--allow-write 授权、可写工具目录(如 ~/.cache)、可写 agent 数据目录(~/.claude、~/.local/share/opencode、~/.pi/agent 等)、worktree 或裸仓库的真实 .git,或后端在无任何授权情况下就将其设为可写的目录树(Linux 上的 /tmp 和 /dev/shm;macOS 上的 /private/tmp 和 /private/var/folders):可写加可执行是一条二进制投放路径,而两个后端都无法从执行授权中减去写入授权
--allow-socket <PATH>⚠️ 危险。允许一个 Unix 域套接字路径,例如自定义 LSP 守护进程或数据库套接字。可重复。另一端的任何东西都在沙箱之外运行,因此将其指向 docker.sock 或 agent 套接字等同于 --allow-docker,唯一的防护是拒绝 --deny-path 重叠。在 Linux 上,内核 7.1 以下它不起作用,因为在 ABI v9 之前 unix 套接字连接不受 Landlock 管控(见 Linux 限制)
--deny-path <PATH>阻止一个原本会被允许的路径。拒绝始终优先。可重复
--allow-port <PORT>允许在额外端口上的出站流量。默认仅 443。可重复。在 macOS 上规则是 (remote ip "*:PORT"),它与协议族无关,因此同时承载 UDP 和 TCP;Landlock 仅管控 TCP 连接。在 proxy.forced 下,该端口完全不打开直接套接字——它可通过代理访问,因此代理感知工具仍可工作
--allow-localhost <PORT>允许出站到 localhost 的某一个端口。Localhost 默认被阻止。用于 MCP 服务器或开发服务器。可重复
--allow-localhost-any允许出站到 localhost 的所有端口。构建工具如 Turbopack(Next.js)和 Vite 需要它,它们使用随机临时端口进行 IPC

环境变量

cplt 默认对子环境进行净化。只有安全的变量会通过,云凭证、数据库 URL 和包令牌会被剥离。它还会注入加固变量,阻止 npm/yarn/pnpm 生命周期脚本(postinstall 钩子,头号供应链攻击途径),禁用 git 提交和标签签名(因为 ~/.ssh 和 ~/.gnupg 在沙箱内不可达),并选择退出开发者工具遥测(DO_NOT_TRACK=1、NEXT_TELEMETRY_DISABLED=1、TURBO_TELEMETRY_DISABLED=1、CHECKPOINT_DISABLE=1 等)。

会通过的变量:

类别示例方式
核心系统HOME、USER、PATH、SHELL、TMPDIR、LANG显式允许列表
终端TERM、COLORTERM、TERM_PROGRAM显式允许列表
编辑器EDITOR、VISUAL、PAGER显式允许列表
认证令牌GH_TOKEN、GITHUB_TOKEN、COPILOT_GITHUB_TOKEN仅在你已设置它们时传递。gh 守卫改用一次性文件
Copilot 配置COPILOT_DEBUG、COPILOT_*前缀允许列表
语言运行时NODE_*、GOPATH、CARGO_HOME、JAVA_HOME、VIRTUAL_ENV、PYTHONPATH显式允许列表
工具管理器NVM_*、FNM_*、PYENV_*、MISE_*、SDKMAN_*、COREPACK_*、YARN_*前缀允许列表
OpenTelemetryOTEL_EXPORTER_OTLP_ENDPOINT、OTEL_SERVICE_NAME、OTEL_RESOURCE_ATTRIBUTES、OTEL_*前缀允许列表(OTEL_EXPORTER_OTLP_HEADERS 可能携带选择性加入的认证)
XDG 目录XDG_CONFIG_HOME、XDG_DATA_HOME、XDG_STATE_HOME、XDG_CACHE_HOME显式允许列表

带密钥后缀保护的前缀允许列表。 匹配允许前缀(如 COPILOT_* 或 YARN_*)的变量,如果以携带密钥的后缀结尾,仍会被丢弃:_TOKEN、_AUTH、_SECRET、_SECRET_KEY、_KEY、_PASSWORD 或 _CREDENTIALS。因此 COPILOT_DEBUG 会通过,而 COPILOT_API_KEY 不会。

始终阻止: AWS_*、AZURE_*、NPM_TOKEN、DATABASE_URL、VAULT_TOKEN、SSH_AUTH_SOCK、Docker 变量、CI 令牌,以及任何不在允许列表中的内容。

标志作用
--pass-env <VAR>将一个环境变量传递给 agent。可重复
--inherit-env⚠️ 危险。继承完整的父环境。仅剥离 NO_COLOR、FORCE_COLOR、SSH_AUTH_SOCK、SSH_AGENT_PID。仅用于调试

沙箱开关

标志作用
--allow-lifecycle-scripts允许 npm/yarn/pnpm 生命周期脚本(postinstall 钩子)运行。默认被阻止。当 npm install 需要它们时使用
--allow-gpg-signing允许在沙箱内进行 GPG 提交和标签签名。授予对公钥环和 GPG agent 套接字的只读访问。私钥仍被拒绝。见 GPG 签名
--allow-jvm-attach允许 /tmp 中的 JVM Attach API unix 套接字。MockK 内联模拟、Mockito 内联 agent、ByteBuddy 需要它。见 JVM Attach API
--allow-msbuild允许 /tmp 中的 MSBuild 工作节点 unix 套接字。dotnet build 需要它。不会启用持久化 MSBuild Server。见 MSBuild 工作节点 IPC
--no-scratch-dir禁用每会话 scratch 目录,该目录默认开启。TMPDIR 将不会被重定向
--scratch-dir显式启用每会话 scratch 目录。已是默认值,因此用于覆盖配置中的 scratch_dir = false
--brief🧪 实验性。将面向 agent 的沙箱简报写入 scratch 目录(CPLT_BRIEF.md)。默认关闭。也可在配置中设置 sandbox.brief = true。不稳定,因此可能在未来的版本中更改或移除
--no-brief本次运行关闭沙箱简报,覆盖配置中的 sandbox.brief = true。同时抑制 AGENTS.md 块,该块以简报为条件
--agents-md🧪 实验性。与 --brief 一起使用时,还将托管的 cplt 块写入项目的 AGENTS.md。默认关闭。也可在配置中设置 sandbox.agents_md = true。没有 --brief 时无效。不稳定,因此可能在未来的版本中更改或移除
--no-agents-md本次运行关闭 AGENTS.md 块,覆盖配置中的 sandbox.agents_md = true。不影响 scratch 目录简报
--allow-tmp-exec⚠️ 危险。允许从系统临时目录(/private/tmp、/private/var/folders)执行。优先使用 scratch 目录
--allow-cache-exec <SUBDIR>允许从一个 执行。可重复。用于在那里缓存已编译二进制文件的工具,如 Playwright 和 pnpm dlx

支持的运行时

cplt 自动发现已安装的工具并写入匹配的沙箱规则。通常只有磁盘上存在的目录才会获得规则,因此没有幻影路径。在 macOS 上,可写的应用目录在发现时即使尚不存在也会被包含,因此它们可以在首次使用时被创建。Linux 无法允许对不存在路径的写入,因此在那里创建必须在沙箱之外进行。

运行时主目录环境变量 / 前缀发现
Node.js.nvm、.local/share/fnm、.local/binNODE_*、NPM_*、NVM_*、FNM_*node
Rust.cargo、.rustupCARGO_HOME、RUSTUP_HOMEcargo
Gogo/bin、go/pkgGOPATH、GOROOT、GOCACHE 等go
Java/Kotlin (JVM).sdkman、.jenv、.gradle、.m2JAVA_HOME、JAVA_TOOL_OPTIONS、GRADLE_*、MAVEN_*、SDKMAN_*、JENV_*java、gradle
Kotlin Native.konan无无
Python.pyenvVIRTUAL_ENV、PYTHONPATH、PYENV_ROOT、PYENV_*python3
Yarn Berry.yarnYARN_*(加固覆盖 YARN_ENABLE_SCRIPTS)yarn
pnpmLibrary/pnpm、.local/share/pnpmPNPM_HOMEpnpm
Corepack无COREPACK_*无
mise.local/share/mise、.mise

运行 cplt doctor 查看 cplt 是否能在你的机器上为你的 agent 工作,运行 cplt doctor --verbose 查看它在你的机器上检测到的一切。

调试

标志作用
--doctor已弃用。 改用 cplt doctor 子命令
--print-profile打印生成的沙箱配置文件(SBPL)并退出
--show-denials实时流式输出 macOS 沙箱拒绝日志
--no-validate跳过验证沙箱限制已激活的启动检查
-y, --yes跳过交互式确认提示。配置摘要仍会打印,以便审计。当 stdin 不是 TTY 时必需,因此 CI 和脚本需要它
-q, --quiet抑制启动横幅和非必要消息。错误和警告仍会打印。也可在配置中设置 sandbox.quiet = true
--no-quiet覆盖 sandbox.quiet = true 并仍然显示启动摘要
--no-audit跳过会话后变更报告。cplt 通常将工作树与运行前固定的基线提交进行差异比较,并列出会话触及的内容,标记敏感路径。-q 也会抑制它
--init-config在 ~/.config/cplt/config.toml 创建起始配置文件并退出

会话标志

这些会转换为 agent 自己的会话标志,因此你不需要 -- 分隔符。

标志作用
--resume[=SESSION]恢复之前的会话。裸 --resume 交互式选择,--resume=NAME 按名称或 ID 选择
--continue恢复当前目录中最近的会话
--remote启用远程控制,以便你可以从 GitHub.com 或移动端监控和引导会话
--name SESSION为会话命名,以便 --resume=NAME 稍后能找到它

--continue 和 --resume 也为 OpenCode、Antigravity 和 Claude Code 做了映射:

cplt 标志CopilotOpenCodeAntigravity (agy)Claude Code
--continue--continue--continue--continue--continue
--resume--resume--continue¹--continue¹--resume
--resume=ID--resume=ID--session ID--conversation ID--resume ID
--remote--remote忽略忽略忽略
--name NAME--name NAME忽略忽略忽略

¹ OpenCode 和 Antigravity 都没有交互式会话选择器,因此裸 --resume 意味着“继续上一个会话”。Claude Code 有一个,因此它直接映射。

--remote 和 --name 仅限 Copilot。Pi 和 shell 模式完全不做转换,因此所有四个标志对它们都会被丢弃。自动恢复是一个单独的机制:当你调用 cplt 且没有透传参数也没有会话标志时,它会为你追加 --resume,且仅适用于 Copilot。

将它们与沙箱标志和 -- 透传参数结合使用:```bash cplt --resume=my-task # resume by name cplt --remote --name my-task -- -p "fix tests" # remote + named + prompt

root@kitploit:~
### Agents

使用 `--agent <name>` 选择一个,或通过 `cplt config set sandbox.agent <name>` 将其设为默认值。当你未指定时,会按顺序从 `PATH` 中自动检测 Copilot、OpenCode 和 Antigravity。

| Agent | `--agent` 值 | 自动检测 | 认证 |
| --- | --- | --- | --- |
| GitHub Copilot CLI | `copilot` | 是,优先级 1 | GitHub token,来自 Keychain 或 `gh` |
| [OpenCode](https://opencode.ai/) | `opencode` | 是,优先级 2 | 通过 `/connect` 使用 Copilot 订阅,或 `--pass-env ANTHROPIC_API_KEY` |
| [Antigravity CLI](https://github.com/google-antigravity/antigravity-cli) | `antigravity`,别名 `agy` 和 `agi` | 是,优先级 3 | 浏览器中的 Google OAuth |
| [Pi](https://github.com/earendil-works/pi) | `pi` | 否 | `--pass-env ANTHROPIC_API_KEY` 等 |
| [Claude Code](https://docs.anthropic.com/en/docs/claude-code) | `claude`,别名 `cc` 和 `claude-code` | 否 | `~/.claude` 或 Keychain 中的订阅 OAuth,`CLAUDE_CODE_OAUTH_TOKEN`(会放弃 Keychain 授权),或 `--pass-env ANTHROPIC_API_KEY` |
| [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) | `dsh`,别名 `deepseek` 和 `deepseek-harness` | 否 | `--pass-env DEEPSEEK_API_KEY`,或 `$DSH_HOME/.env`(`~/.dsh/.env`) |
| 你的 shell | `shell` | 否 | 无 |

- **Pi、Claude Code、goose 和 DeepSeek Harness 永远不会被自动检测。** `pi` 和 `dsh` 是通用二进制名称,可能会与你机器上的其他东西冲突,而 Claude Code 必须有意选择。
- **第三方 API 密钥是选择性启用的。** `ANTHROPIC_API_KEY`、`OPENAI_API_KEY`、`GEMINI_API_KEY`、`OPENROUTER_API_KEY`、`ANTHROPIC_AUTH_TOKEN`、`CLAUDE_CODE_OAUTH_TOKEN` 以及 Bedrock/Vertex 路由变量(`CLAUDE_CODE_USE_BEDROCK`、`AWS_BEARER_TOKEN_BEDROCK`、`CLAUDE_CODE_USE_VERTEX`、`ANTHROPIC_VERTEX_PROJECT_ID`、`GOOGLE_CLOUD_PROJECT`)永远不会传递,除非你用 `--pass-env` 指定它们。
- **订阅认证不需要环境变量。** OpenCode 的 `/connect` 设备流程将其 token 存储在 `~/.local/share/opencode/auth.json`,而 Claude Code 的 OAuth token 位于 `~/.claude`(Linux 上为 `.credentials.json`)或 macOS Keychain 中。两者都可以在沙箱内访问,因此 cplt 不会因为缺少 API 密钥而对两者发出警告。
- **OAuth 浏览器流程在出现登录提示时需要 `--allow-browser`。** 这涵盖 Antigravity;这里的其他所有 agent 都使用设备流程,会打印一个代码和一个 URL,不需要浏览器。该标志允许 agent 在沙箱外启动任何应用程序,并且无法缩小到 URL 范围,因此请在登录时开启它,登录后关闭——参见[标志表](#sandbox-toggles)和 [docs/security.md](https://github.com/navikt/cplt/blob/main/docs/security.md#--allow-browser-is-a-sandbox-escape-and-cannot-be-scoped)。
- **Claude Code 自动更新已禁用**,通过 `DISABLE_AUTOUPDATER=1`。Claude Code 没有 `--no-auto-update` 标志,在沙箱内自我更新是一种持久化途径,而且无论如何它都会因只读安装路径而失败。
- **`CLAUDE_CONFIG_DIR` 会被遵循。** 当它被设置时,cplt 会授予该目录而不是 `~/.claude`,并传递该变量,因此重新定位的配置根目录可以继续工作。
- OpenCode 是[官方支持的 Copilot 客户端](https://github.blog/changelog/2026-01-16-github-copilot-now-supports-opencode/),因此你现有的 Copilot 订阅可以在 OpenCode 内通过 `/connect` 使用。

每个 agent 的配置目录、Keychain 使用、执行权限和环境隔离详见 [SECURITY.md](https://github.com/navikt/cplt/blob/main/SECURITY.md#supported-agents)。

### goose 支持

cplt 可以沙箱化 [goose](https://github.com/aaif-goose/goose),这是一个开源 AI agent(二进制文件 `goose`)。已针对 goose 1.48.0 验证。```bash
# Run goose (must be explicit — not auto-detected)
cplt --agent goose

# goose is provider-agnostic — pass your provider's API key
cplt --agent goose --pass-env ANTHROPIC_API_KEY
cplt --agent goose --pass-env OPENAI_API_KEY

# Skip the keyring entirely: keep the key in the environment
GOOSE_DISABLE_KEYRING=1 cplt --agent goose --pass-env OPENAI_API_KEY --pass-env GOOSE_DISABLE_KEYRING

# Set goose as your default agent
cplt config set sandbox.agent goose

goose 的安全注意事项:

  • 不会自动检测:需通过 --agent goose 显式选择,或在配置中设置 sandbox.agent = "goose"
  • 与提供商无关:goose 将模型流量路由到用户配置的提供商(Anthropic、OpenAI、Google、Databricks、OpenRouter 等)。常见的提供商密钥(ANTHROPIC_API_KEY、OPENAI_API_KEY、AZURE_OPENAI_API_KEY、GOOGLE_API_KEY、DATABRICKS_HOST/DATABRICKS_TOKEN、GROQ_API_KEY、OPENROUTER_API_KEY、XAI_API_KEY、AWS_BEARER_TOKEN_BEDROCK)会被识别为认证提示,必须通过 --pass-env 传递。goose 读取的是 GOOGLE_API_KEY,而不是 GEMINI_API_KEY。此子集之外的任何提供商仍然可用:用 --pass-env 指定其变量名即可
  • 没有默认域名:在一次 --observe-domains 捕获中,goose 没有联系任何自己的主机,因此其内置允许列表仅为共享的包注册表基础域名。在启用 --default-allowlist 之前,请通过 allowed_domains 添加你的提供商的域名
  • 钥匙串被授予权限,但你可以避免它:goose 默认将提供商密钥存储在 macOS 登录钥匙串中,因此 cplt 会授予其权限——但该授权范围比 goose 自身的条目更宽(#242)。GOOSE_DISABLE_KEYRING=1 会让 goose 改用其配置目录中的 secrets.yaml,而通过 --pass-env 传递密钥则完全避免存储密钥。在 Linux 上,goose 使用 D-Bus Secret Service,钥匙串授权不会影响它
  • 配置目录为只读:~/.config/goose/config.yaml 声明了 extensions: 条目,goose 会在每次会话启动时生成其 cmd,因此可写的配置目录是一个主机持久化向量。正常会话不会写入它;/mode 更改和持久化的工具权限不会在沙箱化运行中保留。请在 cplt 之外使用 goose configure 重新配置
  • goose 的数据目录(~/.local/share/goose/)和状态目录(~/.local/state/goose/)可写,但拒绝执行。goose 在 macOS 上也使用这些 XDG 路径,并遵循那里的 XDG_* 覆盖
  • --continue 和裸 --resume 映射到 goose session --resume;--resume=ID 映射到 goose session --resume --session-id ID;--name X 映射到 goose session --name X。这些是子命令标志,因此 cplt 会随它们注入 session 子命令。--remote 被忽略(goose 没有对应项)

DeepSeek Harness 支持

cplt 可以对 DeepSeek Harness(二进制文件 dsh)进行沙箱化,这是 DeepSeek 推出的面向插件的代理 harness。上游将其作为开发者预览版发布,其自身的 SAFETY.md 表示不要将其控制措施作为唯一边界来依赖,而这正是 cplt 存在的意义所在。```bash

Run DSH (must be explicit — not auto-detected)

cplt --agent dsh

Pass the API key, or keep it in $DSH_HOME/.env

cplt --agent dsh --pass-env DEEPSEEK_API_KEY

Set DSH as your default agent

cplt config set sandbox.agent dsh

root@kitploit:~
**DSH 的安全注意事项:**
- **不会自动检测**:使用 `--agent dsh`(别名 `deepseek`、`deepseek-harness`)选择它,或设置 `sandbox.agent = "dsh"`。`dsh` 是一个简短、通用的命令名称,可能与你机器上的其他东西重名
- **在 cplt 内部关闭 DSH 自带的沙箱**:DSH 会将每次 shell 和文件工具调用包裹在它自己的进程沙箱中——macOS 上是 Seatbelt,Linux 上是 bwrap 或 Landlock。两者都无法嵌套在 cplt 内部。macOS 不支持嵌套的 `sandbox-exec` 调用(正是这一限制导致 cplt 关闭 Gradle 的内部沙箱,参见[限制](#limitations)),而 bwrap 使用 `unshare` 构建其命名空间,cplt 的 seccomp 过滤器会拒绝该调用。无论哪种方式,cplt 都是实际的强制边界,因此对于沙箱会话,请选择 DSH 自带的 `danger-full-access` 权限预设。如果保留内部运行器开启,工具调用会以沙箱运行器错误失败,而不是任务错误
- **单一 home 根目录,且 cplt 遵循覆盖设置**:DSH 将会话、设置、缓存和配置文件保存在 `$DSH_HOME` 下(默认为 `~/.dsh`)。`DSH_HOME` 在环境变量允许列表中,因此子进程解析出的根目录与 cplt 授予的相同。指向系统根目录或你的主目录的值会在启动前被拒绝,与 `CLAUDE_CONFIG_DIR` 经历相同的否决
- **主机持久化防护**:`$DSH_HOME/cordis.patch.yml`——Loader 在启动时读取的 home 级覆盖文件——被禁止写入。`$DSH_HOME/profiles/` 保持可写,因为 DSH 在每次启动时都会重写每个 profile 的 `cordis.yml` include-root,因此每个 profile 的 `cordis.patch.yml` 和已安装的插件是已记录的残留风险——请在 cplt 之外进行 profile 和 `dsh plugin` 编辑,并始终通过 cplt 启动 `dsh`,这样即使被植入任何东西也仍然在沙箱中运行
- **默认域名**:仅 `deepseek.com`。自带的 `dsh-llm-deepseek` 适配器默认指向 `https://api.deepseek.com`。将 `DEEPSEEK_BASE_URL` 指向网关后,你必须通过 `allowed_domains` 添加该网关的域名
- **认证**:使用 `--pass-env DEEPSEEK_API_KEY` 传递密钥,或将其保存在 `$DSH_HOME/.env` 中。通过 DSH 自己的模型 UI 保存的密钥会落在 `$DSH_HOME/.credentials.yaml` 中,位于同一个可写根目录内。macOS Keychain 被拒绝,因此通过 HTTPS 的 `git push` 需要 `gh` 在 `hosts.yml` 中的令牌或 `--pass-env GH_TOKEN`

### Shell 模式

运行一个普通的沙箱化 shell,没有 AI 代理,但具有相同的限制。便于测试构建工具、调试沙箱问题,或者只是手动谨慎操作。```bash
# Interactive sandboxed shell (uses $SHELL: fish, zsh, bash)
cplt --agent shell

# Inspect what's allowed without entering the shell
cplt --agent shell --print-profile

同样的默认拒绝规则适用:文件系统隔离、网络限制、环境变量清理。Shell 配置目录(fish 变量和历史记录、zsh 历史记录)保持可写。

对于单条命令,cplt exec 比 cplt --agent shell -- -c 'cmd' 更简洁。

Exec 模式

在沙箱内运行任意命令,无需启动 agent。没有启动横幅,没有确认提示,因此适合脚本、管道和 shell 别名。```bash

Sandbox a single command

cplt exec -- npm install cplt exec -- make build cplt exec -- go test ./...

Compound commands via $SHELL -c

cplt exec -c "npm install && npm test"

Pass sandbox flags as usual

cplt exec --allow-lifecycle-scripts -- npm install cplt exec --project-dir /path/to/repo -- make build cplt exec --with-proxy -- curl https://example.com

Shell aliases for sandboxed tools

alias npm="cplt exec -- npm" alias node="cplt exec -- node" alias python="cplt exec -- python"

root@kitploit:~
每个顶层 `cplt` 标志均适用:`--project-dir`、`--allow-read`、`--deny-path`、`--with-proxy`、`--pass-env` 等。添加 `--no-quiet` 可在命令运行前查看完整的沙箱配置摘要。

### 示例```bash
# The common case: Copilot in the sandbox
cplt -- -p "fix the tests"

# Sessions
cplt --resume                                   # pick one interactively
cplt --resume=my-refactor                       # by name
cplt --continue                                 # most recent in this directory
cplt --remote --name my-task -- -p "fix tests"  # named remote session

# Check the environment before the first run
cplt doctor

# Let Copilot read a shared library directory
cplt --allow-read ~/shared-libs -- -p "use shared-libs"

# Block a path you don't want Copilot to see
cplt --deny-path ~/.config/gh -- -p "refactor auth"

# Extra outbound port, e.g. an external API
cplt --allow-port 8443 -- -p "test the API"

# Localhost for MCP servers or dev servers
cplt --allow-localhost 3000 --allow-localhost 8080 -- -p "use the MCP server"

# All of localhost, needed by Next.js/Turbopack and Vite builds
cplt --allow-localhost-any -- -p "fix the build"

# Pass specific env vars through
cplt --pass-env MY_CUSTOM_VAR --pass-env ANOTHER_VAR -- -p "run with custom config"

# Inherit the full environment (dangerous, debugging only)
cplt --inherit-env -- -p "debug the build"

# Network
cplt --no-proxy -- -p "fix the tests"                    # proxy is on by default
cplt --blocked-domains ./blocked-domains.txt -- -p "refactor"
cplt --allow-private-domain intern.nav.no -- -p "use mcp-onboarding"

# Non-interactive / CI (skip the confirmation prompt)
cplt --yes -- -p "fix the tests"

# Inspect and debug the sandbox itself
cplt --print-profile
cplt --show-denials -- -p "fix the tests"

配置

配置分为两个层级:全局配置,用于开发者偏好设置;以及按仓库配置,用于团队策略。```bash

Browse and change settings interactively

cplt settings

Set global preferences

cplt config set sandbox.quiet true cplt config set proxy.blocked_domains "~/.config/cplt/blocked-domains.txt" cplt config set git_guard.mode warn # observe pushes instead of blocking them cplt config set gh_guard.enabled false # opt out of the gh guard entirely

Set per-repo policy (committed to .cplt.toml)

cplt config set --repo sandbox.allow_jvm_attach true cplt config set --repo deny.paths "~/secrets"

Inspect

cplt config show # effective config (file + defaults) cplt config explain # every key with its description

root@kitploit:~
`cplt settings` 是交互式编辑器,提供 Effective、Global 和 Repository 视图、搜索、暂存更改,并在保存任何安全敏感内容前进行明确确认。`cplt config` 仍是面向脚本和 CI 的稳定非交互式接口。仓库提案仍通过 `cplt trust` 单独提交和批准。编辑器从不提交或自动批准它们。

优先级依次为 CLI 标志、位于 `~/.config/cplt/config.toml` 的全局配置文件,然后是内置默认值。`.cplt.toml` 中的按仓库配置是一个独立的层,而不是该阶梯上的一级:`[deny]` 无条件收紧,而已批准的权限仅具有累加性,因此仓库可以启用某项功能,但永远无法关闭由 CLI 标志或全局配置设定的内容。

仓库根目录中的 `.cplt.toml` 承载团队策略:```toml
[deny]                    # Applied automatically, no opt-in needed
paths = ["~/secrets", "~/.vault-token"]
env = ["VAULT_TOKEN", "DATABASE_URL"]

[propose]                 # Requires developer approval (cplt trust accept)
gh_guard = true
git_push_prevention = true
allow_jvm_attach = true
allow_docker = true

[propose.allow]
ports = [5432]
localhost = [3000]
socket = ["/var/run/docker.sock"]

cplt 从 git HEAD 读取它,因此 agent 无法在会话中途篡改自己的策略,并且信任批准被固定到文件的内容上。未提交的 .cplt.toml 不会授予任何权限,直到它被提交,不过它的 [deny] 键仍然生效。在 CI 和脚本中,没有人能回答提示,--accept-repo-config 会在那一次运行中批准已提交文件的提案,而不会持久化任何信任。cplt init 通过检测项目的工具链为你写入一个:```bash cplt init # preview detected permissions cplt init --write # write .cplt.toml to disk cplt init --quiet # output only TOML (pipe-friendly) cplt init --global # generate a personal ~/.config/cplt/config.toml

root@kitploit:~
它了解 JVM(Gradle/Maven)、Node.js、Docker、Python、Rust、Go、Playwright、Spring Boot、Ktor、TestContainers、Next.js、Vite、Flyway、Cypress,以及来自 `.env.example` 的环境密钥。危险权限会从生成器中输出,并附带风险警告。`--global` 则关注机器级别的项目:Playwright 浏览器、GPG 签名、注册表凭据、替代代理。

有些键仅限全局使用,并且会从 `.cplt.toml` 中被拒绝,因为它们与特定机器相关或属于本地偏好:`sandbox.agent`、`sandbox.quiet`、`sandbox.yes`、`sandbox.validate`、`sandbox.scratch_dir`、`sandbox.pass_env`、`sandbox.inherit_env`、`sandbox.allow_cache_exec`、`sandbox.allow_cache_exec_any`、`proxy.enabled`、`proxy.port`、`proxy.log_file`、`proxy.log_level`、`proxy.blocked_domains`、`proxy.allowed_domains`,以及所有 `[gh_guard]` 和 `[git_guard]` 键。

完整细节,包括信任模型、路径展开规则以及完整的配置文件参考:[docs/configuration.md](https://github.com/navikt/cplt/blob/main/docs/configuration.md)。

## 架构```
┌──────────────────────────────────┐
│  cplt (Rust binary)              │
│  ┌───────────┐  ┌─────────────┐  │
│  │ Policy    │  │ CONNECT     │  │
│  │ Generator │  │ Proxy       │  │
│  └─────┬─────┘  │ (optional)  │  │
│        │        └─────────────┘  │
│        ▼                         │
│  ┌─────────────┬────────────┐    │
│  │   macOS     │   Linux    │    │
│  │  Seatbelt   │  Landlock  │    │
│  │  sandbox-   │  + seccomp │    │
│  │  exec       │  pre_exec  │    │
│  └─────────────┴────────────┘    │
│        │                         │
│        ▼                         │
│  copilot (sandboxed)             │
│  ├── All child processes         │
│  ├── Cannot read ~/.ssh          │
│  ├── Network port-restricted     │
│  ├── SSH agent blocked           │
│  └── Filesystem = primary ctrl   │
└──────────────────────────────────┘

安全模型是一个默认拒绝的文件系统,并带有内核强制执行。在 macOS 上,以及在 Linux 内核 6.7+(Landlock ABI v4)上,网络默认限制为端口 443,可通过 --allow-port 添加额外端口。在较旧的 Linux 内核上,CONNECT 代理提供该限制,这就是它默认启用的原因。SSH agent 访问和 localhost 出站流量在 macOS 上被内核阻止。在 Linux 上两者都不会被阻止:基于端口的 Landlock 规则无法区分 localhost 和远程主机,并且在内核 7.1 以下,unix socket 的 connect() 不受 Landlock 管控,因此除了 bubblewrap 屏蔽的套接字之外,被扣留的 SSH_AUTH_SOCK 是 agent 与你已加载密钥之间的唯一屏障。配置文件生成器会探测你的环境(cplt doctor --verbose 显示相同的探测结果),并且只为磁盘上实际存在的工具目录生成规则。规则更少,沙箱更严密。

  • macOS:生成 Seatbelt/SBPL 配置文件并交给 sandbox-exec
  • Linux:Landlock LSM 规则加上 seccomp-BPF 过滤器,通过 pre_exec 应用(内核 5.13+,TCP 端口过滤在内核 6.7+)

内部实现和模块布局:docs/architecture.md。威胁模型、防御层和诚实的缺口:SECURITY.md。

安全

单一二进制文件,最小依赖,无运行时服务,无遥测。三层防御,层与层之间边界清晰:

层执行方式可绕过?保护内容
1. 内核沙箱macOS Seatbelt / Linux Landlock+seccomp❌ 否文件访问、exec、网络端口
2. 网络代理CONNECT 代理,域名过滤❌ 否(在沙箱内)出站连接、数据外泄
3. 命令守卫基于 PATH 的包装脚本⚠️ 软屏障推送、合并、发布、API 写入

cplt 防护的内容:

  • 机密外泄(SSH 密钥、云凭证、.env 文件):被内核阻止
  • 从临时目录执行未授权代码:被内核阻止
  • 通过缓存目录中的二进制文件实现持久化:被内核阻止,因为这些目录被拒绝 exec
  • 通过项目中的 git hooks 实现持久化:在 macOS 上 .git/hooks 在内核层面被拒绝写入。在 Linux 上,使用 Landlock 且没有 Bubblewrap 时,它保持可写,而 cplt 自身的父进程侧 git 随后以 core.hooksPath=/dev/null 运行,因此它永远不会执行植入的 hook,不过你自己运行的 git 仍然会执行
  • 通过既可写又可执行的包管理器工具目录(mise shims、PNPM_HOME、~/.deno/bin、~/.bun/bin)实现持久化:这些目录被授予写权限,以便 pnpm add -g 之类的命令能在沙箱内工作,因此 agent 可以留下一个二进制文件,之后某个 shell 会从你的 PATH 中拾取它
  • 植入的二进制文件劫持 cplt 启动的 agent:在启动和审计时,cplt 从固定的系统目录而非 PATH 解析它自己运行的辅助程序(git、bwrap、sandbox-exec、mise,以及它从中读取 token 的 gh),但 agent 二进制文件本身从它被发现的位置运行,对于 npm 全局安装来说,这通常位于可写的 mise 或 node 目录树下。cplt 无法从固定目录解析它——它合法地存在于你的版本管理器放置它的位置——因此它会将解析出的路径与沙箱即将应用的写规则进行比对,并在启动时发出警告,指明该二进制文件和可写的目录树,然后继续执行
  • cplt doctor:它的 --version 探测会在父进程中运行它在你的 PATH 上找到的每个 agent 二进制文件,因此植入的二进制文件会在那里执行——与上述启动时相同的已发现路径暴露,这就是 doctor 是一份报告而非边界的原因。它的 gh 检查从受信任目录解析,而它的内核版本读取完全不启动任何进程
  • 向未授权域名的数据外泄:被代理阻止
  • 意外推送到 main 以及未经审查的 PR 合并:被守卫阻止

cplt 不防护的内容:

  • 项目目录内已有的恶意代码。agent 在那里拥有完整的读写权限
  • agent 引入的逻辑缺陷。你仍然需要审查代码
  • 绕过命令守卫的复杂对手。请使用服务端分支保护
  • 对允许域名的网络攻击。如果 github.com 被允许,agent 就可以在那里读写
  • 对将认证信息存储在那里的 agent 而言的 macOS Keychain 访问。内容受密码保护,并且 sandbox.keychain_substitute 可以在 agent 拥有其他凭证的情况下放弃该授权

我们的优先级,按顺序排列:正确(每个声明都经过测试,每个边缘情况都有 CVE 或研究参考)、透明(SECURITY.md 不隐藏任何内容)、简单(单一二进制文件,零配置要求,合理的默认值)以及实用(不挡路,让 agent 安全地工作)。

更多:docs/security.md · SECURITY.md

网络与代理

代理默认开启。来自 Copilot CLI、gh 和 curl 的所有出站流量都通过 HTTP_PROXY/HTTPS_PROXY 和 NODE_USE_ENV_PROXY=1 经过本地 CONNECT 代理。它监听操作系统分配的临时端口,因此不会发生冲突。你可以获得实时连接日志、域名阻止、域名允许列表、持久审计日志,以及沙箱强制执行的相同端口策略(443 加上 allow.ports 中的任何内容)。```bash cplt --proxy-forced -- -p "fix tests" # force all egress through the proxy cplt --no-proxy -- -p "fix tests" # disable for one run cplt --blocked-domains blocked-domains.txt -- -p "x" # block known-bad domains cplt --allowed-domains allowed-domains.txt -- -p "x" # allowlist mode cplt --default-allowlist -- -p "x" # fail-closed: only the agent's own domains cplt --observe-domains -- -p "x" # record what the agent contacts, block nothing cplt --proxy-upstream http://proxy.corp:8080 -- -p "x" # chain through a corporate proxy

root@kitploit:~
`--observe-domains-out <FILE>` 将观测到的域名集合写入文件,每行一个域名,而
`--proxy-upstream-no-proxy <HOST>` 列出应直接访问而非通过上游访问的主机。```bash
cplt config set proxy.enabled false
cplt config set proxy.blocked_domains "~/.config/cplt/blocked-domains.txt"
cplt config set proxy.allowed_domains "~/.config/cplt/allowed-domains.txt"
cplt config set proxy.log_file "~/.config/cplt/proxy.log"

代理强制模式是选择性启用的。它将内核出口限制到代理端口,因此直接打开的套接字,或 env -u HTTPS_PROXY,都无法绕过。在 macOS 上强制执行是完整的,会固定到 localhost:<proxy_port>。在 Linux 上它会阻止直接的 TCP :443,并且一条 seccomp 规则仅允许 AF_INET/AF_INET6 下协议为 0 或 IPPROTO_TCP 的 SOCK_STREAM,因此 UDP、raw、SCTP 和 DCCP 也被关闭——代价是任何打开此类套接字的操作都会受影响,而不仅仅是发送 UDP 的代码。剩下的就是基于端口的残留,evil.com:<proxy_port>,直到 #114。

在代理强制模式之外,Linux 不限制 UDP。Landlock 的网络权限在 ABI v10 之前仅支持 TCP,cplt 只处理 AccessNet::ConnectTcp,并且上述 seccomp 规则被有意不应用——在那里拒绝 SOCK_DGRAM 会破坏 getaddrinfo(3),从而破坏所有 DNS,影响每一个非代理工具。因此,在默认模式下,向任意主机发送出站 UDP、入站 UDP 绑定、DNS 隧道和 QUIC/HTTP-3 都不受管控,而 CONNECT 代理仅承载 TCP,所以这些都不会出现在代理日志中。macOS 在默认模式下限制 UDP,但也不路由它:remote ip "*:443" 覆盖 UDP,因此 443 上的 QUIC/HTTP-3 在那里同样会绕过代理离开。在 proxy.forced 下,代理日志是 macOS 上出口的完整记录。在 Linux 上,除了上述 evil.com:<proxy_port> 残留外,它也是完整的,该残留不经过代理,因此不会出现在其日志中。

两个列表的匹配方式相同:example.com 覆盖精确域名及所有子域名,匹配不区分大小写,并会去除末尾的点。阻止列表和允许列表文件每五秒重新读取一次,因此你可以实时编辑它们。本地主机流量通过 NO_PROXY 绕过代理,永远不会出现在审计日志中。--proxy-timeout <SECONDS> 限制请求和标头读取(默认 60),并且不会拆除已建立的 CONNECT 隧道,这些隧道可能空闲长达一小时。

每个代理标志、域名过滤细节、上游企业代理链式连接以及连接日志格式:docs/proxy.md。

命令防护

启用它们后,cplt 会通过 $PATH 中的包装脚本拦截 gh 和 git:

命令操作
gh pr merge、gh repo delete、gh release create🔒 已阻止
git push origin main、git push --force🔒 已阻止
gh api(写入其他仓库)🔒 已检查范围
gh pr list、gh issue list、git commit✅ 允许
git push origin feature-branch✅ 在 protect_default_branch_only 下允许

这是第 3 层,一个软屏障。它阻止合规代理意外做出破坏性操作。对于硬边界,请依赖内核沙箱和服务端分支保护。

启用 gh 防护后,cplt 还会在启动时缓存 GitHub 令牌,并通过 gh auth token 回调提供一次,然后删除缓存。这减少了意外和基于环境的泄漏。它不是针对恶意代理的边界,因为缓存位于代理自己的 TMPDIR 中,在合法消费者之前读取它的代理仍然能获得令牌。SECURITY.md 中有关于 block_auth_token 的完整声明。

完整行为:docs/gh-guard.md · docs/git-guard.md

已知影响

沙箱会有意阻止某些工作流。常见情况及其修复方法:

影响修复
.env 文件被阻止cplt config set sandbox.allow_env_files true
npm postinstall 钩子被阻止cplt config set sandbox.allow_lifecycle_scripts true
go test / mise run 被阻止(临时执行)临时目录默认开启。如果仍需要,cplt config set sandbox.allow_tmp_exec true
本地主机连接被阻止cplt config set allow.localhost 3000,或 cplt config set sandbox.allow_localhost_any true
Docker 被阻止cplt config set sandbox.allow_docker true ⚠️
SSH 被阻止改用 HTTPS 远程
GPG 签名被禁用cplt config set sandbox.allow_gpg_signing true
JVM MockK/Mockito 失败cplt config set sandbox.allow_jvm_attach true
dotnet build MSBuild 工作节点被阻止cplt config set sandbox.allow_msbuild true
私有注册表凭据被阻止cplt config set allow.read "~/.m2/settings.xml"
内部 Maven/Nexus 仓库不可达(Gradle/Maven)cplt config set proxy.allow_private_domains "intern.example.com"。IP 字面量仓库 URL 无法被允许——请为主机提供 DNS 名称;见下文
Playwright Chromium 无法启动允许缓存执行,然后禁用 Chromium 的嵌套沙箱;见下文

Playwright Chromium 需要 cplt config set sandbox.allow_cache_exec ms-playwright, 并且 Chromium 必须在不使用其自身嵌套沙箱的情况下运行。在 macOS 上,其辅助进程无法在 cplt 内初始化第二个 Seatbelt 沙箱(forbidden-sandbox-reinit);在 Linux 上,cplt 的 seccomp 过滤器会阻止该沙箱所需的命名空间系统调用。作为库的 Playwright 已经以 --no-sandbox 启动,而同一个选择性启用会为 Playwright MCP 设置 PLAYWRIGHT_MCP_SANDBOX=false,否则它会重新开启。任何其他 Chromium 启动器都需要自己加上 --no-sandbox。cplt 仍然是强制执行的内核边界,但被攻破的渲染器随后会获得完整的 cplt Playwright 配置文件,而不是 Chromium 更窄的子配置文件。参见 Cache exec 和 SECURITY.md。

Git 提交对每个代理都有效;git push 是否能通过 HTTPS 工作取决于代理。 三个前提条件:使用 HTTPS 远程而不是 SSH(git remote set-url origin https://github.com/org/repo.git,或用 git config --global url."https://github.com/".insteadOf "[email protected]:" 全局重写),在沙箱外运行一次 gh auth login,如果凭据助手尚未配置,则运行 gh auth setup-git。然后推送会运行 gh auth git-credential,它需要一个 gh 能从沙箱内访问的令牌——这因代理而异,参见 Git workflow。默认情况下,git 防护会拒绝推送到默认分支以及所有强制推送;请推送功能分支。SSH 代理套接字被阻止,因为它会解锁所有已加载的密钥,并且可以向任何主机进行身份验证,而 gh 凭据助手仅限于 GitHub。

JVM 是代理感知的,因此现在需要允许位于私有 IP 上的内部 Maven 仓库。 cplt 会将 http(s).proxyHost/proxyPort 注入 JAVA_TOOL_OPTIONS,因此 Gradle 和 Maven 依赖解析会经过 CONNECT 代理,并出现在代理日志中,而不是绕过它。然后代理的 SSRF 防护会拒绝解析到私有地址空间的内部 Nexus 或 Artifactory,就像它对 curl、npm 和 pip 所做的那样。将其 DNS 名称添加到 proxy.allow_private_domains。以裸 IP 字面量(https://10.20.30.40/repository/maven-public/)编写的仓库 URL 无法通过任何键被允许——该检查在查询允许列表之前运行——因此此类仓库需要 DNS 名称。WorkerExecutor 插件 fork,以及在 cplt 外启动并在 cplt 内复用的 Gradle 守护进程,不会被代理。参见 Internal Maven/Gradle repositories on private IPs。

Gradle 9+ 运行自己的嵌套沙箱,cplt 会将其关闭。 自 Gradle 8.8 起,守护进程会将自身包装在 sandbox-exec 中(由 GRADLE_MACOS_SANDBOX 控制,之前是 org.gradle.daemon.sandbox 属性)。macOS 不支持嵌套的 sandbox-exec 调用,因此内部沙箱在套接字操作上会失败并显示 "Operation not permitted"。cplt 会注入 GRADLE_MACOS_SANDBOX=off,因为它已经提供了内核级沙箱。这是一个已知上游问题,会影响任何将 Gradle 包装在外部沙箱中的工具。如果你确实想要 Gradle 自己的沙箱,可以用 --pass-env GRADLE_MACOS_SANDBOX 覆盖。

Copilot CLI 1.0.83 运行自己的嵌套沙箱,cplt 会将其关闭。 在 Linux 上,该沙箱会构建网络命名空间——slirp4netns、iptables、/dev/net/tun——而 cplt 的 seccomp 过滤器会拒绝它所采用的 unshare。cplt 还会设置 HTTP_PROXY/HTTPS_PROXY,在 1.0.83 中,无论你是否要求,这都会将 Linux 沙箱置于代理出口路径上,因此两者在每次启动时都会冲突。症状:[cplt] Starting Copilot in sandbox... 然后没有任何输出。cplt 会注入 Copilot 自己的退出选项 COPILOT_CLI_SANDBOX_SUPPORT_OVERRIDE=unsupported;Copilot 会在该会话中退出,并说明这一点,同时保留你保存的 sandbox.enabled 不变。cplt 是边界,就像它对 Gradle 和 Chromium 一样。可以用 --pass-env COPILOT_CLI_SANDBOX_SUPPORT_OVERRIDE 覆盖。要求使用沙箱的企业托管策略会覆盖所有这些——参见 Copilot CLI's own command sandbox。

每个影响,包括各工具表格、JVM 和 Kotlin 守护进程说明、GPG 故障排除以及私有注册表平台差异:docs/known-impacts.md。

限制

macOS

  • sandbox-exec 已弃用。Apple 尚未移除它,但可能会在未来某个 macOS 版本中移除。
  • SBPL 没有基于域名的过滤。可选的 CONNECT 代理改为提供域名阻止。
  • SBPL 的 lsopen 也没有过滤器,因此 --allow-browser 要么是全部 Launch Services,要么完全没有。启用后,代理可以启动沙箱外的任何应用程序,任何包装器都无法缩小该范围——参见 docs/security.md。

Linux

  • 需要内核 5.13+,并启用 Landlock LSM。
  • TCP 端口过滤需要内核 6.7+。较旧的内核只能获得仅文件系统强制执行。
  • Landlock 无法拒绝允许路径内的子路径,因此项目目录内 .env 的读/写/删除不受内核强制执行。当 Bubblewrap 处于活动状态时,.git/hooks 写入会被阻止。
  • --deny-path 需要 Bubblewrap。当 bwrap 处于活动状态时,它通过挂载掩码强制执行。没有它时,Landlock 仅支持允许列表,cplt 会警告该拒绝操作,而不是应用它。

更多:docs/security.md

贡献

欢迎贡献。```bash git clone https://github.com/navikt/cplt.git && cd cplt git config core.hooksPath hack # enables pre-commit fmt + clippy checks mise run check # runs fmt, clippy, and tests

root@kitploit:~
在开始大规模更改之前,请先提交一个 issue。每个 PR 都必须通过 CI(fmt、clippy、测试)。

## 参考资料

- [SECURITY.md](https://github.com/navikt/cplt/blob/main/SECURITY.md),完整的安全模型、威胁分析、测试策略以及现有技术
- [Apple sandbox-exec(1)](https://keith.github.io/xcode-man-pages/sandbox-exec.1.html)
- [Chromium Seatbelt V2 设计](https://chromium.googlesource.com/chromium/src/sandbox/+show/refs/heads/main/mac/seatbelt_sandbox_design.md)
- [Landlock LSM 文档](https://docs.kernel.org/userspace-api/landlock.html)
- [seccomp-BPF 文档](https://www.kernel.org/doc/html/latest/userspace-api/seccomp_filter.html)
- [OWASP SSRF 防范速查表](https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html)
- [michaelneale/agent-seatbelt-sandbox](https://github.com/michaelneale/agent-seatbelt-sandbox)

## 许可证

[MIT](https://github.com/navikt/cplt/blob/main/LICENSE)
~/Library/Caches/<SUBDIR>
--allow-cache-exec-any⚠️ 危险。允许从整个 ~/Library/Caches 执行。优先使用 --allow-cache-exec <SUBDIR>
--allow-browser⚠️ 危险。开启此项后,agent 可以在沙箱之外启动你机器上的任何应用程序。 该授权是 Launch Services,而非浏览器:launchd 在 Seatbelt 配置文件之外启动目标,因此 open -a Terminal /tmp/x.sh 会在无沙箱下运行。这无法限定到 URL——SBPL 的 lsopen 不接受过滤器,而且该授权可通过 LSOpenCFURLRef() 访问,完全不需要 open 二进制文件,因此没有任何包装器能缩小它(#251,以及 docs/security.md)。仅在登录提示实际显示在屏幕上时(MCP 服务器 OAuth、重新认证)才开启它,然后将其关闭。默认关闭
--deny-clipboard通过拒绝 com.apple.pasteboard Mach 服务,阻止 agent 读取或写入 macOS 剪贴板(pbpaste/pbcopy)。其他所有 Mach 服务(Keychain、DNS、Security framework)不受影响。默认开启——此标志重述默认值
--allow-clipboard将 macOS 剪贴板还给 agent,cplt 默认拒绝它。等同于 sandbox.deny_clipboard = false
--use-bubblewrap仅 Linux。要求在 Landlock 和 seccomp 之上使用 bubblewrap 命名空间层(PID、mount、IPC、UTS、cgroup、用户命名空间以及私有 /tmp)。如果缺少 bwrap 则报错。两个标志都不给时自动检测
--no-bubblewrap仅 Linux。永不使用 bubblewrap,即使已安装。回退到 Landlock 和 seccomp。当 bwrap 破坏特定工具时使用它
MISE_*
mise