fix: decode WinRM Cyrillic from CP866 and show deploy.log UTF-8 (0.20.17)

This commit is contained in:
2026-06-20 19:58:54 +10:00
parent 9484160045
commit 4d9a9e7e2b
4 changed files with 69 additions and 7 deletions
+47 -5
View File
@@ -19,7 +19,7 @@ _CLIXML_PROGRESS_NOISE = frozenset(
}
)
_CLIXML_MARKER = "#< CLIXML"
_CYRILLIC_RE = re.compile(r"[\u0400-\u04FF]")
RDP_REMOTE_STAGING = r"C:\ProgramData\RDP-login-monitor\_sac_staging"
RDP_BUNDLE_FILES = (
"Login_Monitor.ps1",
@@ -128,6 +128,39 @@ def _clixml_to_plain(text: str) -> str:
return without
def _decode_text_score(text: str) -> int:
cyrillic = len(_CYRILLIC_RE.findall(text))
suspicious = len(re.findall(r"\?{3,}", text))
return cyrillic * 5 - text.count("\ufffd") * 20 - suspicious * 15 - max(0, text.count("?") - cyrillic) * 2
def _decode_winrm_bytes(data: bytes) -> str:
if not data:
return ""
for encoding in ("utf-8-sig", "utf-8"):
try:
text = data.decode(encoding)
except UnicodeDecodeError:
break
score = _decode_text_score(text)
if text.isascii() or score > 0:
return text
best_score = -10_000
best_text = ""
for encoding in ("cp866", "cp1251"):
try:
text = data.decode(encoding)
except UnicodeDecodeError:
continue
score = _decode_text_score(text)
if score > best_score:
best_score = score
best_text = text
if best_text:
return best_text
return data.decode("utf-8", errors="replace")
def _winrm_failure_detail(stdout: str, stderr: str, exit_code: int) -> str:
stdout_plain = _clixml_to_plain(stdout)
stderr_plain = _clixml_to_plain(stderr)
@@ -213,8 +246,8 @@ def run_winrm_ps(
except Exception as exc:
return WinRmCmdResult(ok=False, message=f"WinRM error ({target}): {exc}", target=target)
stdout = (result.std_out or b"").decode("utf-8", errors="replace")
stderr = (result.std_err or b"").decode("utf-8", errors="replace")
stdout = _decode_winrm_bytes(result.std_out or b"")
stderr = _decode_winrm_bytes(result.std_err or b"")
exit_code = int(result.status_code)
stdout, stderr = _sanitize_winrm_streams(stdout, stderr)
return _finalize_winrm_run(
@@ -260,8 +293,8 @@ def run_winrm_cmd(
except Exception as exc:
return WinRmCmdResult(ok=False, message=f"WinRM error ({target}): {exc}", target=target)
stdout = (result.std_out or b"").decode("utf-8", errors="replace")
stderr = (result.std_err or b"").decode("utf-8", errors="replace")
stdout = _decode_winrm_bytes(result.std_out or b"")
stderr = _decode_winrm_bytes(result.std_err or b"")
exit_code = int(result.status_code)
stdout, stderr = _sanitize_winrm_streams(stdout, stderr)
return _finalize_winrm_run(
@@ -292,6 +325,9 @@ def _powershell_script_prelude() -> str:
return (
"$ProgressPreference = 'SilentlyContinue'\n"
"$InformationPreference = 'SilentlyContinue'\n"
"$utf8NoBom = New-Object System.Text.UTF8Encoding $false\n"
"[Console]::OutputEncoding = $utf8NoBom\n"
"$OutputEncoding = $utf8NoBom\n"
)
@@ -338,8 +374,14 @@ def _deploy_from_staging_body(staging_path: str) -> str:
f"$staging = '{literal}'\n"
"$deploy = Join-Path $staging 'Deploy-LoginMonitor.ps1'\n"
"if (-not (Test-Path -LiteralPath $deploy)) { throw \"Deploy-LoginMonitor.ps1 missing in staging\" }\n"
"$logPath = Join-Path $env:ProgramData 'RDP-login-monitor\\Logs\\deploy.log'\n"
"Write-Output \"Running: $deploy -SourceShareRoot $staging\"\n"
"powershell.exe -NoProfile -ExecutionPolicy Bypass -File $deploy -SourceShareRoot $staging\n"
"if (Test-Path -LiteralPath $logPath) {\n"
" Write-Output ''\n"
" Write-Output '--- deploy.log ---'\n"
" Get-Content -LiteralPath $logPath -Encoding UTF8 | Select-Object -Last 60\n"
"}\n"
)
+1 -1
View File
@@ -1,5 +1,5 @@
"""Единый источник версии SAC (API, health, логи, OpenAPI)."""
APP_NAME = "Security Alert Center"
APP_VERSION = "0.20.16"
APP_VERSION = "0.20.17"
APP_VERSION_LABEL = f"{APP_NAME} v.{APP_VERSION}"