diff --git a/backend/app/services/agent_update.py b/backend/app/services/agent_update.py index 8649d23..51a2e77 100644 --- a/backend/app/services/agent_update.py +++ b/backend/app/services/agent_update.py @@ -192,6 +192,9 @@ def run_windows_agent_update_fallback( host: Host, cfg_win, script_path: str, + *, + repo_url: str = "", + git_branch: str = "main", ) -> AgentUpdateFallbackResult: if not is_windows_host(host): return AgentUpdateFallbackResult(ok=False, message="Host is not Windows", product_version=None) @@ -216,6 +219,8 @@ def run_windows_agent_update_fallback( user=cfg_win.user, password=cfg_win.password, script_path=script_path, + repo_url=repo_url, + git_branch=git_branch, ), ) if result is None: @@ -256,7 +261,13 @@ def execute_agent_update_fallback(db: Session, host: Host) -> AgentUpdateFallbac ) elif product == PRODUCT_RDP or is_windows_host(host): win_cfg = get_effective_win_admin_config(db) - result = run_windows_agent_update_fallback(host, win_cfg, cfg.win_agent_update_script) + result = run_windows_agent_update_fallback( + host, + win_cfg, + cfg.win_agent_update_script, + repo_url=cfg.rdp_git_repo_url, + git_branch=cfg.git_branch, + ) else: return AgentUpdateFallbackResult( ok=False, diff --git a/backend/app/services/winrm_connect.py b/backend/app/services/winrm_connect.py index 4d3f02a..5ac8bbc 100644 --- a/backend/app/services/winrm_connect.py +++ b/backend/app/services/winrm_connect.py @@ -289,27 +289,73 @@ def _wrap_powershell_body(body: str) -> str: ) -def _rdp_update_powershell_script(script_path: str) -> str: +def _rdp_update_powershell_script( + script_path: str, + *, + repo_url: str = "", + git_branch: str = "main", +) -> str: script = script_path.strip() if script: literal = _powershell_literal_path(script) body = ( f"$p = '{literal}'\n" "if (-not (Test-Path -LiteralPath $p)) { throw \"Deploy script not found: $p\" }\n" - "& $p" - ) - else: - body = ( - "$candidates = @(\n" - " (Join-Path $env:ProgramData 'RDP-login-monitor\\Deploy-LoginMonitor.ps1'),\n" - " (Join-Path $env:ProgramData 'LoginMonitor\\Deploy-LoginMonitor.ps1'),\n" - " (Join-Path $env:USERPROFILE 'Deploy-LoginMonitor.ps1')\n" - ")\n" - "$p = $candidates | Where-Object { Test-Path -LiteralPath $_ } | Select-Object -First 1\n" - "if (-not $p) { throw 'Deploy-LoginMonitor.ps1 not found on client PC' }\n" "Write-Output \"Running: $p\"\n" "& $p" ) + return _wrap_powershell_body(body) + + safe_repo = _powershell_literal_path( + (repo_url or "https://git.kalinamall.ru/PapaTramp/RDP-login-monitor.git").strip() + ) + safe_branch = _powershell_literal_path((git_branch or "main").strip() or "main") + body = ( + "function Invoke-RdpDeploy {\n" + " param([string]$Path)\n" + " Write-Output \"Running: $Path\"\n" + " & $Path\n" + "}\n" + "$deployCandidates = [System.Collections.Generic.List[string]]::new()\n" + "if ($env:USERDOMAIN) {\n" + " $deployCandidates.Add((Join-Path \"\\\\$($env:USERDOMAIN)\\NETLOGON\\RDP-login-monitor\" 'Deploy-LoginMonitor.ps1'))\n" + "}\n" + "try {\n" + " $dom = (Get-CimInstance Win32_ComputerSystem -ErrorAction Stop).Domain\n" + " if ($dom) {\n" + " $deployCandidates.Add((Join-Path \"\\\\$dom\\NETLOGON\\RDP-login-monitor\" 'Deploy-LoginMonitor.ps1'))\n" + " }\n" + "} catch { }\n" + "foreach ($p in $deployCandidates) {\n" + " if (Test-Path -LiteralPath $p) {\n" + " Invoke-RdpDeploy $p\n" + " exit 0\n" + " }\n" + "}\n" + f"$gitUrl = '{safe_repo}'\n" + f"$branch = '{safe_branch}'\n" + "$repoDir = Join-Path $env:ProgramData 'RDP-login-monitor\\_sac_git'\n" + "$gitCmd = Get-Command git.exe -ErrorAction SilentlyContinue\n" + "if (-not $gitCmd) {\n" + " throw 'Deploy-LoginMonitor.ps1 not on NETLOGON and git.exe missing; set win_agent_update_script in SAC or install Git'\n" + "}\n" + "if (-not (Test-Path -LiteralPath (Join-Path $repoDir '.git'))) {\n" + " if (Test-Path -LiteralPath $repoDir) { Remove-Item -LiteralPath $repoDir -Recurse -Force }\n" + " New-Item -ItemType Directory -Path (Split-Path $repoDir -Parent) -Force | Out-Null\n" + " & git.exe clone -b $branch $gitUrl $repoDir\n" + " if ($LASTEXITCODE -ne 0) { throw \"git clone failed (exit $LASTEXITCODE)\" }\n" + "} else {\n" + " Push-Location $repoDir\n" + " & git.exe fetch origin\n" + " & git.exe reset --hard \"origin/$branch\"\n" + " $code = $LASTEXITCODE\n" + " Pop-Location\n" + " if ($code -ne 0) { throw \"git reset failed (exit $code)\" }\n" + "}\n" + "$deploy = Join-Path $repoDir 'Deploy-LoginMonitor.ps1'\n" + "if (-not (Test-Path -LiteralPath $deploy)) { throw \"Deploy-LoginMonitor.ps1 missing in git checkout: $repoDir\" }\n" + "& $deploy -SourceShareRoot $repoDir\n" + ) return _wrap_powershell_body(body) @@ -319,12 +365,18 @@ def run_winrm_rdp_monitor_update( user: str, password: str, script_path: str = "", + repo_url: str = "", + git_branch: str = "main", ) -> WinRmCmdResult: return run_winrm_ps( target=target, user=user, password=password, - script=_rdp_update_powershell_script(script_path), + script=_rdp_update_powershell_script( + script_path, + repo_url=repo_url, + git_branch=git_branch, + ), timeout_sec=900, ) diff --git a/backend/app/version.py b/backend/app/version.py index 1e1c986..fbfdaff 100644 --- a/backend/app/version.py +++ b/backend/app/version.py @@ -1,5 +1,5 @@ """Единый источник версии SAC (API, health, логи, OpenAPI).""" APP_NAME = "Security Alert Center" -APP_VERSION = "0.20.11" +APP_VERSION = "0.20.12" APP_VERSION_LABEL = f"{APP_NAME} v.{APP_VERSION}" diff --git a/backend/tests/test_winrm_connect.py b/backend/tests/test_winrm_connect.py index d7a3e71..563151f 100644 --- a/backend/tests/test_winrm_connect.py +++ b/backend/tests/test_winrm_connect.py @@ -7,17 +7,29 @@ from app.services.winrm_connect import ( ) -def test_rdp_update_default_script_uses_rdp_login_monitor_path(): +def test_rdp_update_default_script_uses_netlogon_and_git_fallback(): script = _rdp_update_powershell_script("") assert "$ProgressPreference = 'SilentlyContinue'" in script - assert "try {" in script - assert "Write-Output ('ERROR: '" in script - assert "RDP-login-monitor\\Deploy-LoginMonitor.ps1" in script + assert "NETLOGON\\RDP-login-monitor" in script + assert "Deploy-LoginMonitor.ps1" in script + assert "_sac_git" in script + assert "git.kalinamall.ru/PapaTramp/RDP-login-monitor.git" in script + assert "ProgramData\\RDP-login-monitor\\Deploy-LoginMonitor.ps1" not in script + + +def test_rdp_update_default_script_honors_sac_git_settings(): + script = _rdp_update_powershell_script( + "", + repo_url="https://git.example.com/org/rdp.git", + git_branch="release", + ) + assert "git.example.com/org/rdp.git" in script + assert "$branch = 'release'" in script def test_rdp_update_custom_script_uses_literal_path(): - script = _rdp_update_powershell_script(r"C:\ProgramData\RDP-login-monitor\Deploy-LoginMonitor.ps1") - assert r"C:\ProgramData\RDP-login-monitor\Deploy-LoginMonitor.ps1" in script + script = _rdp_update_powershell_script(r"\\b26\NETLOGON\RDP-login-monitor\Deploy-LoginMonitor.ps1") + assert r"\\b26\NETLOGON\RDP-login-monitor\Deploy-LoginMonitor.ps1" in script assert "Test-Path -LiteralPath" in script diff --git a/frontend/src/version.ts b/frontend/src/version.ts index 5defd9a..a0cde1c 100644 --- a/frontend/src/version.ts +++ b/frontend/src/version.ts @@ -1,4 +1,4 @@ /** Fallback до загрузки /health; при релизе держите в sync с backend/app/version.py */ export const APP_NAME = "Security Alert Center"; -export const APP_VERSION = "0.20.11"; +export const APP_VERSION = "0.20.12"; export const APP_VERSION_LABEL = `${APP_NAME} v.${APP_VERSION}`;