From 24d93399fb2bb9be8e640b71769564137c615663 Mon Sep 17 00:00:00 2001 From: dengzi <2294612869@qq.com> Date: Thu, 5 Nov 2026 15:09:13 +0800 Subject: [PATCH] fix(lark): keep Windows SID lookup working with non-ASCII account names (#7845) * Keep the Windows SID lookup alive on non-UTF-8 console output `_resolve_current_user_sid` calls `whoami /user /fo csv /nh` in text mode without an encoding. `make dev` and `make gateway` start the Gateway with PYTHONUTF8=1, so on a Chinese Windows host the interpreter decodes as UTF-8 while whoami writes the account name in the console's ANSI code page. The strict decode raises inside subprocess's reader thread; the exception is not re-raised, so stdout comes back as None and the credential-tree hardening walk dies with `AttributeError: 'NoneType' object has no attribute 'strip'` before any directory is created or hardened. Pin the encoding and keep an errors policy. The SID is the second CSV field and is pure ASCII, as are the delimiters, so replacing the undecodable account name is harmless. #6590 pinned the seven lark-cli/npm captures in this file; this is the eighth text-mode capture and, unlike those, does not emit UTF-8, so the errors policy is the part that matters here. Add a cross-platform regression test next to the #6590 encoding tests. * docs(lark): correct the SID capture wording `undecodable` is the spelling used elsewhere in the repository, and the comment about the console code page reads better as "which does not need to match". No behaviour change. --------- Co-authored-by: Willem Jiang --- .../harness/deerflow/integrations/lark_cli.py | 7 ++++ .../test_lark_cli_subprocess_encoding.py | 42 +++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/backend/packages/harness/deerflow/integrations/lark_cli.py b/backend/packages/harness/deerflow/integrations/lark_cli.py index 434a0eb2e3..f31ff4434f 100644 --- a/backend/packages/harness/deerflow/integrations/lark_cli.py +++ b/backend/packages/harness/deerflow/integrations/lark_cli.py @@ -488,6 +488,13 @@ def _resolve_current_user_sid() -> str: ["whoami", "/user", "/fo", "csv", "/nh"], capture_output=True, text=True, + encoding="utf-8", + # whoami writes the account name in the console's ANSI code page, which does + # not need to match the interpreter's encoding (e.g. UTF-8 mode on a Chinese Windows). + # Without errors="replace" the decoder raises inside subprocess's reader + # thread and leaves stdout as None. Only the SID (ASCII) and the CSV + # delimiters matter here, so replacing the account name is harmless. + errors="replace", check=False, ) if result.returncode != 0: diff --git a/backend/tests/test_lark_cli_subprocess_encoding.py b/backend/tests/test_lark_cli_subprocess_encoding.py index d5663a0819..6a43e39786 100644 --- a/backend/tests/test_lark_cli_subprocess_encoding.py +++ b/backend/tests/test_lark_cli_subprocess_encoding.py @@ -17,6 +17,12 @@ that names no codec is decoded with one that mangles UTF-8, while a call that pins its own ``encoding`` is passed through untouched. That reproduces the behaviour of the pre-fix call sites on a cp936/cp1252 host even when the test host itself is UTF-8. + +The Windows SID lookup is a separate capture in the same module: ``whoami`` +writes the account name in the console's ANSI code page rather than UTF-8, so a +pinned UTF-8 decode still needs an ``errors`` policy. Only the SID (ASCII) and +the CSV delimiters are used, but a strict decode kills the pipe reader thread +and turns ``stdout`` into ``None`` before the SID can be read. """ from __future__ import annotations @@ -28,6 +34,7 @@ import subprocess import sys import textwrap from pathlib import Path +from types import SimpleNamespace import pytest @@ -180,3 +187,38 @@ def test_unpinned_capture_corrupts_the_same_output(fake_cli) -> None: # A UTF-8 host locale: the locale decode happens to be correct here. return assert EXPECTED_USER not in raw + + +def test_windows_sid_resolution_tolerates_non_utf8_account_name( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """The Windows credential-tree walk keeps its SID when the account name is CJK. + + ``whoami /user /fo csv /nh`` prints ``"\\",""`` with the + account name in the console's ANSI code page (the bytes below are a GBK user + name). A text-mode capture that names no codec is decoded with the + interpreter's encoding; on a UTF-8-mode host the strict decode raises inside + subprocess's reader thread and ``stdout`` comes back as ``None``. + ``ensure_lark_cli_credential_tree`` then fails with ``AttributeError`` before + it can harden any credential directory. The SID itself is ASCII, so replacing + the undecodable account name is enough to keep the CSV parse intact. + """ + raw = b'"DESKTOP-ABC\\\xb5\xca\xd7\xd3","S-1-5-21-1-2-3-1001"\r\n' + expected_sid = "S-1-5-21-1-2-3-1001" + + def ansi_whoami(_argv, **kwargs): + if kwargs.get("text") or kwargs.get("universal_newlines"): + encoding = kwargs.get("encoding") + if encoding is None: + # Emulate the reader thread dying on a strict locale decode. + return SimpleNamespace(returncode=0, stdout=None, stderr="") + stdout = raw.decode(encoding, kwargs.get("errors") or "strict") + else: + stdout = raw + return SimpleNamespace(returncode=0, stdout=stdout, stderr="") + + # Control: without an errors policy this capture really does lose stdout. + assert ansi_whoami(["whoami"], text=True).stdout is None + + monkeypatch.setattr(lark_cli.subprocess, "run", ansi_whoami) + assert lark_cli._resolve_current_user_sid() == expected_sid