diff --git a/tests/entrypoints/launchers/api_server/test_api_server_process_manager.py b/tests/entrypoints/launchers/api_server/test_api_server_process_manager.py index 93d0c49700..40fefc2374 100644 --- a/tests/entrypoints/launchers/api_server/test_api_server_process_manager.py +++ b/tests/entrypoints/launchers/api_server/test_api_server_process_manager.py @@ -401,3 +401,52 @@ def test_gather_actual_addresses_child_crash_before_report(): manager.shutdown() time.sleep(0.2) sock.close() + + +def test_rust_frontend_launch_log_redacts_credentials(monkeypatch, caplog): + """The Rust frontend command carries every non-default arg as JSON. + Credentials must not reach the log line.""" + import subprocess as subprocess_mod + + from vllm.entrypoints.launchers.cli_args import make_arg_parser + from vllm.utils.argparse_utils import FlexibleArgumentParser + from vllm.v1.utils import RustFrontendProcessManager + + hf_token = "hf_TESTTOKEN123" + api_key = "sk-TESTKEY456" + args = make_arg_parser(FlexibleArgumentParser()).parse_args( + ["--model", "org/model", "--hf-token", hf_token, "--api-key", api_key] + ) + + class _FakeProc: + pid = 4321 + returncode = 0 + + def poll(self): + return 0 + + def wait(self, timeout=None): + return 0 + + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + monkeypatch.setattr(subprocess_mod, "Popen", lambda *a, **kw: _FakeProc()) + with caplog.at_level("INFO", logger="vllm.v1.utils"): + RustFrontendProcessManager( + binary_path="/nonexistent/vllm-rs", + sock=sock, + args=args, + input_address="ipc:///tmp/in", + output_address="ipc:///tmp/out", + engine_start_index=0, + engine_count=1, + data_parallel_size=1, + ) + finally: + sock.close() + + message = caplog.text + assert "Launching Rust frontend:" in message + assert hf_token not in message + assert api_key not in message + assert '"hf_token": "***"' in message diff --git a/tests/entrypoints/serve/utils/test_api_utils.py b/tests/entrypoints/serve/utils/test_api_utils.py index 989c8be208..b1bf765595 100644 --- a/tests/entrypoints/serve/utils/test_api_utils.py +++ b/tests/entrypoints/serve/utils/test_api_utils.py @@ -8,8 +8,8 @@ import pytest from vllm.entrypoints.openai.engine.protocol import StreamOptions from vllm.entrypoints.serve.utils import api_utils from vllm.entrypoints.serve.utils.api_utils import ( - _redact_sensitive_args, get_max_tokens, + redact_sensitive_args, should_include_usage, ) @@ -121,7 +121,7 @@ class TestRedactSensitiveArgs: def test_redact_replaces_sensitive_values_only(self): args = {"api_key": self.API_KEY, "hf_token": "hf_secret", "other": "visible"} - redacted = _redact_sensitive_args(args) + redacted = redact_sensitive_args(args) assert redacted == { "api_key": "***", "hf_token": "***", @@ -136,7 +136,7 @@ class TestRedactSensitiveArgs: def test_no_sensitive_fields_returns_original(self): args = {"model_tag": "org/model", "other": "visible"} - assert _redact_sensitive_args(args) is args + assert redact_sensitive_args(args) is args def test_api_key_not_in_log(self, monkeypatch, caplog): non_default = { diff --git a/vllm/entrypoints/serve/utils/api_utils.py b/vllm/entrypoints/serve/utils/api_utils.py index c3c99e5744..87229a4722 100644 --- a/vllm/entrypoints/serve/utils/api_utils.py +++ b/vllm/entrypoints/serve/utils/api_utils.py @@ -271,7 +271,7 @@ def jsonify_non_default_args( _SENSITIVE_ARG_FIELDS = frozenset({"api_key", "hf_token"}) -def _redact_sensitive_args(args: dict[str, Any]) -> dict[str, Any]: +def redact_sensitive_args(args: dict[str, Any]) -> dict[str, Any]: """Return a copy of `args` with sensitive values redacted for logging.""" if not any(key in _SENSITIVE_ARG_FIELDS for key in args): return args @@ -283,7 +283,7 @@ def _redact_sensitive_args(args: dict[str, Any]) -> dict[str, Any]: def log_non_default_args(args: Namespace | EngineArgs): non_default_args = get_non_default_args(args) - logger.info("non-default args: %s", _redact_sensitive_args(non_default_args)) + logger.info("non-default args: %s", redact_sensitive_args(non_default_args)) def should_include_usage( diff --git a/vllm/v1/utils.py b/vllm/v1/utils.py index 0501c2ae32..b0411dc9e8 100644 --- a/vllm/v1/utils.py +++ b/vllm/v1/utils.py @@ -389,7 +389,12 @@ class RustFrontendProcessManager: args_json = json.dumps(args_dict, sort_keys=True) cmd.extend(["--args-json", args_json]) - logger.info("Launching Rust frontend: %s", " ".join(cmd)) + # The subprocess needs the real values, but the log must not carry + # credentials such as api_key or hf_token. + from vllm.entrypoints.serve.utils.api_utils import redact_sensitive_args + + redacted_json = json.dumps(redact_sensitive_args(args_dict), sort_keys=True) + logger.info("Launching Rust frontend: %s", " ".join(cmd[:-1] + [redacted_json])) self._proc = subprocess.Popen(cmd, pass_fds=(fd,)) # Create a process wrapper with a sentinel fd for monitoring