yangsi7/rotate-env

GitHub: yangsi7/rotate-env

一个轻量 CLI 工具,用于在递归查找的所有 .env 和 .mcp.json 文件中安全、批量地轮换泄露的 API 密钥,全程不暴露密钥内容。

Stars: 1 | Forks: 0

rotate logo: a key with a worried face wrapped in a rotate arrow

rotate

一个小型的 CLI 工具,可以批量轮换从你运行它的位置递归查找到的 `.env` 和 `.mcp.json` 文件中出现的所有 API-key 环境变量。手动或由 AI agent 运行都很安全:它从文件或环境变量中读取新 key,并且绝不显示它。

CI ShellCheck Release License: MIT

你的 AI 编码 agent 刚刚把一个有效的 API key 粘贴到了你一半项目的十二个 `.env` 文件和三个 `.mcp.json` 配置中。现在你必须轮换它:找到每一个文件,用它们各自恰好使用的引号风格替换其中的值,并且在这个过程中不能把新 key 泄漏到你的 shell 历史记录或已提交的模板中。`rotate` 恰好能在一行命令中完成这些操作,而且全程不会打印出密钥。 ![rotate 演示:查找散落在 .env 和 .mcp.json 文件中的泄露 API key 副本,然后用一行命令全部轮换](https://raw.githubusercontent.com/yangsi7/rotate-env/main/demo.gif) ## 快速开始 ``` # 1) Dry-run:查看所有持有该 key 的文件(不写入任何内容): rotate SOME_API_KEY_ENV_VAR # 2) 无需值守地在所有地方 rotate 它: ROTATE_NEW_VALUE='sk_your_new_api_key_value' rotate --apply --yes SOME_API_KEY_ENV_VAR ``` 默认情况下,`rotate` 会递归搜索当前目录。你可以使用 `--root` 将其指向其他位置: ``` rotate --apply --root ~/code ELEVENLABS_API_KEY ``` ## 安装 **Homebrew** (macOS 和 Linux): ``` brew install yangsi7/tap/rotate ``` **Nix** (flake): ``` nix run github:yangsi7/rotate-env -- --help # run without installing nix profile install github:yangsi7/rotate-env # install ``` **安装脚本** (任何带有 `curl` 的 Unix 系统): ``` curl -fsSL https://raw.githubusercontent.com/yangsi7/rotate-env/v0.1.3/install.sh | bash ``` 这会将 `rotate` 安装到 `~/.local/bin` 中(无需 sudo)。想在运行前先检查一下?先阅读脚本: ``` curl -fsSLO https://raw.githubusercontent.com/yangsi7/rotate-env/v0.1.3/install.sh less install.sh bash install.sh ```
手动安装 (git clone) ``` git clone https://github.com/yangsi7/rotate-env.git cd rotate-env make install # copies rotate into ~/.local/bin (override with PREFIX=...) # 或者直接将其复制到您 PATH 上的任意位置: install -m 755 rotate ~/.local/bin/rotate ``` 可以使用 `make uninstall` 或 `bash install.sh --uninstall` 卸载。
**前置要求:** `bash`、`awk` 以及 [`fd`](https://github.com/sharkdp/fd)(Debian/Ubuntu 中为 `fd-find`,二进制文件为 `fdfind`)。仅在处理 `.mcp.json` 文件时才需要 [`jq`](https://jqlang.github.io/jq/)。 ## 用例 - **AI agent 泄漏了 key。** Agent 将密钥复制到了多个项目文件中。一次性在所有地方轮换它。 - **密钥被入侵或过期。** 你重新生成了提供商的密钥,并且需要每个项目都应用新的密钥。 - **离职处理 / 凭据清理。** 有人离开了团队,或者某个密钥超出了策略使用期限。按计划进行轮换。 - **Monorepo 和多项目设置。** 一个被数十个 `.env` 文件和 MCP 配置引用的密钥。 ## 用法 ``` rotate [options] ``` 默认模式是掩码的预演(dry-run)预览。传入 `--apply` 来实际写入(会有确认提示,或者加上 `--yes` 跳过提示)。 | 选项 | 描述 | | --- | --- | | `-n, --dry-run` | 预览更改而不写入(默认) | | `--apply` | 写入更改(会提示确认) | | `-y, --yes` | 跳过确认提示(非交互式 `--apply` 必需) | | `-b, --backup` | 在写入之前将每个文件复制到 `.bak.`(chmod 600) | | `--value-file ` | 从文件中读取新值 | | `--old-value-file ` | 仅替换当前值匹配的项(精准替换) | | `--root ` | 递归搜索根目录(可重复指定;将替换默认设置) | | `--shallow-root ` | 深度为 1 的搜索根目录(可重复指定) | | `--max-depth ` | 限制递归深度 | | `--list` | 列出已发现的目标文件并退出 | | `-v, --verbose` | 调试日志 | | `-V, --version` | 打印版本 | | `-h, --help` | 包含更多示例的完整帮助 | ### 提供新值(绝不在命令行上提供) 密钥绝不会从 flag 中读取,因为命令行参数对每个进程来说通过 `ps` 都是可见的。请改用以下方式之一来提供: ``` rotate --apply --value-file ~/new.key ELEVENLABS_API_KEY # from a file ROTATE_NEW_VALUE='sk_...' rotate --apply ELEVENLABS_API_KEY # from an env var pbpaste | rotate --apply ELEVENLABS_API_KEY # piped on stdin rotate --apply ELEVENLABS_API_KEY # silent interactive prompt ``` ### 精准轮换 仅替换特定的泄漏值,保留同一变量的任何其他值不受影响: ``` ROTATE_OLD_VALUE='sk_leaked' ROTATE_NEW_VALUE='sk_fresh' rotate --apply --yes ANTHROPIC_API_KEY ``` ## 支持的文件格式 **`.env`, `.env.*`, `.envrc`**(`=` 分隔符),包含所有常见的形式: ``` KEY=value KEY="value" KEY='value' export KEY=value KEY= # empty / declared-but-unset is filled in KEY=value # inline comment (comment preserved) ``` 保留原始的引号风格、`export` 前缀和行尾注释,并且按字面值插入(因此 key 中的 `/`、`&` 和反斜杠都是安全的)。匹配是精确的,所以轮换 `API_KEY` 绝不会影响 `GOOGLE_API_KEY` 或 `NEXT_PUBLIC_API_KEY`。 **`.mcp.json`**(JSON `:` 分隔符),覆盖密钥隐藏的所有三个位置: ``` { "mcpServers": { "a": { "env": { "ELEVENLABS_API_KEY": "value" } }, // env block "b": { "headers": { "x-api-key": "value" } }, // HTTP headers "c": { "args": ["env", "ELEVENLABS_API_KEY=value", "cmd"] } // args[] wrapper }} ``` ## 工作原理 1. **发现** 每次运行都使用 `fd` 查找目标文件(这样新项目会被自动识别)。名为 `node_modules`、`worktrees`、`.git`、`.next`、`dist`、`build` 的目录会被剔除;`*.yaml`/`*.yml` 文件会被跳过;占位符文件(`*example*`、`*sample*`、`*template*`、`.env.dist`、`.env.tmpl`、`.env.defaults`,不区分大小写匹配)以及 `.bak`/`.orig` 备份文件会被排除,这样真实的密钥就永远不会被写入到已提交的模板中。 2. **编辑** 使用 `awk` 处理 `.env` 文件(字面值、感知引号、精确匹配 key),使用 `jq walk` 处理 `.mcp.json`(覆盖 env/headers/args)。 3. **写入** 原子化操作:在同目录下生成临时文件,然后执行 `mv`,保留原始权限。 ## 安全保证 - **密钥绝不会出现在 argv 上**(通过环境变量传递给 `awk`/`jq`),并且**绝不会被打印**(所有日志都会将其掩码处理为 `ab****yz`)。 - **默认空运行(Dry-run)。** 除非传入 `--apply`,否则不会写入任何内容,而且 dry-run 绝不会写入临时文件,因此新值在预览期间永远不会触及磁盘。 - **幂等性。** 使用相同的值重新运行不会改变任何内容。已经等于新值的条目会被跳过,因此不包含该 key 的文件绝不会被重写。 - **原子化且可逆。** 针对每个文件进行临时文件+`mv` 操作并保留权限,外加可选的 `--backup`(chmod 600)。注意这里没有跨文件事务:如果在写入过程中失败,之前的文件已经被轮换了,所以如果你想要回滚功能,请使用 `--backup`。 ## 适合从 AI agent(或 CI)中运行 `rotate` 首先是一个普通的命令行工具:将其指向某个目录,它就会轮换每个匹配文件中的 key。由于它的设计初衷是绝不暴露密钥,因此把它交给 AI 编码 agent 或 CI 任务也是安全的,不会把密钥泄漏到对话记录、模型上下文或你的日志中(这正是这款工具存在的意义,用来清理这类烂摊子)。 - **它绝不打印密钥。** 所有输出都被掩码为 `ab****yz`,因此该值绝不会落入 agent 对话记录、CI 日志或终端回滚缓冲区中。 - **它绝不会将密钥放在 argv 上。** 该值通过环境变量传递给 `awk`/`jq`,因此无法通过 `ps`、shell 历史记录或 agent 记录的命令行捕获。直接拒绝将密钥作为 `--value` flag 传递。 - **它完全以非交互方式运行。** 通过 `$ROTATE_NEW_VALUE`、`--value-file` 或 stdin 提供值,并在没有 TTY 的情况下运行 `--apply --yes`。默认开启 dry-run,因此 agent 可以在最终确定前预览(已掩码的)计划。 - **它不会泄漏到已提交的文件中。** 绝不会写入占位符和模板文件(`*example*`、`.env.dist` 等,不区分大小写匹配),因此 agent 不会意外地将真实 key 轮换到被 git 追踪的模板中。 - **它是确定性的且可解析的。** 分级日志发送到 stderr,数据发送到 stdout,并且退出码是稳定的(`0` 正常,`2` 用法错误,`127` 缺少依赖),因此 agent 可以根据结果执行不同分支。运行具备幂等性,所以重试总是安全的。 Agent 可以在一次无人值守的调用中,跨每个项目轮换受入侵的 key: ``` ROTATE_NEW_VALUE="$NEW_KEY" rotate --apply --yes ELEVENLABS_API_KEY ``` **Claude Code 用户:** 本仓库内置了一个 `/rotate-key` 技能(`.claude/skills/rotate-key/),它封装了 CLI,提供了一个安全且绝不暴露密钥的工作流(预览,然后使用带外提供的值进行应用)。当你在本仓库的克隆副本中工作时,它会自动可用。 ## 版本控制 本项目遵循 [语义化版本](https://semver.org/)。公开形式包括 flags、输出格式和退出码。请参阅 [CHANGELOG.md](CHANGELOG.md)。 ## 贡献 欢迎提交 Issue 和 PR。在提交之前,请运行 `make lint`(ShellCheck)和 `make test`(bats)。CI 会在 Linux 和 macOS 上运行这两者。 ## 许可证 [MIT](LICENSE) © yangsi7
标签:LNA, SOC Prime, StruQ, 应用安全, 开发工具, 网络调试, 自动化