From 7b23a1084683c58a4c20f4e1290968536cc34e85 Mon Sep 17 00:00:00 2001 From: Elior Nataf Lackritz Date: Mon, 19 Oct 2026 09:45:42 -0400 Subject: [PATCH] fix(cli): stop printing subprocess stdin in verbose output (#9350) `--verbose` printed each subprocess command followed by everything piped to its stdin, which can hold credentials. It now prints only the command line, so `langgraph up --verbose`, `langgraph build` and `langgraph deploy --verbose` no longer show the generated compose file or Dockerfile (`langgraph dockerfile` still writes the Dockerfile out). --- libs/cli/langgraph_cli/exec.py | 7 ++----- libs/cli/tests/unit_tests/test_exec.py | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 5 deletions(-) create mode 100644 libs/cli/tests/unit_tests/test_exec.py diff --git a/libs/cli/langgraph_cli/exec.py b/libs/cli/langgraph_cli/exec.py index 32c3d3470..72257b098 100644 --- a/libs/cli/langgraph_cli/exec.py +++ b/libs/cli/langgraph_cli/exec.py @@ -44,11 +44,8 @@ async def subp_exec( on_stdout: Callable[[str], bool | None] | None = None, ) -> tuple[str | None, str | None]: if verbose: - cmd_str = f"+ {cmd} {' '.join(map(str, args))}" - if input: - print(cmd_str, " <\n", "\n".join(filter(None, input.splitlines())), sep="") - else: - print(cmd_str) + # Never echo stdin: it can carry secrets (docker login --password-stdin). + print(f"+ {cmd} {' '.join(map(str, args))}") if wait: await asyncio.sleep(wait) diff --git a/libs/cli/tests/unit_tests/test_exec.py b/libs/cli/tests/unit_tests/test_exec.py new file mode 100644 index 000000000..e17f17eed --- /dev/null +++ b/libs/cli/tests/unit_tests/test_exec.py @@ -0,0 +1,17 @@ +import sys + +from langgraph_cli.exec import subp_exec + + +async def test_verbose_prints_command_but_not_stdin(capsys): + await subp_exec( + sys.executable, + "-c", + "import sys; sys.stdin.read()", + input="registry-push-token\n", + verbose=True, + ) + + out = capsys.readouterr().out + assert out.startswith(f"+ {sys.executable} -c"), out + assert "registry-push-token" not in out, out