SecretSpec 发布了 dotenv-ng,称 dotenvy 更改了一个 bcrypt 凭证
Domen Kozar 的分支让 SecretSpec 能控制用于其凭据迁移路径的解析器的修复和发布。
By RuntimeWire Staff · Published
Primary source: SecretSpec
Why it matters
Migration tooling cannot safely move credentials out of `.env` files if the parser changes them first. The fork gives SecretSpec control over a dependency inside its security boundary.

Domen Kozar, the founder of Cachix, released dotenv-ng 1.0 after SecretSpec found that the Rust dotenvy parser it used could silently change a credential while reading a .env file.
The August 15 release grew from a narrow bug into a decision about dependency control. In Kozar's announcement, SecretSpec said the new library began as a fork of dotenvy 0.15.7. SecretSpec initially considered carrying a small patch, then found enough issues around JSON, Windows paths, Unicode names, precedence and process-environment mutation to justify a breaking 1.0 rewrite.
Kozar has spent much of his career removing sharp edges from developer infrastructure. He studied telecommunications at the University of Ljubljana, participated in Google Summer of Code from 2010 through 2012, and began contributing to Nix in 2012. According to Cachix's history, he started the Nix consultancy Enlambda in 2016 and launched Cachix in 2018 to make reproducible builds and software distribution easier for engineering teams. Calm Company Fund announced an investment in Cachix in May 2020. Kozar's biography provides additional background on his work before Cachix.
解析器将凭证变成了另一个字符串
触发分叉的故障记录在 SecretSpec issue #73,该问题于 4 月 17 日提出。一个测试用的 .env 文件包含以美元符号开头字段(例如 $2a$10)的 bcrypt 片段。文件本身没有被改动,但 dotenvy 将这些片段解释为变量替换。
该问题的复现示例显示加载后的值丢失了 bcrypt 字符串的一部分。SecretSpec 在稍后遭遇了认证失败,调查因此偏离了导致问题的解析器。解析器返回的是语法上有效的值,所以没有立即出现指向实际错误的解析错误,开发者无法直接定位问题。
这种失败模式比直接拒绝有更高的风险。可见的错误会在靠近原因处停止执行。被静默修改的令牌、密码或哈希可能进入应用程序,并在数据库、认证服务或外部 API 处失败,看起来像是凭证错误或配置失误。
上游已经知道这种行为。一个关于禁用替换的 dotenvy 请求 于 2024 年 7 月 29 日提出,警告环境值中的美元符号可能会意外消失。一个添加该选项的 pull request 在 2026 年提交,但目标是一个未发布的 API。那使得 SecretSpec 无法依赖已发布的 dotenvy 版本来获得可配置的替换。该仓库仍将 2023 年 3 月 22 日发布的 0.15.7 标识为其最新已发布版本。
为了发布控制而分叉
Rust 的 dotenv 依赖链已经经历过一次维护移交。原始的 dotenv crate 在 2020 年停止发布,RustSec 将其归类为不再维护,并建议用户改用 dotenvy。
Dotenvy 自称是一个维护良好的分叉,但当该错误阻碍 SecretSpec 时,其发布间隔已经超过三年。Kozar 承认开源维护者并不欠下游用户一个发布。SecretSpec 仍然需要“在我们控制的时间表上进行破坏性修复”(来源)。
这个时间表是创建 dotenv-ng 的战略原因。SecretSpec 在本地文件、密码管理器和云密钥存储之间处理凭证迁移。深嵌在该迁移路径中的解析器即便由他人维护,也可能成为 SecretSpec 的安全边界的一部分。分叉使得 Cachix 对代码、兼容性中断和发布节奏负责。
代价是一个长期的维护义务。Cachix 现在必须跟踪 Rust 的变更、解析器的边界情况以及各异的 .env 方言。dotenv-ng README 表示渲染遵循 dotenv-ng 语法,并不承诺兼容所有 dotenv 方言。这个更窄的契约让 Kozar 可以优先考虑无损解析,而不是继承的行为。
dotenv-ng 的变化
在 dotenv-ng 中,美元符号默认被视为字面量。需要变量替换的应用必须显式启用它。这颠覆了导致 bcrypt 故障的默认假设,保护了包含以美元符号开头片段的密码、哈希和令牌。
重写还引入了具有来源感知的结构化错误,在更改进程环境之前验证完整输入,并在环境变更周围设置了显式的 Rust unsafe 边界。它的语法接受破折号、开头为数字、开头为点和键名中的 Unicode,而其渲染器只添加为使值能在解析-渲染往返过程中存活所需的引号和转义。
SecretSpec 表示 属性测试覆盖了任意 Unicode、语法密集的值和完整文档。Kozar 还报告了解析器和渲染器的 100% 行覆盖率。这些是维护者报告的测试指标,而不是每种 .env 方言都会被完全相同解析的证据,兼容性说明对此有明确区分。
Rust 应用可以从 crates.io 安装 dotenv-ng,同时通过 Cargo 别名保留熟悉的 dotenv 依赖名。该库建议对大多数应用使用非变异加载,并将环境变更保留给在启动额外线程或异步运行时之前明确调用的场景。
SecretSpec 想掌握退出坡道
这个分叉适合 SecretSpec 产品中的一个明显矛盾。SecretSpec 认为环境变量和 .env 文件并非长期的凭证接口,但它仍然在一个新的 .env 解析器上进行了投资。
SecretSpec 首页列出 30 个提供者和九类语言 SDK,涵盖本地钥匙串、密码管理器、Vault、云密钥存储、SOPS、.env 文件和进程环境。现有的 .env 文件通常是采用的起点。如果 SecretSpec 在导入或执行过程中误读了这些文件,其更广泛的迁移模型会在凭证到达更安全的提供者之前就失败。
Kozar 正把 .env 视为一个退出坡道。拥有解析器让 SecretSpec 能控制首个且最易出错的步骤:在不更改用户现有凭证的前提下读取它们。从 SecretSpec 0.20 开始,dotenv-ng 将在 SecretSpec 中处理 dotenv 的解析和渲染。
该发布也捕捉到开源中创始人常做的决定。创业公司可以等待上游发布、维护私有补丁或接受分叉的责任。Kozar 选择了分叉,因为该故障涉及凭证数据且上游的发布节奏不再与 SecretSpec 相匹配。由此产生的库虽然小,但它将一个重要依赖重新置于创始人的控制之下。