sanlee-ys/agent-ops

GitHub: sanlee-ys/agent-ops

一套围绕 agentic 编程 CLI 在含真实凭证的单机环境中安全运行的运营操作框架,包含安全防护钩子、事故复盘和工作协议。

Stars: 0 | Forks: 0

# agent-ops 关于将一个 agentic 编程 CLI 作为真正的队友,在一台真实的机器上运行,且附近存放着真实凭证的实战笔记——由一位工程师编写,专为单台机器设计,之所以公开发布,是因为这些故障模式不会一直保持静止。 ## 为什么会有这个项目 像 Claude Code 这样的 Agentic CLI 已经不再局限于编辑文件了。它们可以运行 shell 命令、读取任意配置、使用环境中的有效 token 调用 MCP server,并扩展为能够在几分钟内消耗真实资金的多 agent 工作流。这种组合——广泛的工具访问权限加上常驻凭证——构成了一个活生生的安全攻击面,而且相关文档少之又少。关于它的大多数描述要么是营销话术(“agent 在设计上是安全的”),要么是那些根本不会发布事后复盘的公司所进行的事后事件响应。 这个仓库不是上述任何一种。它是在一台机器上实际日常运行 Claude Code 的过程中成长起来的操作层:包含一种安全姿态、以机械方式执行部分姿态的 `PreToolUse` 防护机制、五篇以无指责格式编写且保留了失败细节的事件事后复盘——另外还有两篇在日志采用严格严重性标准后被有意降级为调试笔记的文章——以及五个可重用的 skills,还有姿态和事件都默认遵循的工作协议(如何界定工作范围,并行会话如何避免相互干扰)。 其中三篇是凭证泄露——其中一篇涵盖了同一个 GitHub PAT 的两次独立泄露——相当于一周内发生了四次泄露事件,其中三次是*同一个*凭证每次通过*不同的*工具或命令形式泄露,因为填补上一个漏洞的防护机制,其作用域仅限于有人想到要枚举的攻击面,而不是实际存在的攻击面。这种模式——一种仅凭其作者想象力决定完整性的机械控制——正是本仓库的核心主线,这也是为什么防护机制本身的源码在这里被公开而不是保密的原因。“通过攻击者不知道规则来实现安全”在攻击者已经拥有本地执行权限的机器上是站不住脚的;该防护机制的价值在于纵深防御,而不是保密,因此公开它没有任何损失,反而可能让下一个漏洞由读者发现,而不是通过一次泄露来发现。 ## 导航 - **`operating-model.md`** — 所有这一切运行所依赖的工作协议:DCB(Direction / Contracts / Bar,即方向/契约/标准)作为范围界定准则、会话预检、并行会话协议,以及作为路由轴的推理工作量(记录了其自身缺乏证据的事实)。已于 2026-08-01 压缩为作为正文起支撑作用的部分——每一个足够重要的实践要么在 `hooks/` 和 `conventions/` 中获得了机械化的兜底保障,要么就是走个过场;长篇内容保存在 git 历史记录中。 - **`security/`** - `posture.md` — 分层安全模型:权限允许列表设计、逃生舱口,以及处理涉及凭证命令(token 轮换、密钥生成)的常驻规则必须由人类直接执行,绝不通过工具调用。 - `credential-guard.py` — 已发布的 `PreToolUse` hook。阻止跨 Bash、PowerShell、Read 和内容模式 Grep 的大量环境变量转储和读取已知敏感文件(shell 配置、SSH 密钥、云 CLI 凭证存储、`.env` 文件)。它涵盖的内容通过其遗漏,也恰好映射了它未涵盖的内容——这在 `security/README.md` 和相关事件中进行了公开讨论。 - `README.md` — hook 是如何接入的,它涵盖和不涵盖的内容,以及针对它所阻止的合法读取的覆盖约定。 - **`hooks/git-staging-guard.py`** — 一个 `PreToolUse` hook,可阻止整树暂存(`git add -A|-u|.`, `git commit -a`),使得一个会话无法将*并行*会话中未提交的工作扫入一个不相关的 commit 中。有趣的限制在于它绝不能阻止文本内容:描述事件的 commit 消息和事后复盘逐字引用了这些 flag,因此它会剥离 heredoc 主体并进行 token化,而不是仅仅 grep 查找 flag。在 `tests/` 中进行了双向测试。 - **`hooks/published-history-guard.py`** — 一个 `PreToolUse` hook,当丢弃的范围包含远程仓库已经拥有的 commit 时,阻止在 `main` 上进行 force-push 或向后 `reset`,因此在一个直接推送到 main 的仓库中,一个会话无法抹除另一个会话已推送的工作。与上述防护不同,它是*有状态*的:判定该命令违规的事实(“该范围包含其他人已发布的 commit”)存在于仓库中,而不是命令字符串中,因此它会询问 git——并且询问 `ls-remote`,从不询问追踪引用,因为一个过时随后刷新的追踪引用正是在其背后的事件中击败 `--force-with-lease` 的原因。详见 [`decisions/ADR-007`](decisions/ADR-007-guard-the-invariant-not-the-verb.md) 中的推理。 - **`incidents/`** — 五篇无指责的事后复盘,设定了严格的标准:真实的暴露、真实的资金消耗,或活生生的控制失效。它们共享一个主线——摘要、影响、根本原因、实际应用的修复措施、经验教训——但格式服从于故障本身,而不是死板的模板。五篇中有四篇是坦诚讲述的同一个故事:同一个凭证攻击面每次通过不同的工具或命令形式泄露,每一个防护机制的范围都局限于其作者想象中的攻击面,而不是实际存在的攻击面。第五篇是一个没有成本上限的多 agent 扇出,在几分钟内耗尽了一个使用配额窗口。 - `2026-07-02-plaintext-api-key-exposure.md` - `2026-07-02-uncapped-premium-fanout.md` - `2026-07-03-github-pat-plaintext-recurrence.md` - `2026-07-03-credential-guard-interpreter-bypass.md` - `2026-07-04-github-pat-read-grep-leak.md` - **`debug-notes/`** — 两篇最初作为事件提交,但在 2026-08-01 日志达到上述标准后被降级的文章:控制台闪现排查(三个根本原因,一个错误诊断)以及被杀死的 `SessionEnd` hook 静默卡死了内存同步。值得保留,但不算事件——一个把所有烦人的 bug 都当成“事件”的日志,是一个毫无严重性可言的日志。这种降级本身就是一种姿态。 - **`conventions/`** — 摆脱了长篇大论而保留下来的规则:并行会话协议、分支卫生、链接验证,以及从阅读公开的 [pi](https://github.com/earendil-works/pi) agent 框架(MIT 许可证)中提炼出的一系列规则——包括双向失效的允许列表、将截断作为一种延迟而非损失、被截断的生产者污染其产生的所有内容、任何由 CI 触发的 agent 所需的四重检查授权门控、作为被执行而非被阅读的面向 agent 的契约,以及严格的仅限 LF 的 JSONL 帧。阅读别人的框架比通过事件驱动来学习成本更低,而且其中一条规则被归类为*独立趋同*:pi 的多会话 git 规则与本地一次险些发生的事故后在此处编写的规则相吻合,它们是分别独立推导出来的。 - **`reference/`** — 一个书架,明确表示不是约定规范:针对这个“舰队”目前尚未遇到的问题的现成设计(字符串替换编辑工具实际需要的匹配算法;让手写的终端 UI 不发生撕裂的两个原语)。归档于此是为了避免日后在时间压力下糟糕地重新推导这些设计,并加以标记,以免有人误把它们当作必须立刻去执行的任务。 - **`vendors/`** — 每个供应商的适配器层(随更名添加,见 [`decisions/ADR-008`](decisions/ADR-008-agent-ops-rename-and-vendor-layer.md)):根目录保持供应商中立的标准;任何属于特定框架格式或方言的内容都按每个供应商一个目录的方式存放。`vendors/claude/skills/` 包含了五个作为模式发布的自定义 skills(`dcb`、`descope-sweep`、`park`、`proglog`、`handoff`);`vendors/codex/` 记录了第二意见供应商的布线、agent 间通道和升级数据包;`vendors/cursor/` 记录了 IDE 通道(有界工作、UI 验证、并行的非冲突关注点——见 [`decisions/ADR-009`](decisions/ADR-009-cursor-ide-lane-in-fleet.md));`vendors/gemini/` 记录了 Google Antigravity (AGY),即经过评估的 Gemini 系列研究/溢出通道,包括其更窄的安全边界。控制平面的决策详见 [`decisions/ADR-010`](decisions/ADR-010-claude-led-four-vendor-orchestration.md)。适配器契约见 [`vendors/README.md`](vendors/README.md)。 - **`decisions/`** — 仓库自身的契约,如实版本化记录: - `ADR-001-public-claude-ops-repo.md` — 范围契约:什么内容会在这里发布,什么永远不会。 - `ADR-002-public-first-canonicality.md` — 对 ADR-001 同步模型的同日反转:本仓库是记录系统,采用公开优先的方式编写,因为“仔细脱敏”是一个行为规则,而本仓库的核心论点就是行为规则必须有机械化的兜底机制。 - `ADR-003-delegation-maturity.md` — 弥补自评 9/10 分中最后一点差距的计划,分三个阶段,每个阶段都用机械化的控制手段或测量的数字来代替长篇大论,外加一个在这些措施落地前刻意锁死的待办事项。通过其自身的测量保持诚实:草案中提出的 40% 的规则攻击面缩减被其要求的审计所证伪——真实数字约为 16%,因为它假设的重复内容基本上并不存在。 - `ADR-004-ref-explicit-git-in-shared-clones.md` — 提交到 `HEAD` 但推送命名 ref 的自动化操作假设两者是同一个对象。在由并行会话共享的克隆中,这个假设是别人的变量,当它以错误的方式失效时,压缩合并(squash-merge)会删除该 commit。明确指向该 ref,否则拒绝执行。 - `ADR-005-herdr-persistence-not-agent-awareness.md` — 一个经过试用并被采用的 agent 多路复用器。之所以保留,很大程度上是为了记录它曾因为一些它根本不具备的功能而差点被拒绝:“它为从未提交的输入报告成功”这一发现来自于一条日志记录与一个 composer 的关联,而这两半都是错的——调用是有效的,而文本是没有人输入过的暗色占位符。在撰写当天进行了两次更正。从单一关联观察中得出的缺陷只是一个假设;在基于它编写决策之前,请先重现它。 - `ADR-008-agent-ops-rename-and-vendor-layer.md` — 从初创时的单供应商名称更名的原因,为什么“llm-ops”和按供应商拆分仓库都被拒绝了,以及 `vendors/` 适配器契约。历史记录保留了它们撰写时所用的名称。 - `ADR-009-cursor-ide-lane-in-fleet.md` — Cursor (Composer) 作为 IDE 通道:有界工作、跨框架传输协议、已记录的防护盲区。其当时的 Gemini 和遥测后果由 ADR-010 进行修订。 - `ADR-010-claude-led-four-vendor-orchestration.md` — 一个控制平面,三个专业通道;框架与模型系列的独立性;可检查的转移;坦诚的防护边界;监视器只观察,从不路由。 - `ADR-006-claim-the-concern-before-working-it.md` — 两个会话在同一个下午、不同的机器上编写了相同的决策,而本该捕捉到这一点的进行中扫描如实返回了空结果:一个会话的工作是一个未追踪的文件,另一个是一个未推送的分支。解决办法不是在会话之间建立通道——双方都相信自己拥有这个关注点,无论他们沟通得多好,在这一点上达成一致的同行总会发生冲突。在做这项工作之前先推送分支,这样声明就会存在于其他机器能够看到的地方。 - **`scripts/redline-guard.py`** — 那个兜底机制:一个 pre-commit hook,扫描暂存内容以查找违反发布边界的行为(凭证形式、私有仓库名称、私有内存链接、本地路径)。其禁止的词汇以 SHA-256 哈希值的形式发布,因此防护机制本身不会违反它所执行的红线。 ## 从这里开始 1. **`security/posture.md`** — 本仓库所假设的模型:分层控制,而不是单一的银弹 hook。 2. **`incidents/2026-07-04-github-pat-read-grep-leak.md`** — 上述核心主线的最敏锐说明。一个为了阻止 shell 命令泄露 token 而构建的 hook,被 Claude 自己的 `Read` 和 `Grep` 工具在没有涉及任何 shell 的情况下读取同一个文件所绕过——甚至“用 grep 代替 cat”也不安全,因为内容模式的 grep 仍然会打印匹配的行,而对于一个 `"KEY": "value"` 配置项来说,匹配的行*就是*密钥本身。 3. **`security/credential-guard.py`** — 修复方案,以其实际的形式呈现。 ## 客观地说说规模 这是一位工程师的机器,而不是一个团队或一个平台。这里没有“舰队”,没有共享的事件通道,没有待命轮换——这里的每一篇“事后复盘”都是由单人会话在错误发生的同一轮次中捕捉到的。之所以仍然发布出来,是因为这些故障模式(机械防护中的工具形式盲区、无上限的多 agent 扇出成本、在生产凭证环境中的意外调试会话)并不取决于团队规模。它们只需要一个拥有 shell 访问权限的 agent,以及一个过早信任它的人。
标签:AI合规, AI智能体, AI运维, StruQ, 凭证保护, 安全防护, 应用安全, 提示词注入防护, 网络安全研究, 逆向工具