Prhub

#54379 [CI] Avoid logging test server environment values

原始 PR 作者 taneem-ibrahim 合并时间 2026-09-04 05:00 文件变更 2 提交数 8 评论 18 代码增减 +91 / -4

执行摘要

修复测试服务器在 CI 日志中泄露敏感凭据的问题,对 CLI 参数进行脱敏并移除环境变量转储。

正如 PR body 所述:“RemoteOpenAIServer logged its complete inherited environment and unredacted CLI credentials, potentially exposing secrets in CI output.” 这是一个需要修复的安全漏洞,以防止在 CI 输出日志中意外暴露 --api-key、--hf-token 等敏感凭据以及继承的环境变量。

这个 PR 是一个清晰、必要的安全改进。它修复了一个实际的敏感信息泄露风险,并通过增加测试覆盖确保了修复的稳健性。建议所有参与 CI/CD 和测试基础设施维护的工程师精读,学习其中的脱敏实现和测试验证方法。它也是如何在测试代码中安全处理凭据的良好范例。

讨论亮点
  1. 扩展应用范围:Reviewer yewentao256 指出,RemoteLaunchRenderServer(tests/utils.py#L815-L830)也可能接收相同的敏感参数,建议一并应用脱敏。作者 taneem-ibrahim 接受了建议并实现了修改。
  2. 处理多值参数:yewentao256 追问脱敏逻辑是否能覆盖 --api-key key1 key2 这种列表形式。作者最初承认不足,随后更新了实现,使脱敏逻辑能识别并处理 nargs="+" 的情况,确保所有 API 密钥值都被脱敏。
  3. 参数变体支持:讨论中提到了是否应该统一 --api_key 和 --api-key 的写法。yewentao256 提出了关于长期维护的疑问,DarkLight1337 认为两者都支持对 UX 更好。作者最终选择同时支持两种变体(通过将 _ 替换为 - 进行查找),以确保安全覆盖。

实现拆解

  1. 引入脱敏工具函数:在 tests/utils.py 中新增常量 _SENSITIVE_CLI_ARG_NARGS,定义 --api-key (nargs=+) 和 --hf-token (nargs=?) 的参数行为。新增 _redact_sensitive_cli_args 函数,该函数遍历命令行参数列表,根据参数名(同时支持连字符和下划线变体)和 = 分隔符识别敏感值,并将其替换为 ***。该逻辑能正确处理单值 (--hf-token=token) 和多值 (--api-key key1 key2) 的情况。
  2. 应用脱敏并清理日志:修改 tests/utils.py 中的 RemoteOpenAIServer._start_server 和 RemoteLaunchRenderServer._start_server 方法。在构建 serve_cmd 列表后,调用 _redact_sensitive_cli_args(serve_cmd) 生成 redacted_serve_cmd,并使用脱敏后的命令进行日志打印。同时,完全移除了 RemoteOpenAIServer 中原本打印 Environment variables: {env} 的日志行,这是防止环境变量泄露的关键步骤。
  3. 添加回归测试:在 tests/entrypoints/unit_tests/test_remote_vllm_server.py 中新增两个参数化测试。test_redact_sensitive_cli_arg_variants 直接测试 _redact_sensitive_cli_args 函数对各种参数格式的处理。test_server_redacts_sensitive_values 通过 mock subprocess.Popen 和 monkeypatch.setenv 来模拟启动服务器,验证输出(capsys)中不包含任何实际的敏感值(包括 CLI 参数值和环境变量值),同时确认原始的未脱敏参数和环境变量仍被正确传递给子进程。
文件 模块 状态 重要度
tests/utils.py 测试工具 modified 6.41
tests/entrypoints/unit_tests/test_remote_vllm_server.py 远程服务器测试 modified 6.6

关键符号

_redact_sensitive_cli_args

关键源码片段

tests/utils.py test-coverage

引入核心脱敏函数 `_redact_sensitive_cli_args` 并修改两个远程服务器类的日志逻辑,是安全修复的主要实现文件。

# tests/utils.py 中新增的敏感参数定义和脱敏函数
_SENSITIVE_CLI_ARG_NARGS = {"--api-key": "+", "--hf-token": "?"}
​
​
def _redact_sensitive_cli_args(args: Sequence[str]) -> list[str]:
    """脱敏命令行参数列表中的敏感值。
​
    处理格式:
      --api-key <value1> <value2> ... (多值)
      --api-key=<value>
      --hf-token <value> (单值)
      --hf-token=<value>
    """
    redacted_args = list(args)
    index = 0
    while index < len(args):
        # 解析参数名,支持 --api-key 和 --api_key 两种形式
        name, separator, _ = args[index].partition("=")
        nargs = _SENSITIVE_CLI_ARG_NARGS.get(name.replace("_", "-"))
        if nargs is None:
            index += 1
            continue
        # 处理等号分隔的形式 (--api-key=value)
        if separator:
            redacted_args[index] = f"{name}=***"
        index += 1
        # 处理空格分隔的值:
        # - 如果是多值参数(nargs="+"),持续脱敏直到遇到下一个选项
        # - 如果是单值参数(nargs="?"),脱敏一个值后即停止
        if not separator or nargs == "+":
            while index < len(args) and not args[index].startswith("-"):
                redacted_args[index] = "***"
                index += 1
                if nargs == "?":
                    break
    return redacted_args
tests/entrypoints/unit_tests/test_remote_vllm_server.py test-coverage

新增针对脱敏函数和服务器启动行为的回归测试,是验证修复正确性的关键文件。

# tests/entrypoints/unit_tests/test_remote_vllm_server.py 中新增的测试用例
​
@pytest.mark.parametrize(
    ("args", "expected"),
    [
        (["--api-key", "key1", "key2"], ["--api-key", "***", "***"]),
        (["--api_key", "key1", "key2"], ["--api_key", "***", "***"]),
        (["--api-key=key1", "key2"], ["--api-key=***", "***"]),
        (["--api_key=key1", "key2"], ["--api_key=***", "***"]),
        (["--hf-token", "token"], ["--hf-token", "***"]),
        (["--hf_token", "token"], ["--hf_token", "***"]),
        (["--hf-token=token"], ["--hf-token=***"]),
        (["--hf_token=token"], ["--hf_token=***"]),
    ],
)
def test_redact_sensitive_cli_arg_variants(args: list[str], expected: list[str]):
    """测试脱敏函数对不同参数格式的处理。"""
    assert test_utils._redact_sensitive_cli_args(args) == expected
​
​
@pytest.mark.parametrize(
    ("server_cls", "command"),
    [
        (RemoteOpenAIServer, ["vllm", "serve"]),
        (RemoteLaunchRenderServer, ["vllm", "launch", "render"]),
    ],
)
def test_server_redacts_sensitive_values(
    server_cls,
    command: list[str],
    monkeypatch: pytest.MonkeyPatch,
    capsys: pytest.CaptureFixture[str],
):
    """测试服务器启动时日志脱敏和原始参数传递。"""
    server = object.__new__(server_cls)
    popen = Mock()
    monkeypatch.setattr(test_utils.subprocess, "Popen", popen)
    monkeypatch.setenv("INHERITED_SECRET", "inherited-secret")
    serve_args = [
        "--api_key",
        "api-secret-1",
        "api-secret-2",
        "--hf-token=hf-secret",
        "--max-num-seqs",
        "2",
    ]
​
    server._start_server(
        "test-model", serve_args, {"OVERRIDE_SECRET": "override-secret"}
    )
​
    stdout = capsys.readouterr().out
    # 验证敏感值不出现在日志中
    assert "api-secret-1" not in stdout
    assert "api-secret-2" not in stdout
    assert "hf-secret" not in stdout
    assert "inherited-secret" not in stdout
    assert "override-secret" not in stdout
    # 验证脱敏后的命令格式正确
    assert "--api_key *** ***" in stdout
    assert "--hf-token=***" in stdout
    assert "--max-num-seqs 2" in stdout
    # 验证原始参数和环境变量仍正确传递给子进程
    assert popen.call_args.args[0] == [*command, "test-model", *serve_args]
    child_env = popen.call_args.kwargs["env"]
    assert child_env["INHERITED_SECRET"] == "inherited-secret"
    assert child_env["OVERRIDE_SECRET"] == "override-secret"

评论区精华

脱敏范围扩展至 RemoteLaunchRenderServer 安全

Reviewer `yewentao256` 指出 `RemoteLaunchRenderServer` 也应应用相同的脱敏逻辑,因为它也可能接收敏感的 serve 参数。作者 `taneem-ibrahim` 认同并实施了修改。

结论:扩展了修复范围,确保所有远程测试服务器的日志都经过脱敏。 · 已解决

处理多值 `--api-key` 参数 正确性

`yewentao256` 询问脱敏逻辑是否覆盖 `--api-key key1 key2` 的列表形式。作者最初承认 `nargs="?"` 的默认处理不足,随后将 `--api-key` 的 `nargs` 定义为 `+`,确保所有后续的值都被脱敏。

结论:完善了脱敏逻辑,使其能正确处理 `api_key` 作为 `list[str]` 的参数形式。 · 已解决

支持参数名变体(`-` 和 `_`) 设计

讨论了是否应该统一 `--api_key` 和 `--api-key`。`yewentao256` 提出长期维护建议,`DarkLight1337` 认为两者都支持更好。作者最终采用 `name.replace("_", "-")` 的方式同时支持两种变体。

结论:决策为同时支持连字符和下划线形式的敏感参数,以兼顾现有使用习惯和安全覆盖。 · 已解决

风险与影响

风险较低。变更仅限于测试工具代码 (tests/utils.py 和 tests/entrypoints/unit_tests/test_remote_vllm_server.py),未触及生产代码逻辑。主要风险在于新引入的脱敏函数 _redact_sensitive_cli_args 存在缺陷,导致某些敏感值未被正确脱敏,但新增的单元测试(test_redact_sensitive_cli_args 和 test_server_redacts_sensitive_values)已对此进行了覆盖和验证。移除环境变量打印日志是纯防御性改进,无功能风险。

用户:无直接影响。该变更仅改善 CI/CD 管道的输出安全性,防止在公开的 CI 日志中意外泄露密钥。
系统:增强了测试基础设施的安全性,遵循了最小权限和敏感信息保护的最佳实践。移除环境变量日志可能略微减少调试信息,但脱敏后的命令日志保留了关键的启动命令信息用于问题排查。
团队:为测试团队和贡献者树立了处理敏感信息的范例,可能激励在其他测试工具中采用类似的脱敏模式。也引发了关于命名规范(- vs _)一致性的内部讨论。

测试工具链安全改进

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论