Русифицировать DLP PowerShell и исправить имя документа печати

This commit is contained in:
Devin AI
2026-05-02 08:30:37 +00:00
parent f7cf5556a0
commit da99d1ac98
18 changed files with 505 additions and 140 deletions
@@ -5,7 +5,7 @@ function Assert-Administrator {
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = [Security.Principal.WindowsPrincipal]::new($identity)
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
throw 'Run this script from an elevated PowerShell session.'
throw 'Запустите этот скрипт из PowerShell с правами администратора.'
}
}
@@ -64,7 +64,7 @@ function Get-ActivityWatchPackageRoot {
Select-Object -First 1
if (-not $afkBinary) {
throw "Cannot find aw-watcher-afk.exe under $ExpandedRoot."
throw "Не удалось найти aw-watcher-afk.exe в $ExpandedRoot."
}
return (Split-Path -Path (Split-Path -Path $afkBinary.FullName -Parent) -Parent)
@@ -130,7 +130,7 @@ function Get-ActivityWatchExecutableMap {
foreach ($entry in $map.GetEnumerator()) {
if (-not (Test-Path -LiteralPath $entry.Value)) {
throw "Missing required ActivityWatch binary: $($entry.Value)"
throw "Не найден обязательный исполняемый файл ActivityWatch: $($entry.Value)"
}
}
@@ -194,7 +194,7 @@ function Normalize-ActivityWatchUsers {
Sort-Object -Unique
if (-not $normalized -or $normalized.Count -eq 0) {
throw 'No target users resolved. Provide -Users or -UserListPath.'
throw 'Не удалось определить целевых пользователей. Укажите -Users или -UserListPath.'
}
return @($normalized)
@@ -243,6 +243,8 @@ function Copy-ActivityWatchCollectorAssets {
[Parameter(Mandatory = $true)]
[string]$EndpointCollectorScriptSource,
[Parameter(Mandatory = $true)]
[string]$SessionCollectorScriptSource,
[Parameter(Mandatory = $true)]
[string]$ExampleRulesSource,
[Parameter(Mandatory = $true)]
[string]$ExamplePolicySource,
@@ -256,6 +258,7 @@ function Copy-ActivityWatchCollectorAssets {
$collectorTarget = Join-Path $StateRoot 'browser-domains-native-collector.ps1'
$endpointCollectorTarget = Join-Path $StateRoot 'dlp-endpoint-signals-collector.ps1'
$sessionCollectorTarget = Join-Path $StateRoot 'worktime-session-collector.ps1'
$exampleRulesTarget = Join-Path $StateRoot 'web-category-rules.example.json'
$rulesTarget = Join-Path $StateRoot 'web-category-rules.json'
$examplePolicyTarget = Join-Path $StateRoot 'dlp-policy.example.json'
@@ -263,6 +266,7 @@ function Copy-ActivityWatchCollectorAssets {
Copy-Item -LiteralPath $CollectorScriptSource -Destination $collectorTarget -Force
Copy-Item -LiteralPath $EndpointCollectorScriptSource -Destination $endpointCollectorTarget -Force
Copy-Item -LiteralPath $SessionCollectorScriptSource -Destination $sessionCollectorTarget -Force
Copy-Item -LiteralPath $ExampleRulesSource -Destination $exampleRulesTarget -Force
Copy-Item -LiteralPath $ExamplePolicySource -Destination $examplePolicyTarget -Force
@@ -282,6 +286,7 @@ function Copy-ActivityWatchCollectorAssets {
return [pscustomobject]@{
CollectorScript = $collectorTarget
EndpointCollectorScript = $endpointCollectorTarget
SessionCollectorScript = $sessionCollectorTarget
ExampleRules = $exampleRulesTarget
ActiveRules = $rulesTarget
ExamplePolicy = $examplePolicyTarget
@@ -308,6 +313,8 @@ function New-ActivityWatchDeploymentConfig {
[Parameter(Mandatory = $true)]
[string]$EndpointCollectorScript,
[Parameter(Mandatory = $true)]
[string]$SessionCollectorScript,
[Parameter(Mandatory = $true)]
[string]$RulesPath,
[Parameter(Mandatory = $true)]
[string]$PolicyPath,
@@ -349,6 +356,7 @@ function New-ActivityWatchDeploymentConfig {
logsRoot = $LogsRoot
collectorScript = $CollectorScript
endpointCollectorScript = $EndpointCollectorScript
sessionCollectorScript = $SessionCollectorScript
rulesPath = $RulesPath
policyPath = $PolicyPath
launchScript = $LaunchScriptPath
@@ -413,7 +421,7 @@ function Read-ActivityWatchDeploymentConfig {
)
if (-not (Test-Path -LiteralPath $Path)) {
throw "Deployment config not found: $Path"
throw "Конфигурация развёртывания не найдена: $Path"
}
return Get-Content -LiteralPath $Path -Raw | ConvertFrom-Json
@@ -631,6 +639,7 @@ function Start-CollectorScriptIfNeeded {
`$script:KnownBuckets = @{}
`$collectorScript = [string]`$config.paths.collectorScript
`$endpointCollectorScript = if (`$config.paths.PSObject.Properties.Name -contains 'endpointCollectorScript') { [string]`$config.paths.endpointCollectorScript } else { '' }
`$sessionCollectorScript = if (`$config.paths.PSObject.Properties.Name -contains 'sessionCollectorScript') { [string]`$config.paths.sessionCollectorScript } else { '' }
`$afkExe = Join-Path `$installRoot 'aw-watcher-afk\aw-watcher-afk.exe'
`$windowExe = Join-Path `$installRoot 'aw-watcher-window\aw-watcher-window.exe'
`$serverArgs = @('--host', [string]`$config.server.host, '--port', [string]`$config.server.port)
@@ -639,11 +648,11 @@ function Start-CollectorScriptIfNeeded {
`$windowEnabled = if (`$config.PSObject.Properties.Name -contains 'collectors' -and `$config.collectors.PSObject.Properties.Name -contains 'windowEnabled') { [bool]`$config.collectors.windowEnabled } else { `$true }
if (`$afkEnabled -and -not (Test-Path -LiteralPath `$afkExe)) {
throw "Missing aw-watcher-afk.exe: `$afkExe"
throw "Не найден aw-watcher-afk.exe: `$afkExe"
}
if (`$windowEnabled -and -not (Test-Path -LiteralPath `$windowExe)) {
throw "Missing aw-watcher-window.exe: `$windowExe"
throw "Не найден aw-watcher-window.exe: `$windowExe"
}
if (`$afkEnabled -and -not (Test-ProcessInSession -Name 'aw-watcher-afk' -SessionId `$sessionId)) {
@@ -661,6 +670,7 @@ catch {
}
Start-CollectorScriptIfNeeded -ScriptPath `$collectorScript -ConfigPath `$ConfigPath -PowerShellExe `$powershellExe -SessionId `$sessionId
Start-CollectorScriptIfNeeded -ScriptPath `$endpointCollectorScript -ConfigPath `$ConfigPath -PowerShellExe `$powershellExe -SessionId `$sessionId
Start-CollectorScriptIfNeeded -ScriptPath `$sessionCollectorScript -ConfigPath `$ConfigPath -PowerShellExe `$powershellExe -SessionId `$sessionId
"@
Set-Content -LiteralPath $Path -Value $content -Encoding UTF8
@@ -850,7 +860,7 @@ function Set-ActivityWatchScheduledTaskAction {
$taskCommand = ('"{0}" {1}' -f $Execute, $Arguments)
& schtasks.exe /Change /TN $TaskName /TR $taskCommand | Out-Null
if ($LASTEXITCODE -ne 0) {
throw "schtasks.exe /Change failed for $TaskName"
throw "schtasks.exe /Change завершился с ошибкой для $TaskName"
}
}
@@ -951,17 +961,17 @@ function Set-ActivityWatchAcl {
& icacls $InstallRoot /inheritance:r /grant:r '*S-1-5-18:(OI)(CI)(F)' '*S-1-5-32-544:(OI)(CI)(F)' '*S-1-5-32-545:(OI)(CI)(RX)' | Out-Null
if ($LASTEXITCODE -ne 0) {
throw "icacls failed for $InstallRoot"
throw "icacls завершился с ошибкой для $InstallRoot"
}
& icacls $StateRoot /inheritance:r /grant:r '*S-1-5-18:(OI)(CI)(F)' '*S-1-5-32-544:(OI)(CI)(F)' '*S-1-5-32-545:(OI)(CI)(RX)' | Out-Null
if ($LASTEXITCODE -ne 0) {
throw "icacls failed for $StateRoot"
throw "icacls завершился с ошибкой для $StateRoot"
}
& icacls $LogsRoot /inheritance:r /grant:r '*S-1-5-18:(OI)(CI)(F)' '*S-1-5-32-544:(OI)(CI)(F)' '*S-1-5-32-545:(OI)(CI)(M)' | Out-Null
if ($LASTEXITCODE -ne 0) {
throw "icacls failed for $LogsRoot"
throw "icacls завершился с ошибкой для $LogsRoot"
}
}
@@ -49,7 +49,7 @@ function Get-DeploymentConfig {
}
$deploymentConfig = Get-DeploymentConfig -Path $ConfigPath
$resolvedServerHost = if ($ServerHost) { $ServerHost } elseif ($deploymentConfig) { [string]$deploymentConfig.server.host } else { throw 'ServerHost is required.' }
$resolvedServerHost = if ($ServerHost) { $ServerHost } elseif ($deploymentConfig) { [string]$deploymentConfig.server.host } else { throw 'Укажите ServerHost или подготовьте deployment-config.json.' }
$resolvedServerPort = if ($PSBoundParameters.ContainsKey('ServerPort')) { $ServerPort } elseif ($deploymentConfig) { [int]$deploymentConfig.server.port } else { 5600 }
$resolvedServerScheme = if ($ServerScheme) { $ServerScheme } elseif ($deploymentConfig) { [string]$deploymentConfig.server.scheme } else { 'http' }
$resolvedRulesPath = if ($RulesPath) { $RulesPath } elseif ($deploymentConfig) { [string]$deploymentConfig.paths.rulesPath } else { 'C:\ProgramData\ActivityWatch\web-category-rules.json' }
@@ -263,11 +263,11 @@ function Load-CustomCategoryRules {
if ($rules.Count -gt 0) {
$script:CategoryRules = @($rules) + @($script:CategoryRules)
Write-CollectorLog ("custom rules loaded: {0}" -f $rules.Count)
Write-CollectorLog ("пользовательские правила загружены: {0}" -f $rules.Count)
}
}
catch {
Write-CollectorLog ("custom rules load failed: {0}" -f $_.Exception.Message)
Write-CollectorLog ("не удалось загрузить пользовательские правила: {0}" -f $_.Exception.Message)
}
}
@@ -338,7 +338,7 @@ function Load-DlpPolicy {
param([string]$Path)
if (-not $Path -or -not (Test-Path -LiteralPath $Path)) {
Write-CollectorLog ("dlp policy not found, disabled: {0}" -f $Path)
Write-CollectorLog ("DLP-политика не найдена, DLP отключен: {0}" -f $Path)
return
}
@@ -372,7 +372,7 @@ function Load-DlpPolicy {
enabled = if ($rule.PSObject.Properties.Name -contains 'enabled') { [bool]$rule.enabled } else { $true }
action = if ($rule.action) { [string]$rule.action } else { [string]$script:DlpDefaults.action }
severity = if ($rule.severity) { [string]$rule.severity } else { [string]$script:DlpDefaults.severity }
message = if ($rule.message) { [string]$rule.message } else { "DLP rule matched: $($rule.id)" }
message = if ($rule.message) { [string]$rule.message } else { "Сработало DLP-правило: $($rule.id)" }
cooldownSeconds = if ($rule.cooldownSeconds) { [int]$rule.cooldownSeconds } else { [int]$script:DlpDefaults.cooldownSeconds }
when = [pscustomobject]@{
domains = if ($when.PSObject.Properties.Name -contains 'domains') { @($when.domains | ForEach-Object { ([string]$_).Trim().ToLowerInvariant() } | Where-Object { $_ }) } else { @() }
@@ -388,10 +388,10 @@ function Load-DlpPolicy {
}
$script:DlpRules = @($loaded)
Write-CollectorLog ("dlp policy loaded: enabled={0}, rules={1}" -f $script:DlpDefaults.enabled, $script:DlpRules.Count)
Write-CollectorLog ("DLP-политика загружена: включена={0}, правил={1}" -f $script:DlpDefaults.enabled, $script:DlpRules.Count)
}
catch {
Write-CollectorLog ("dlp policy parse failed: {0}" -f $_.Exception.Message)
Write-CollectorLog ("не удалось разобрать DLP-политику: {0}" -f $_.Exception.Message)
}
}
@@ -625,7 +625,7 @@ function Capture-IncidentScreenshot {
}
}
catch {
Write-CollectorLog ("screenshot capture failed: {0}" -f $_.Exception.Message)
Write-CollectorLog ("не удалось сделать снимок инцидента: {0}" -f $_.Exception.Message)
return @{}
}
}
@@ -775,7 +775,7 @@ function Send-CategoryHeartbeat {
Load-CustomCategoryRules -Path $resolvedRulesPath
Load-DlpPolicy -Path $resolvedPolicyPath
Write-CollectorLog ("collector started against {0}" -f $script:ApiBase)
Write-CollectorLog ("коллектор запущен для {0}" -f $script:ApiBase)
while ($true) {
try {
@@ -815,7 +815,7 @@ while ($true) {
}
}
catch {
Write-CollectorLog ("collector error: {0}" -f $_.Exception.Message)
Write-CollectorLog ("ошибка коллектора: {0}" -f $_.Exception.Message)
}
Start-Sleep -Seconds $resolvedPollSeconds
@@ -44,6 +44,7 @@ $launchScriptPath = Join-Path $StateRoot 'launch-watchers.ps1'
$recoveryScriptPath = Join-Path $StateRoot 'recovery-loop.ps1'
$collectorSource = Join-Path $PSScriptRoot 'browser-domains-native-collector.ps1'
$endpointCollectorSource = Join-Path $PSScriptRoot 'dlp-endpoint-signals-collector.ps1'
$sessionCollectorSource = Join-Path $PSScriptRoot 'worktime-session-collector.ps1'
$exampleRulesSource = Join-Path $PSScriptRoot 'web-category-rules.example.json'
$examplePolicySource = Join-Path $PSScriptRoot 'dlp-policy.example.json'
@@ -57,6 +58,7 @@ Get-ActivityWatchExecutableMap -InstallRoot $InstallRoot | Out-Null
$assetResult = Copy-ActivityWatchCollectorAssets `
-CollectorScriptSource $collectorSource `
-EndpointCollectorScriptSource $endpointCollectorSource `
-SessionCollectorScriptSource $sessionCollectorSource `
-ExampleRulesSource $exampleRulesSource `
-ExamplePolicySource $examplePolicySource `
-StateRoot $StateRoot `
@@ -76,6 +78,7 @@ $config = New-ActivityWatchDeploymentConfig `
-LogsRoot $logsRoot `
-CollectorScript $assetResult.CollectorScript `
-EndpointCollectorScript $assetResult.EndpointCollectorScript `
-SessionCollectorScript $assetResult.SessionCollectorScript `
-RulesPath $assetResult.ActiveRules `
-PolicyPath $assetResult.ActivePolicy `
-PollSeconds $PollSeconds `
@@ -100,8 +103,8 @@ Register-ActivityWatchUserTasks -TaskDefinitions $taskDefinitions -LaunchScriptP
Register-ActivityWatchRecoveryTask -TaskName $config.recovery.taskName -RecoveryScriptPath $recoveryScriptPath -ConfigPath $configPath
Start-ActivityWatchTasks -TaskDefinitions $taskDefinitions -RecoveryTaskName $config.recovery.taskName
Write-Host 'ActivityWatch deployed for users:'
Write-Host 'ActivityWatch развёрнут для пользователей:'
$targetUsers | ForEach-Object { Write-Host " - $_" }
Write-Host "Server: ${ServerScheme}://$ServerHost`:$ServerPort"
Write-Host "State root: $StateRoot"
Write-Host "Policy file: $($assetResult.ActivePolicy)"
Write-Host "Сервер: ${ServerScheme}://$ServerHost`:$ServerPort"
Write-Host "Каталог данных: $StateRoot"
Write-Host "Файл DLP-политики: $($assetResult.ActivePolicy)"
@@ -46,7 +46,7 @@ $hardeningScript = Join-Path $PSScriptRoot 'hardening-recovery.ps1'
$validationScript = Join-Path $PSScriptRoot 'validate-deployment.ps1'
if (-not (Test-Path -LiteralPath $deployScript)) {
throw "Missing script: $deployScript"
throw "Не найден скрипт: $deployScript"
}
& $deployScript `
@@ -118,7 +118,7 @@ $report = [ordered]@{
if ($ValidateAfterDeploy) {
if (-not (Test-Path -LiteralPath $validationScript)) {
throw "Missing script: $validationScript"
throw "Не найден скрипт: $validationScript"
}
$validation = & $validationScript -ConfigPath (Join-Path $StateRoot 'deployment-config.json')
@@ -132,6 +132,6 @@ if ($reportDirectory) {
$report | ConvertTo-Json -Depth 12 | Set-Content -LiteralPath $effectiveReportPath -Encoding UTF8
Write-Host 'ActivityWatch ensemble deploy completed.'
Write-Host "Users: $($resolvedUsers -join ', ')"
Write-Host "Report: $effectiveReportPath"
Write-Host 'Комплексное развёртывание ActivityWatch завершено.'
Write-Host "Пользователи: $($resolvedUsers -join ', ')"
Write-Host "Отчёт: $effectiveReportPath"
@@ -42,6 +42,7 @@ $launchScriptPath = Join-Path $StateRoot 'launch-watchers.ps1'
$recoveryScriptPath = Join-Path $StateRoot 'recovery-loop.ps1'
$collectorSource = Join-Path $PSScriptRoot 'browser-domains-native-collector.ps1'
$endpointCollectorSource = Join-Path $PSScriptRoot 'dlp-endpoint-signals-collector.ps1'
$sessionCollectorSource = Join-Path $PSScriptRoot 'worktime-session-collector.ps1'
$exampleRulesSource = Join-Path $PSScriptRoot 'web-category-rules.example.json'
$examplePolicySource = Join-Path $PSScriptRoot 'dlp-policy.example.json'
@@ -55,6 +56,7 @@ Get-ActivityWatchExecutableMap -InstallRoot $InstallRoot | Out-Null
$assetResult = Copy-ActivityWatchCollectorAssets `
-CollectorScriptSource $collectorSource `
-EndpointCollectorScriptSource $endpointCollectorSource `
-SessionCollectorScriptSource $sessionCollectorSource `
-ExampleRulesSource $exampleRulesSource `
-ExamplePolicySource $examplePolicySource `
-StateRoot $StateRoot `
@@ -74,6 +76,7 @@ $config = New-ActivityWatchDeploymentConfig `
-LogsRoot $logsRoot `
-CollectorScript $assetResult.CollectorScript `
-EndpointCollectorScript $assetResult.EndpointCollectorScript `
-SessionCollectorScript $assetResult.SessionCollectorScript `
-RulesPath $assetResult.ActiveRules `
-PolicyPath $assetResult.ActivePolicy `
-PollSeconds $PollSeconds `
@@ -98,9 +101,9 @@ Register-ActivityWatchUserTasks -TaskDefinitions $taskDefinitions -LaunchScriptP
Register-ActivityWatchRecoveryTask -TaskName $config.recovery.taskName -RecoveryScriptPath $recoveryScriptPath -ConfigPath $configPath
Start-ActivityWatchTasks -TaskDefinitions $taskDefinitions -RecoveryTaskName $config.recovery.taskName
Write-Host "ActivityWatch deployed for $TargetUser"
Write-Host "Server: ${ServerScheme}://$ServerHost`:$ServerPort"
Write-Host "Install root: $InstallRoot"
Write-Host "State root: $StateRoot"
Write-Host "Rules file: $($assetResult.ActiveRules)"
Write-Host "Policy file: $($assetResult.ActivePolicy)"
Write-Host "ActivityWatch развёрнут для пользователя: $TargetUser"
Write-Host "Сервер: ${ServerScheme}://$ServerHost`:$ServerPort"
Write-Host "Каталог установки: $InstallRoot"
Write-Host "Каталог данных: $StateRoot"
Write-Host "Файл правил: $($assetResult.ActiveRules)"
Write-Host "Файл DLP-политики: $($assetResult.ActivePolicy)"
@@ -210,7 +210,7 @@ function Capture-IncidentScreenshot {
}
}
catch {
Write-EndpointLog ("screenshot capture failed: {0}" -f $_.Exception.Message)
Write-EndpointLog ("не удалось сделать снимок инцидента: {0}" -f $_.Exception.Message)
return @{}
}
}
@@ -246,7 +246,7 @@ function Load-DlpPolicy {
}
if (-not $Path -or -not (Test-Path -LiteralPath $Path)) {
Write-EndpointLog ("policy not found, using defaults: {0}" -f $Path)
Write-EndpointLog ("DLP-политика не найдена, используются значения по умолчанию: {0}" -f $Path)
return
}
@@ -266,7 +266,7 @@ function Load-DlpPolicy {
}
}
catch {
Write-EndpointLog ("policy parse failed: {0}" -f $_.Exception.Message)
Write-EndpointLog ("не удалось разобрать DLP-политику: {0}" -f $_.Exception.Message)
}
}
@@ -319,13 +319,13 @@ function Evaluate-ClipboardRules {
$action = if ($rule.action) { [string]$rule.action } else { [string]$script:Policy.defaults.action }
$severity = if ($rule.severity) { [string]$rule.severity } else { [string]$script:Policy.defaults.severity }
$message = if ($rule.message) { [string]$rule.message } else { "Clipboard rule matched: $ruleId" }
$message = if ($rule.message) { [string]$rule.message } else { "Сработало правило буфера обмена: $ruleId" }
Send-DlpIncidentHeartbeat -RuleId $ruleId -Action $action -Severity $severity -Message $message -SignalType 'clipboard' -Data @{
clipboardHash = $ClipboardHash
clipboardLength = $ClipboardText.Length
}
Write-EndpointLog ("incident clipboard rule={0} action={1} severity={2}" -f $ruleId, $action, $severity)
Write-EndpointLog ("инцидент буфера обмена правило={0} действие={1} важность={2}" -f $ruleId, $action, $severity)
}
}
@@ -347,13 +347,13 @@ function Evaluate-UsbRules {
$action = if ($rule.action) { [string]$rule.action } else { [string]$script:Policy.defaults.action }
$severity = if ($rule.severity) { [string]$rule.severity } else { [string]$script:Policy.defaults.severity }
$message = if ($rule.message) { [string]$rule.message } else { "USB rule matched: $ruleId" }
$message = if ($rule.message) { [string]$rule.message } else { "Сработало правило USB-носителя: $ruleId" }
Send-DlpIncidentHeartbeat -RuleId $ruleId -Action $action -Severity $severity -Message $message -SignalType 'usb_insert' -Data @{
driveLetter = $DriveLetter
volumeName = $VolumeName
}
Write-EndpointLog ("incident usb rule={0} action={1} severity={2} drive={3}" -f $ruleId, $action, $severity, $DriveLetter)
Write-EndpointLog ("инцидент USB правило={0} действие={1} важность={2} диск={3}" -f $ruleId, $action, $severity, $DriveLetter)
}
}
@@ -385,14 +385,14 @@ function Evaluate-PrintRules {
$action = if ($rule.action) { [string]$rule.action } else { [string]$script:Policy.defaults.action }
$severity = if ($rule.severity) { [string]$rule.severity } else { [string]$script:Policy.defaults.severity }
$message = if ($rule.message) { [string]$rule.message } else { "Print rule matched: $ruleId" }
$message = if ($rule.message) { [string]$rule.message } else { "Сработало правило печати: $ruleId" }
Send-DlpIncidentHeartbeat -RuleId $ruleId -Action $action -Severity $severity -Message $message -SignalType 'print_job' -Data @{
printerName = $PrinterName
documentName = $DocumentName
owner = $Owner
}
Write-EndpointLog ("incident print rule={0} action={1} severity={2} printer={3}" -f $ruleId, $action, $severity, $PrinterName)
Write-EndpointLog ("инцидент печати правило={0} действие={1} важность={2} принтер={3}" -f $ruleId, $action, $severity, $PrinterName)
}
}
@@ -402,6 +402,52 @@ function Test-LooksLikeMojibakeQuestionMarks {
return $Value -match '\?{2,}'
}
function Test-DocumentNameNeedsFallback {
param([AllowNull()][string]$Value)
if ([string]::IsNullOrWhiteSpace($Value)) { return $true }
$trimmed = $Value.Trim()
if (Test-LooksLikeMojibakeQuestionMarks -Value $trimmed) { return $true }
if ($trimmed -match '^[0-9]+$') { return $true }
if ($trimmed -match '^(?i)(print document|document|local downlevel document)$') { return $true }
return $false
}
function Get-EventXmlValue {
param(
[Parameter(Mandatory = $true)][xml]$EventXml,
[Parameter(Mandatory = $true)][string]$Name
)
$node = $EventXml.Event.UserData.DocumentPrinted.$Name
if ($null -ne $node) {
return [string]$node
}
return ''
}
function Get-PrintJobPrinterName {
param(
[AllowNull()][string]$JobName,
[AllowNull()][string]$FallbackPrinterName
)
if ([string]::IsNullOrWhiteSpace($JobName)) {
if (-not [string]::IsNullOrWhiteSpace($FallbackPrinterName)) {
return $FallbackPrinterName.Trim()
}
return ''
}
$parts = $JobName -split ',', 2
if ($parts.Count -gt 0 -and -not [string]::IsNullOrWhiteSpace($parts[0])) {
return $parts[0].Trim()
}
return $JobName.Trim()
}
function Normalize-OwnerForMatch {
param([AllowNull()][string]$Value)
if ([string]::IsNullOrWhiteSpace($Value)) { return '' }
@@ -469,13 +515,50 @@ function Get-PrintServiceEventSummary {
$propertyValues += [string]$prop.Value
}
$xml = $null
try {
$xml = [xml]$Event.ToXml()
}
catch {
}
$jobId = ''
$documentName = ''
$owner = ''
$portName = ''
$printerName = ''
$sizeBytes = ''
$pageCount = ''
if ($xml) {
$jobId = Get-EventXmlValue -EventXml $xml -Name 'Param1'
$documentName = Get-EventXmlValue -EventXml $xml -Name 'Param2'
$owner = Get-EventXmlValue -EventXml $xml -Name 'Param3'
$portName = Get-EventXmlValue -EventXml $xml -Name 'Param4'
$printerName = Get-EventXmlValue -EventXml $xml -Name 'Param5'
$sizeBytes = Get-EventXmlValue -EventXml $xml -Name 'Param7'
$pageCount = Get-EventXmlValue -EventXml $xml -Name 'Param8'
}
if ([string]::IsNullOrWhiteSpace($jobId) -and $props.Count -ge 1) { $jobId = [string]$props[0].Value }
if ([string]::IsNullOrWhiteSpace($documentName) -and $props.Count -ge 2) { $documentName = [string]$props[1].Value }
if ([string]::IsNullOrWhiteSpace($owner) -and $props.Count -ge 3) { $owner = [string]$props[2].Value }
if ([string]::IsNullOrWhiteSpace($portName) -and $props.Count -ge 4) { $portName = [string]$props[3].Value }
if ([string]::IsNullOrWhiteSpace($printerName) -and $props.Count -ge 5) { $printerName = [string]$props[4].Value }
if ([string]::IsNullOrWhiteSpace($sizeBytes) -and $props.Count -ge 7) { $sizeBytes = [string]$props[6].Value }
if ([string]::IsNullOrWhiteSpace($pageCount) -and $props.Count -ge 8) { $pageCount = [string]$props[7].Value }
[pscustomobject]@{
RecordId = [string]$Event.RecordId
TimeCreated = if ($Event.TimeCreated) { $Event.TimeCreated.ToString('o') } else { '' }
PropertyCount = $props.Count
DocumentName = if ($props.Count -ge 1) { [string]$props[0].Value } else { '' }
Owner = if ($props.Count -ge 2) { [string]$props[1].Value } else { '' }
PrinterName = if ($props.Count -ge 4) { [string]$props[3].Value } else { '' }
JobId = $jobId
DocumentName = $documentName
Owner = $owner
PortName = $portName
PrinterName = $printerName
SizeBytes = $sizeBytes
PageCount = $pageCount
PropertyValues = $propertyValues
}
}
@@ -488,7 +571,7 @@ function Get-PrintServiceDocumentFallback {
)
$preferred = [string]$EventSummary.DocumentName
if (-not (Test-LooksLikeMojibakeQuestionMarks -Value $preferred) -and $preferred -notmatch '^[0-9]+$') {
if (-not (Test-DocumentNameNeedsFallback -Value $preferred)) {
return $preferred
}
@@ -499,9 +582,10 @@ function Get-PrintServiceDocumentFallback {
$candidate = [string]$value
if ([string]::IsNullOrWhiteSpace($candidate)) { continue }
if ($candidate -eq $preferred) { continue }
if ($EventSummary.JobId -and $candidate -eq [string]$EventSummary.JobId) { continue }
if ($Owner -and $candidate -like "*$Owner*") { continue }
if ($PrinterName -and $candidate -like "*$PrinterName*") { continue }
if (Test-LooksLikeMojibakeQuestionMarks -Value $candidate) { continue }
if (Test-DocumentNameNeedsFallback -Value $candidate) { continue }
if ($candidate -match '[\\/:]' -and $candidate -match '\.[A-Za-z0-9]{1,8}$') {
$pathCandidates.Add($candidate)
@@ -546,7 +630,7 @@ function Write-PrintServiceEventTrace {
}
Write-EndpointLog (
'printservice-307 phase={0} recordId={1} time={2} owner={3} printer={4} document={5} resolved={6} properties=[{7}] reason={8}' -f
'printservice-307 этап={0} recordId={1} время={2} владелец={3} принтер={4} документ={5} итоговыйДокумент={6} свойства=[{7}] причина={8}' -f
$Phase,
$EventSummary.RecordId,
$EventSummary.TimeCreated,
@@ -561,6 +645,7 @@ function Write-PrintServiceEventTrace {
function Get-BetterDocumentNameFromPrintServiceEvents {
param(
[string]$JobId,
[string]$Owner,
[string]$PrinterName
)
@@ -578,32 +663,41 @@ function Get-BetterDocumentNameFromPrintServiceEvents {
$summary = Get-PrintServiceEventSummary -Event $event
$resolvedDocument = Get-PrintServiceDocumentFallback -EventSummary $summary -Owner $Owner -PrinterName $PrinterName
$jobMatches = if ($JobId) { [string]$summary.JobId -eq [string]$JobId } else { $true }
$ownerMatches = if ($Owner) { Test-OwnerLooseMatch -Expected $Owner -Actual $summary.Owner } else { $true }
$printerMatches = if ($PrinterName) { Test-PrinterLooseMatch -Expected $PrinterName -Actual $summary.PrinterName } else { $true }
if ($pass -eq 'strict') {
if ($JobId -and -not $jobMatches) {
Write-PrintServiceEventTrace -EventSummary $summary -Phase 'scan' -MatchReason 'несовпадение-jobid-strict' -ResolvedDocument $resolvedDocument
continue
}
if ($Owner -and -not $ownerMatches) {
Write-PrintServiceEventTrace -EventSummary $summary -Phase 'scan' -MatchReason 'owner-mismatch-strict' -ResolvedDocument $resolvedDocument
Write-PrintServiceEventTrace -EventSummary $summary -Phase 'scan' -MatchReason 'несовпадение-владельца-strict' -ResolvedDocument $resolvedDocument
continue
}
if ($PrinterName -and -not $printerMatches) {
Write-PrintServiceEventTrace -EventSummary $summary -Phase 'scan' -MatchReason 'printer-mismatch-strict' -ResolvedDocument $resolvedDocument
Write-PrintServiceEventTrace -EventSummary $summary -Phase 'scan' -MatchReason 'несовпадение-принтера-strict' -ResolvedDocument $resolvedDocument
continue
}
}
else {
if ($Owner -and $PrinterName -and -not $ownerMatches -and -not $printerMatches) {
Write-PrintServiceEventTrace -EventSummary $summary -Phase 'scan' -MatchReason 'owner-and-printer-mismatch-relaxed' -ResolvedDocument $resolvedDocument
if ($JobId -and (-not $jobMatches) -and $Owner -and $PrinterName -and -not $ownerMatches -and -not $printerMatches) {
Write-PrintServiceEventTrace -EventSummary $summary -Phase 'scan' -MatchReason 'несовпадение-владельца-и-принтера-relaxed' -ResolvedDocument $resolvedDocument
continue
}
if ((-not $JobId) -and $Owner -and $PrinterName -and -not $ownerMatches -and -not $printerMatches) {
Write-PrintServiceEventTrace -EventSummary $summary -Phase 'scan' -MatchReason 'несовпадение-владельца-и-принтера-relaxed' -ResolvedDocument $resolvedDocument
continue
}
}
if ([string]::IsNullOrWhiteSpace($resolvedDocument)) {
Write-PrintServiceEventTrace -EventSummary $summary -Phase 'scan' -MatchReason ('no-document-candidate-' + $pass) -ResolvedDocument ''
Write-PrintServiceEventTrace -EventSummary $summary -Phase 'scan' -MatchReason ('нет-кандидата-документа-' + $pass) -ResolvedDocument ''
continue
}
$matchReasonBase = if (Test-LooksLikeMojibakeQuestionMarks -Value $summary.DocumentName) { 'fallback-used' } else { 'direct' }
$matchReasonBase = if (Test-DocumentNameNeedsFallback -Value $summary.DocumentName) { 'использован-резервный-вариант' } else { 'напрямую' }
Write-PrintServiceEventTrace -EventSummary $summary -Phase 'selected' -MatchReason ($matchReasonBase + '-' + $pass) -ResolvedDocument $resolvedDocument
return $resolvedDocument
}
@@ -616,7 +710,7 @@ function Get-BetterDocumentNameFromPrintServiceEvents {
}
$deploymentConfig = Get-DeploymentConfig -Path $ConfigPath
$resolvedServerHost = if ($ServerHost) { $ServerHost } elseif ($deploymentConfig) { [string]$deploymentConfig.server.host } else { throw 'ServerHost is required.' }
$resolvedServerHost = if ($ServerHost) { $ServerHost } elseif ($deploymentConfig) { [string]$deploymentConfig.server.host } else { throw 'Укажите ServerHost или подготовьте deployment-config.json.' }
$resolvedServerPort = if ($PSBoundParameters.ContainsKey('ServerPort')) { $ServerPort } elseif ($deploymentConfig) { [int]$deploymentConfig.server.port } else { 5600 }
$resolvedServerScheme = if ($ServerScheme) { $ServerScheme } elseif ($deploymentConfig) { [string]$deploymentConfig.server.scheme } else { 'http' }
$resolvedPolicyPath = if ($PolicyPath) { $PolicyPath } elseif ($deploymentConfig -and $deploymentConfig.paths.PSObject.Properties.Name -contains 'policyPath') { [string]$deploymentConfig.paths.policyPath } else { 'C:\ProgramData\ActivityWatch\dlp-policy.json' }
@@ -648,7 +742,7 @@ $script:IncidentScreenshotEnabled = $resolvedIncidentScreenshotEnabled
$script:ScreenshotTypesLoaded = $false
Load-DlpPolicy -Path $resolvedPolicyPath
Write-EndpointLog ("endpoint collector started against {0}" -f $script:ApiBase)
Write-EndpointLog ("endpoint-коллектор запущен для {0}" -f $script:ApiBase)
while ($true) {
try {
@@ -709,13 +803,13 @@ while ($true) {
if ($script:SeenPrintJob.ContainsKey($jobId)) { continue }
$script:SeenPrintJob[$jobId] = (Get-Date).ToUniversalTime()
$printerName = [string]$job.Name
$printerName = Get-PrintJobPrinterName -JobName ([string]$job.Name) -FallbackPrinterName ([string]$job.DriverName)
$documentName = [string]$job.Document
$owner = [string]$job.Owner
$documentNameOriginal = $documentName
if (Test-LooksLikeMojibakeQuestionMarks -Value $documentName) {
$eventDocumentName = Get-BetterDocumentNameFromPrintServiceEvents -Owner $owner -PrinterName $printerName
if (Test-DocumentNameNeedsFallback -Value $documentName) {
$eventDocumentName = Get-BetterDocumentNameFromPrintServiceEvents -JobId $jobId -Owner $owner -PrinterName $printerName
if ($eventDocumentName) {
$documentName = $eventDocumentName
}
@@ -726,6 +820,7 @@ while ($true) {
documentName = $documentName
documentNameOriginal = $documentNameOriginal
owner = $owner
printJobId = $jobId
}
Evaluate-PrintRules -PrinterName $printerName -DocumentName $documentName -Owner $owner
}
@@ -789,7 +884,7 @@ while ($true) {
}
}
catch {
Write-EndpointLog ("collector error: {0}" -f $_.Exception.Message)
Write-EndpointLog ("ошибка коллектора: {0}" -f $_.Exception.Message)
}
Start-Sleep -Seconds $resolvedPollSeconds
@@ -42,7 +42,7 @@ if (Test-Path -LiteralPath $ConfigPath) {
}
if (-not $existingConfig -and (-not $ServerHost)) {
throw 'deployment-config.json is missing. Provide -ServerHost and user parameters, or run a deploy script first.'
throw 'deployment-config.json отсутствует. Укажите -ServerHost и параметры пользователей либо сначала выполните скрипт развёртывания.'
}
$effectiveStateRoot = if ($StateRoot) { $StateRoot } elseif ($existingConfig) { [string]$existingConfig.paths.stateRoot } else { 'C:\ProgramData\ActivityWatch' }
@@ -78,7 +78,7 @@ elseif ($existingConfig) {
@($existingConfig.userTasks | ForEach-Object { [string]$_.userId })
}
else {
throw 'Target users are missing.'
throw 'Не указаны целевые пользователи.'
}
New-ActivityWatchDirectory -Path $effectiveStateRoot
@@ -96,6 +96,7 @@ Get-ActivityWatchExecutableMap -InstallRoot $effectiveInstallRoot | Out-Null
$assetResult = Copy-ActivityWatchCollectorAssets `
-CollectorScriptSource (Join-Path $PSScriptRoot 'browser-domains-native-collector.ps1') `
-EndpointCollectorScriptSource (Join-Path $PSScriptRoot 'dlp-endpoint-signals-collector.ps1') `
-SessionCollectorScriptSource (Join-Path $PSScriptRoot 'worktime-session-collector.ps1') `
-ExampleRulesSource (Join-Path $PSScriptRoot 'web-category-rules.example.json') `
-ExamplePolicySource (Join-Path $PSScriptRoot 'dlp-policy.example.json') `
-StateRoot $effectiveStateRoot `
@@ -139,6 +140,6 @@ Register-ActivityWatchUserTasks -TaskDefinitions $taskDefinitions -LaunchScriptP
Register-ActivityWatchRecoveryTask -TaskName $config.recovery.taskName -RecoveryScriptPath $effectiveRecoveryScript -ConfigPath $effectiveConfigPath
Start-ActivityWatchTasks -TaskDefinitions $taskDefinitions -RecoveryTaskName $config.recovery.taskName
Write-Host 'ActivityWatch hardening/recovery completed.'
Write-Host "Config: $effectiveConfigPath"
Write-Host "Users repaired: $($effectiveUsers -join ', ')"
Write-Host 'Укрепление и восстановление ActivityWatch завершены.'
Write-Host "Конфигурация: $effectiveConfigPath"
Write-Host "Пользователи восстановлены: $($effectiveUsers -join ', ')"
@@ -14,6 +14,7 @@ $installRoot = [string]$config.paths.installRoot
$stateRoot = [string]$config.paths.stateRoot
$collectorScript = [string]$config.paths.collectorScript
$endpointCollectorScript = if ($config.paths.PSObject.Properties.Name -contains 'endpointCollectorScript') { [string]$config.paths.endpointCollectorScript } else { Join-Path $stateRoot 'dlp-endpoint-signals-collector.ps1' }
$sessionCollectorScript = if ($config.paths.PSObject.Properties.Name -contains 'sessionCollectorScript') { [string]$config.paths.sessionCollectorScript } else { Join-Path $stateRoot 'worktime-session-collector.ps1' }
$rulesPath = [string]$config.paths.rulesPath
$policyPath = if ($config.paths.PSObject.Properties.Name -contains 'policyPath') { [string]$config.paths.policyPath } else { Join-Path $stateRoot 'dlp-policy.json' }
$launchScript = [string]$config.paths.launchScript
@@ -24,6 +25,7 @@ $requiredFiles = @(
(Join-Path $installRoot 'aw-watcher-window\aw-watcher-window.exe'),
$collectorScript,
$endpointCollectorScript,
$sessionCollectorScript,
$rulesPath,
$policyPath,
$launchScript,
@@ -37,6 +39,12 @@ $missingFiles = @(
$processNames = @('aw-watcher-afk', 'aw-watcher-window')
$runningProcesses = Get-Process -Name $processNames -ErrorAction SilentlyContinue | Select-Object Name, Id, SessionId
$sessionCollectorProcesses = Get-CimInstance Win32_Process -ErrorAction SilentlyContinue |
Where-Object {
($_.Name -ieq 'powershell.exe' -or $_.Name -ieq 'pwsh.exe') -and
$_.CommandLine -match [Regex]::Escape($sessionCollectorScript)
} |
Select-Object Name, ProcessId, SessionId, CommandLine
$taskNames = @()
if ($config.userTasks) {
@@ -57,7 +65,7 @@ $tasks = foreach ($taskName in $taskNames) {
else {
[pscustomobject]@{
taskName = $taskName
state = 'Missing'
state = 'Отсутствует'
present = $false
}
}
@@ -81,7 +89,11 @@ $result = [ordered]@{
}
processes = [ordered]@{
list = @($runningProcesses)
ok = [bool](($runningProcesses | Select-Object -ExpandProperty Name -Unique).Count -ge 2)
sessionCollectors = @($sessionCollectorProcesses)
ok = [bool](
(($runningProcesses | Select-Object -ExpandProperty Name -Unique).Count -ge 2) -and
($sessionCollectorProcesses.Count -ge 1)
)
}
}
@@ -0,0 +1,145 @@
param(
[string]$ConfigPath = 'C:\ProgramData\ActivityWatch\deployment-config.json',
[string]$Hostname,
[int]$PollSeconds = 30
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
function Get-Config {
param([string]$Path)
if (-not (Test-Path -LiteralPath $Path)) {
throw "Конфигурация не найдена: $Path"
}
Get-Content -LiteralPath $Path -Raw | ConvertFrom-Json
}
function Invoke-AwJsonPost {
param(
[Parameter(Mandatory = $true)][string]$Uri,
[Parameter(Mandatory = $true)][string]$Json
)
$bytes = [Text.Encoding]::UTF8.GetBytes($Json)
Invoke-RestMethod -Method Post -Uri $Uri -ContentType 'application/json; charset=utf-8' -Body $bytes | Out-Null
}
function Ensure-Bucket {
param(
[Parameter(Mandatory = $true)][string]$ApiBase,
[Parameter(Mandatory = $true)][string]$BucketId,
[Parameter(Mandatory = $true)][string]$HostnameValue
)
try {
Invoke-RestMethod -Method Get -Uri "$ApiBase/buckets/$BucketId" | Out-Null
return
}
catch {
}
$body = @{
client = 'aw-worktime-session-collector'
type = 'aw.worktime.session'
hostname = $HostnameValue
} | ConvertTo-Json -Compress
Invoke-AwJsonPost -Uri "$ApiBase/buckets/$BucketId" -Json $body
}
function Get-SessionRecords {
$records = @()
try {
$lines = quser 2>$null
if (-not $lines) {
return @()
}
foreach ($line in ($lines | Select-Object -Skip 1)) {
$clean = ($line -replace '^\s*>?', '').Trim()
if (-not $clean) {
continue
}
$parts = $clean -split '\s+'
if ($parts.Count -lt 4) {
continue
}
$sessionId = 0
if ($parts[2] -match '^\d+$') {
$sessionId = [int]$parts[2]
}
$records += [pscustomobject]@{
username = $parts[0]
sessionName = $parts[1]
sessionId = $sessionId
state = $parts[3]
}
}
}
catch {
}
return $records
}
$cfg = Get-Config -Path $ConfigPath
$hostValue = if ($Hostname) { $Hostname } else { [string]$env:COMPUTERNAME }
$apiBase = '{0}://{1}:{2}/api/0' -f [string]$cfg.server.scheme, [string]$cfg.server.host, [string]$cfg.server.port
$bucketId = 'aw-worktime-sessions_' + $hostValue
$pulse = 120
$sleepSec = if ($PollSeconds -gt 0) {
$PollSeconds
}
elseif ($cfg.collector -and $cfg.collector.pollSeconds) {
[int]$cfg.collector.pollSeconds
}
else {
30
}
Ensure-Bucket -ApiBase $apiBase -BucketId $bucketId -HostnameValue $hostValue
while ($true) {
$now = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fffZ')
$records = Get-SessionRecords
if (-not $records -or $records.Count -eq 0) {
$records = @([pscustomobject]@{
username = $env:USERNAME
sessionName = ''
sessionId = (Get-Process -Id $PID).SessionId
state = 'Unknown'
})
}
foreach ($rec in $records) {
$payload = @{
timestamp = $now
duration = 0
data = @{
username = [string]$rec.username
userId = "$($env:USERDOMAIN)\$($rec.username)"
sessionId = [int]$rec.sessionId
sessionName = [string]$rec.sessionName
state = [string]$rec.state
active = ($rec.state -match 'Active')
hostname = $hostValue
source = 'worktime-session-collector'
}
} | ConvertTo-Json -Depth 6 -Compress
try {
Invoke-AwJsonPost -Uri "$apiBase/buckets/$bucketId/heartbeat?pulsetime=$pulse" -Json $payload
}
catch {
}
}
Start-Sleep -Seconds $sleepSec
}
+11 -11
View File
@@ -5,7 +5,7 @@ function Assert-Administrator {
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = [Security.Principal.WindowsPrincipal]::new($identity)
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
throw 'Run this script from an elevated PowerShell session.'
throw 'Запустите этот скрипт из PowerShell с правами администратора.'
}
}
@@ -64,7 +64,7 @@ function Get-ActivityWatchPackageRoot {
Select-Object -First 1
if (-not $afkBinary) {
throw "Cannot find aw-watcher-afk.exe under $ExpandedRoot."
throw "Не удалось найти aw-watcher-afk.exe в $ExpandedRoot."
}
return (Split-Path -Path (Split-Path -Path $afkBinary.FullName -Parent) -Parent)
@@ -130,7 +130,7 @@ function Get-ActivityWatchExecutableMap {
foreach ($entry in $map.GetEnumerator()) {
if (-not (Test-Path -LiteralPath $entry.Value)) {
throw "Missing required ActivityWatch binary: $($entry.Value)"
throw "Не найден обязательный исполняемый файл ActivityWatch: $($entry.Value)"
}
}
@@ -194,7 +194,7 @@ function Normalize-ActivityWatchUsers {
Sort-Object -Unique
if (-not $normalized -or $normalized.Count -eq 0) {
throw 'No target users resolved. Provide -Users or -UserListPath.'
throw 'Не удалось определить целевых пользователей. Укажите -Users или -UserListPath.'
}
return @($normalized)
@@ -421,7 +421,7 @@ function Read-ActivityWatchDeploymentConfig {
)
if (-not (Test-Path -LiteralPath $Path)) {
throw "Deployment config not found: $Path"
throw "Конфигурация развёртывания не найдена: $Path"
}
return Get-Content -LiteralPath $Path -Raw | ConvertFrom-Json
@@ -648,11 +648,11 @@ function Start-CollectorScriptIfNeeded {
`$windowEnabled = if (`$config.PSObject.Properties.Name -contains 'collectors' -and `$config.collectors.PSObject.Properties.Name -contains 'windowEnabled') { [bool]`$config.collectors.windowEnabled } else { `$true }
if (`$afkEnabled -and -not (Test-Path -LiteralPath `$afkExe)) {
throw "Missing aw-watcher-afk.exe: `$afkExe"
throw "Не найден aw-watcher-afk.exe: `$afkExe"
}
if (`$windowEnabled -and -not (Test-Path -LiteralPath `$windowExe)) {
throw "Missing aw-watcher-window.exe: `$windowExe"
throw "Не найден aw-watcher-window.exe: `$windowExe"
}
if (`$afkEnabled -and -not (Test-ProcessInSession -Name 'aw-watcher-afk' -SessionId `$sessionId)) {
@@ -860,7 +860,7 @@ function Set-ActivityWatchScheduledTaskAction {
$taskCommand = ('"{0}" {1}' -f $Execute, $Arguments)
& schtasks.exe /Change /TN $TaskName /TR $taskCommand | Out-Null
if ($LASTEXITCODE -ne 0) {
throw "schtasks.exe /Change failed for $TaskName"
throw "schtasks.exe /Change завершился с ошибкой для $TaskName"
}
}
@@ -961,17 +961,17 @@ function Set-ActivityWatchAcl {
& icacls $InstallRoot /inheritance:r /grant:r '*S-1-5-18:(OI)(CI)(F)' '*S-1-5-32-544:(OI)(CI)(F)' '*S-1-5-32-545:(OI)(CI)(RX)' | Out-Null
if ($LASTEXITCODE -ne 0) {
throw "icacls failed for $InstallRoot"
throw "icacls завершился с ошибкой для $InstallRoot"
}
& icacls $StateRoot /inheritance:r /grant:r '*S-1-5-18:(OI)(CI)(F)' '*S-1-5-32-544:(OI)(CI)(F)' '*S-1-5-32-545:(OI)(CI)(RX)' | Out-Null
if ($LASTEXITCODE -ne 0) {
throw "icacls failed for $StateRoot"
throw "icacls завершился с ошибкой для $StateRoot"
}
& icacls $LogsRoot /inheritance:r /grant:r '*S-1-5-18:(OI)(CI)(F)' '*S-1-5-32-544:(OI)(CI)(F)' '*S-1-5-32-545:(OI)(CI)(M)' | Out-Null
if ($LASTEXITCODE -ne 0) {
throw "icacls failed for $LogsRoot"
throw "icacls завершился с ошибкой для $LogsRoot"
}
}
+10 -10
View File
@@ -49,7 +49,7 @@ function Get-DeploymentConfig {
}
$deploymentConfig = Get-DeploymentConfig -Path $ConfigPath
$resolvedServerHost = if ($ServerHost) { $ServerHost } elseif ($deploymentConfig) { [string]$deploymentConfig.server.host } else { throw 'ServerHost is required.' }
$resolvedServerHost = if ($ServerHost) { $ServerHost } elseif ($deploymentConfig) { [string]$deploymentConfig.server.host } else { throw 'Укажите ServerHost или подготовьте deployment-config.json.' }
$resolvedServerPort = if ($PSBoundParameters.ContainsKey('ServerPort')) { $ServerPort } elseif ($deploymentConfig) { [int]$deploymentConfig.server.port } else { 5600 }
$resolvedServerScheme = if ($ServerScheme) { $ServerScheme } elseif ($deploymentConfig) { [string]$deploymentConfig.server.scheme } else { 'http' }
$resolvedRulesPath = if ($RulesPath) { $RulesPath } elseif ($deploymentConfig) { [string]$deploymentConfig.paths.rulesPath } else { 'C:\ProgramData\ActivityWatch\web-category-rules.json' }
@@ -263,11 +263,11 @@ function Load-CustomCategoryRules {
if ($rules.Count -gt 0) {
$script:CategoryRules = @($rules) + @($script:CategoryRules)
Write-CollectorLog ("custom rules loaded: {0}" -f $rules.Count)
Write-CollectorLog ("пользовательские правила загружены: {0}" -f $rules.Count)
}
}
catch {
Write-CollectorLog ("custom rules load failed: {0}" -f $_.Exception.Message)
Write-CollectorLog ("не удалось загрузить пользовательские правила: {0}" -f $_.Exception.Message)
}
}
@@ -338,7 +338,7 @@ function Load-DlpPolicy {
param([string]$Path)
if (-not $Path -or -not (Test-Path -LiteralPath $Path)) {
Write-CollectorLog ("dlp policy not found, disabled: {0}" -f $Path)
Write-CollectorLog ("DLP-политика не найдена, DLP отключен: {0}" -f $Path)
return
}
@@ -372,7 +372,7 @@ function Load-DlpPolicy {
enabled = if ($rule.PSObject.Properties.Name -contains 'enabled') { [bool]$rule.enabled } else { $true }
action = if ($rule.action) { [string]$rule.action } else { [string]$script:DlpDefaults.action }
severity = if ($rule.severity) { [string]$rule.severity } else { [string]$script:DlpDefaults.severity }
message = if ($rule.message) { [string]$rule.message } else { "DLP rule matched: $($rule.id)" }
message = if ($rule.message) { [string]$rule.message } else { "Сработало DLP-правило: $($rule.id)" }
cooldownSeconds = if ($rule.cooldownSeconds) { [int]$rule.cooldownSeconds } else { [int]$script:DlpDefaults.cooldownSeconds }
when = [pscustomobject]@{
domains = if ($when.PSObject.Properties.Name -contains 'domains') { @($when.domains | ForEach-Object { ([string]$_).Trim().ToLowerInvariant() } | Where-Object { $_ }) } else { @() }
@@ -388,10 +388,10 @@ function Load-DlpPolicy {
}
$script:DlpRules = @($loaded)
Write-CollectorLog ("dlp policy loaded: enabled={0}, rules={1}" -f $script:DlpDefaults.enabled, $script:DlpRules.Count)
Write-CollectorLog ("DLP-политика загружена: включена={0}, правил={1}" -f $script:DlpDefaults.enabled, $script:DlpRules.Count)
}
catch {
Write-CollectorLog ("dlp policy parse failed: {0}" -f $_.Exception.Message)
Write-CollectorLog ("не удалось разобрать DLP-политику: {0}" -f $_.Exception.Message)
}
}
@@ -625,7 +625,7 @@ function Capture-IncidentScreenshot {
}
}
catch {
Write-CollectorLog ("screenshot capture failed: {0}" -f $_.Exception.Message)
Write-CollectorLog ("не удалось сделать снимок инцидента: {0}" -f $_.Exception.Message)
return @{}
}
}
@@ -775,7 +775,7 @@ function Send-CategoryHeartbeat {
Load-CustomCategoryRules -Path $resolvedRulesPath
Load-DlpPolicy -Path $resolvedPolicyPath
Write-CollectorLog ("collector started against {0}" -f $script:ApiBase)
Write-CollectorLog ("коллектор запущен для {0}" -f $script:ApiBase)
while ($true) {
try {
@@ -815,7 +815,7 @@ while ($true) {
}
}
catch {
Write-CollectorLog ("collector error: {0}" -f $_.Exception.Message)
Write-CollectorLog ("ошибка коллектора: {0}" -f $_.Exception.Message)
}
Start-Sleep -Seconds $resolvedPollSeconds
+4 -4
View File
@@ -103,8 +103,8 @@ Register-ActivityWatchUserTasks -TaskDefinitions $taskDefinitions -LaunchScriptP
Register-ActivityWatchRecoveryTask -TaskName $config.recovery.taskName -RecoveryScriptPath $recoveryScriptPath -ConfigPath $configPath
Start-ActivityWatchTasks -TaskDefinitions $taskDefinitions -RecoveryTaskName $config.recovery.taskName
Write-Host 'ActivityWatch deployed for users:'
Write-Host 'ActivityWatch развёрнут для пользователей:'
$targetUsers | ForEach-Object { Write-Host " - $_" }
Write-Host "Server: ${ServerScheme}://$ServerHost`:$ServerPort"
Write-Host "State root: $StateRoot"
Write-Host "Policy file: $($assetResult.ActivePolicy)"
Write-Host "Сервер: ${ServerScheme}://$ServerHost`:$ServerPort"
Write-Host "Каталог данных: $StateRoot"
Write-Host "Файл DLP-политики: $($assetResult.ActivePolicy)"
+5 -5
View File
@@ -46,7 +46,7 @@ $hardeningScript = Join-Path $PSScriptRoot 'hardening-recovery.ps1'
$validationScript = Join-Path $PSScriptRoot 'validate-deployment.ps1'
if (-not (Test-Path -LiteralPath $deployScript)) {
throw "Missing script: $deployScript"
throw "Не найден скрипт: $deployScript"
}
& $deployScript `
@@ -118,7 +118,7 @@ $report = [ordered]@{
if ($ValidateAfterDeploy) {
if (-not (Test-Path -LiteralPath $validationScript)) {
throw "Missing script: $validationScript"
throw "Не найден скрипт: $validationScript"
}
$validation = & $validationScript -ConfigPath (Join-Path $StateRoot 'deployment-config.json')
@@ -132,6 +132,6 @@ if ($reportDirectory) {
$report | ConvertTo-Json -Depth 12 | Set-Content -LiteralPath $effectiveReportPath -Encoding UTF8
Write-Host 'ActivityWatch ensemble deploy completed.'
Write-Host "Users: $($resolvedUsers -join ', ')"
Write-Host "Report: $effectiveReportPath"
Write-Host 'Комплексное развёртывание ActivityWatch завершено.'
Write-Host "Пользователи: $($resolvedUsers -join ', ')"
Write-Host "Отчёт: $effectiveReportPath"
+6 -6
View File
@@ -101,9 +101,9 @@ Register-ActivityWatchUserTasks -TaskDefinitions $taskDefinitions -LaunchScriptP
Register-ActivityWatchRecoveryTask -TaskName $config.recovery.taskName -RecoveryScriptPath $recoveryScriptPath -ConfigPath $configPath
Start-ActivityWatchTasks -TaskDefinitions $taskDefinitions -RecoveryTaskName $config.recovery.taskName
Write-Host "ActivityWatch deployed for $TargetUser"
Write-Host "Server: ${ServerScheme}://$ServerHost`:$ServerPort"
Write-Host "Install root: $InstallRoot"
Write-Host "State root: $StateRoot"
Write-Host "Rules file: $($assetResult.ActiveRules)"
Write-Host "Policy file: $($assetResult.ActivePolicy)"
Write-Host "ActivityWatch развёрнут для пользователя: $TargetUser"
Write-Host "Сервер: ${ServerScheme}://$ServerHost`:$ServerPort"
Write-Host "Каталог установки: $InstallRoot"
Write-Host "Каталог данных: $StateRoot"
Write-Host "Файл правил: $($assetResult.ActiveRules)"
Write-Host "Файл DLP-политики: $($assetResult.ActivePolicy)"
+122 -27
View File
@@ -210,7 +210,7 @@ function Capture-IncidentScreenshot {
}
}
catch {
Write-EndpointLog ("screenshot capture failed: {0}" -f $_.Exception.Message)
Write-EndpointLog ("не удалось сделать снимок инцидента: {0}" -f $_.Exception.Message)
return @{}
}
}
@@ -246,7 +246,7 @@ function Load-DlpPolicy {
}
if (-not $Path -or -not (Test-Path -LiteralPath $Path)) {
Write-EndpointLog ("policy not found, using defaults: {0}" -f $Path)
Write-EndpointLog ("DLP-политика не найдена, используются значения по умолчанию: {0}" -f $Path)
return
}
@@ -266,7 +266,7 @@ function Load-DlpPolicy {
}
}
catch {
Write-EndpointLog ("policy parse failed: {0}" -f $_.Exception.Message)
Write-EndpointLog ("не удалось разобрать DLP-политику: {0}" -f $_.Exception.Message)
}
}
@@ -319,13 +319,13 @@ function Evaluate-ClipboardRules {
$action = if ($rule.action) { [string]$rule.action } else { [string]$script:Policy.defaults.action }
$severity = if ($rule.severity) { [string]$rule.severity } else { [string]$script:Policy.defaults.severity }
$message = if ($rule.message) { [string]$rule.message } else { "Clipboard rule matched: $ruleId" }
$message = if ($rule.message) { [string]$rule.message } else { "Сработало правило буфера обмена: $ruleId" }
Send-DlpIncidentHeartbeat -RuleId $ruleId -Action $action -Severity $severity -Message $message -SignalType 'clipboard' -Data @{
clipboardHash = $ClipboardHash
clipboardLength = $ClipboardText.Length
}
Write-EndpointLog ("incident clipboard rule={0} action={1} severity={2}" -f $ruleId, $action, $severity)
Write-EndpointLog ("инцидент буфера обмена правило={0} действие={1} важность={2}" -f $ruleId, $action, $severity)
}
}
@@ -347,13 +347,13 @@ function Evaluate-UsbRules {
$action = if ($rule.action) { [string]$rule.action } else { [string]$script:Policy.defaults.action }
$severity = if ($rule.severity) { [string]$rule.severity } else { [string]$script:Policy.defaults.severity }
$message = if ($rule.message) { [string]$rule.message } else { "USB rule matched: $ruleId" }
$message = if ($rule.message) { [string]$rule.message } else { "Сработало правило USB-носителя: $ruleId" }
Send-DlpIncidentHeartbeat -RuleId $ruleId -Action $action -Severity $severity -Message $message -SignalType 'usb_insert' -Data @{
driveLetter = $DriveLetter
volumeName = $VolumeName
}
Write-EndpointLog ("incident usb rule={0} action={1} severity={2} drive={3}" -f $ruleId, $action, $severity, $DriveLetter)
Write-EndpointLog ("инцидент USB правило={0} действие={1} важность={2} диск={3}" -f $ruleId, $action, $severity, $DriveLetter)
}
}
@@ -385,14 +385,14 @@ function Evaluate-PrintRules {
$action = if ($rule.action) { [string]$rule.action } else { [string]$script:Policy.defaults.action }
$severity = if ($rule.severity) { [string]$rule.severity } else { [string]$script:Policy.defaults.severity }
$message = if ($rule.message) { [string]$rule.message } else { "Print rule matched: $ruleId" }
$message = if ($rule.message) { [string]$rule.message } else { "Сработало правило печати: $ruleId" }
Send-DlpIncidentHeartbeat -RuleId $ruleId -Action $action -Severity $severity -Message $message -SignalType 'print_job' -Data @{
printerName = $PrinterName
documentName = $DocumentName
owner = $Owner
}
Write-EndpointLog ("incident print rule={0} action={1} severity={2} printer={3}" -f $ruleId, $action, $severity, $PrinterName)
Write-EndpointLog ("инцидент печати правило={0} действие={1} важность={2} принтер={3}" -f $ruleId, $action, $severity, $PrinterName)
}
}
@@ -402,6 +402,52 @@ function Test-LooksLikeMojibakeQuestionMarks {
return $Value -match '\?{2,}'
}
function Test-DocumentNameNeedsFallback {
param([AllowNull()][string]$Value)
if ([string]::IsNullOrWhiteSpace($Value)) { return $true }
$trimmed = $Value.Trim()
if (Test-LooksLikeMojibakeQuestionMarks -Value $trimmed) { return $true }
if ($trimmed -match '^[0-9]+$') { return $true }
if ($trimmed -match '^(?i)(print document|document|local downlevel document)$') { return $true }
return $false
}
function Get-EventXmlValue {
param(
[Parameter(Mandatory = $true)][xml]$EventXml,
[Parameter(Mandatory = $true)][string]$Name
)
$node = $EventXml.Event.UserData.DocumentPrinted.$Name
if ($null -ne $node) {
return [string]$node
}
return ''
}
function Get-PrintJobPrinterName {
param(
[AllowNull()][string]$JobName,
[AllowNull()][string]$FallbackPrinterName
)
if ([string]::IsNullOrWhiteSpace($JobName)) {
if (-not [string]::IsNullOrWhiteSpace($FallbackPrinterName)) {
return $FallbackPrinterName.Trim()
}
return ''
}
$parts = $JobName -split ',', 2
if ($parts.Count -gt 0 -and -not [string]::IsNullOrWhiteSpace($parts[0])) {
return $parts[0].Trim()
}
return $JobName.Trim()
}
function Normalize-OwnerForMatch {
param([AllowNull()][string]$Value)
if ([string]::IsNullOrWhiteSpace($Value)) { return '' }
@@ -469,13 +515,50 @@ function Get-PrintServiceEventSummary {
$propertyValues += [string]$prop.Value
}
$xml = $null
try {
$xml = [xml]$Event.ToXml()
}
catch {
}
$jobId = ''
$documentName = ''
$owner = ''
$portName = ''
$printerName = ''
$sizeBytes = ''
$pageCount = ''
if ($xml) {
$jobId = Get-EventXmlValue -EventXml $xml -Name 'Param1'
$documentName = Get-EventXmlValue -EventXml $xml -Name 'Param2'
$owner = Get-EventXmlValue -EventXml $xml -Name 'Param3'
$portName = Get-EventXmlValue -EventXml $xml -Name 'Param4'
$printerName = Get-EventXmlValue -EventXml $xml -Name 'Param5'
$sizeBytes = Get-EventXmlValue -EventXml $xml -Name 'Param7'
$pageCount = Get-EventXmlValue -EventXml $xml -Name 'Param8'
}
if ([string]::IsNullOrWhiteSpace($jobId) -and $props.Count -ge 1) { $jobId = [string]$props[0].Value }
if ([string]::IsNullOrWhiteSpace($documentName) -and $props.Count -ge 2) { $documentName = [string]$props[1].Value }
if ([string]::IsNullOrWhiteSpace($owner) -and $props.Count -ge 3) { $owner = [string]$props[2].Value }
if ([string]::IsNullOrWhiteSpace($portName) -and $props.Count -ge 4) { $portName = [string]$props[3].Value }
if ([string]::IsNullOrWhiteSpace($printerName) -and $props.Count -ge 5) { $printerName = [string]$props[4].Value }
if ([string]::IsNullOrWhiteSpace($sizeBytes) -and $props.Count -ge 7) { $sizeBytes = [string]$props[6].Value }
if ([string]::IsNullOrWhiteSpace($pageCount) -and $props.Count -ge 8) { $pageCount = [string]$props[7].Value }
[pscustomobject]@{
RecordId = [string]$Event.RecordId
TimeCreated = if ($Event.TimeCreated) { $Event.TimeCreated.ToString('o') } else { '' }
PropertyCount = $props.Count
DocumentName = if ($props.Count -ge 1) { [string]$props[0].Value } else { '' }
Owner = if ($props.Count -ge 2) { [string]$props[1].Value } else { '' }
PrinterName = if ($props.Count -ge 4) { [string]$props[3].Value } else { '' }
JobId = $jobId
DocumentName = $documentName
Owner = $owner
PortName = $portName
PrinterName = $printerName
SizeBytes = $sizeBytes
PageCount = $pageCount
PropertyValues = $propertyValues
}
}
@@ -488,7 +571,7 @@ function Get-PrintServiceDocumentFallback {
)
$preferred = [string]$EventSummary.DocumentName
if (-not (Test-LooksLikeMojibakeQuestionMarks -Value $preferred) -and $preferred -notmatch '^[0-9]+$') {
if (-not (Test-DocumentNameNeedsFallback -Value $preferred)) {
return $preferred
}
@@ -499,9 +582,10 @@ function Get-PrintServiceDocumentFallback {
$candidate = [string]$value
if ([string]::IsNullOrWhiteSpace($candidate)) { continue }
if ($candidate -eq $preferred) { continue }
if ($EventSummary.JobId -and $candidate -eq [string]$EventSummary.JobId) { continue }
if ($Owner -and $candidate -like "*$Owner*") { continue }
if ($PrinterName -and $candidate -like "*$PrinterName*") { continue }
if (Test-LooksLikeMojibakeQuestionMarks -Value $candidate) { continue }
if (Test-DocumentNameNeedsFallback -Value $candidate) { continue }
if ($candidate -match '[\\/:]' -and $candidate -match '\.[A-Za-z0-9]{1,8}$') {
$pathCandidates.Add($candidate)
@@ -546,7 +630,7 @@ function Write-PrintServiceEventTrace {
}
Write-EndpointLog (
'printservice-307 phase={0} recordId={1} time={2} owner={3} printer={4} document={5} resolved={6} properties=[{7}] reason={8}' -f
'printservice-307 этап={0} recordId={1} время={2} владелец={3} принтер={4} документ={5} итоговыйДокумент={6} свойства=[{7}] причина={8}' -f
$Phase,
$EventSummary.RecordId,
$EventSummary.TimeCreated,
@@ -561,6 +645,7 @@ function Write-PrintServiceEventTrace {
function Get-BetterDocumentNameFromPrintServiceEvents {
param(
[string]$JobId,
[string]$Owner,
[string]$PrinterName
)
@@ -578,32 +663,41 @@ function Get-BetterDocumentNameFromPrintServiceEvents {
$summary = Get-PrintServiceEventSummary -Event $event
$resolvedDocument = Get-PrintServiceDocumentFallback -EventSummary $summary -Owner $Owner -PrinterName $PrinterName
$jobMatches = if ($JobId) { [string]$summary.JobId -eq [string]$JobId } else { $true }
$ownerMatches = if ($Owner) { Test-OwnerLooseMatch -Expected $Owner -Actual $summary.Owner } else { $true }
$printerMatches = if ($PrinterName) { Test-PrinterLooseMatch -Expected $PrinterName -Actual $summary.PrinterName } else { $true }
if ($pass -eq 'strict') {
if ($JobId -and -not $jobMatches) {
Write-PrintServiceEventTrace -EventSummary $summary -Phase 'scan' -MatchReason 'несовпадение-jobid-strict' -ResolvedDocument $resolvedDocument
continue
}
if ($Owner -and -not $ownerMatches) {
Write-PrintServiceEventTrace -EventSummary $summary -Phase 'scan' -MatchReason 'owner-mismatch-strict' -ResolvedDocument $resolvedDocument
Write-PrintServiceEventTrace -EventSummary $summary -Phase 'scan' -MatchReason 'несовпадение-владельца-strict' -ResolvedDocument $resolvedDocument
continue
}
if ($PrinterName -and -not $printerMatches) {
Write-PrintServiceEventTrace -EventSummary $summary -Phase 'scan' -MatchReason 'printer-mismatch-strict' -ResolvedDocument $resolvedDocument
Write-PrintServiceEventTrace -EventSummary $summary -Phase 'scan' -MatchReason 'несовпадение-принтера-strict' -ResolvedDocument $resolvedDocument
continue
}
}
else {
if ($Owner -and $PrinterName -and -not $ownerMatches -and -not $printerMatches) {
Write-PrintServiceEventTrace -EventSummary $summary -Phase 'scan' -MatchReason 'owner-and-printer-mismatch-relaxed' -ResolvedDocument $resolvedDocument
if ($JobId -and (-not $jobMatches) -and $Owner -and $PrinterName -and -not $ownerMatches -and -not $printerMatches) {
Write-PrintServiceEventTrace -EventSummary $summary -Phase 'scan' -MatchReason 'несовпадение-владельца-и-принтера-relaxed' -ResolvedDocument $resolvedDocument
continue
}
if ((-not $JobId) -and $Owner -and $PrinterName -and -not $ownerMatches -and -not $printerMatches) {
Write-PrintServiceEventTrace -EventSummary $summary -Phase 'scan' -MatchReason 'несовпадение-владельца-и-принтера-relaxed' -ResolvedDocument $resolvedDocument
continue
}
}
if ([string]::IsNullOrWhiteSpace($resolvedDocument)) {
Write-PrintServiceEventTrace -EventSummary $summary -Phase 'scan' -MatchReason ('no-document-candidate-' + $pass) -ResolvedDocument ''
Write-PrintServiceEventTrace -EventSummary $summary -Phase 'scan' -MatchReason ('нет-кандидата-документа-' + $pass) -ResolvedDocument ''
continue
}
$matchReasonBase = if (Test-LooksLikeMojibakeQuestionMarks -Value $summary.DocumentName) { 'fallback-used' } else { 'direct' }
$matchReasonBase = if (Test-DocumentNameNeedsFallback -Value $summary.DocumentName) { 'использован-резервный-вариант' } else { 'напрямую' }
Write-PrintServiceEventTrace -EventSummary $summary -Phase 'selected' -MatchReason ($matchReasonBase + '-' + $pass) -ResolvedDocument $resolvedDocument
return $resolvedDocument
}
@@ -616,7 +710,7 @@ function Get-BetterDocumentNameFromPrintServiceEvents {
}
$deploymentConfig = Get-DeploymentConfig -Path $ConfigPath
$resolvedServerHost = if ($ServerHost) { $ServerHost } elseif ($deploymentConfig) { [string]$deploymentConfig.server.host } else { throw 'ServerHost is required.' }
$resolvedServerHost = if ($ServerHost) { $ServerHost } elseif ($deploymentConfig) { [string]$deploymentConfig.server.host } else { throw 'Укажите ServerHost или подготовьте deployment-config.json.' }
$resolvedServerPort = if ($PSBoundParameters.ContainsKey('ServerPort')) { $ServerPort } elseif ($deploymentConfig) { [int]$deploymentConfig.server.port } else { 5600 }
$resolvedServerScheme = if ($ServerScheme) { $ServerScheme } elseif ($deploymentConfig) { [string]$deploymentConfig.server.scheme } else { 'http' }
$resolvedPolicyPath = if ($PolicyPath) { $PolicyPath } elseif ($deploymentConfig -and $deploymentConfig.paths.PSObject.Properties.Name -contains 'policyPath') { [string]$deploymentConfig.paths.policyPath } else { 'C:\ProgramData\ActivityWatch\dlp-policy.json' }
@@ -648,7 +742,7 @@ $script:IncidentScreenshotEnabled = $resolvedIncidentScreenshotEnabled
$script:ScreenshotTypesLoaded = $false
Load-DlpPolicy -Path $resolvedPolicyPath
Write-EndpointLog ("endpoint collector started against {0}" -f $script:ApiBase)
Write-EndpointLog ("endpoint-коллектор запущен для {0}" -f $script:ApiBase)
while ($true) {
try {
@@ -709,13 +803,13 @@ while ($true) {
if ($script:SeenPrintJob.ContainsKey($jobId)) { continue }
$script:SeenPrintJob[$jobId] = (Get-Date).ToUniversalTime()
$printerName = [string]$job.Name
$printerName = Get-PrintJobPrinterName -JobName ([string]$job.Name) -FallbackPrinterName ([string]$job.DriverName)
$documentName = [string]$job.Document
$owner = [string]$job.Owner
$documentNameOriginal = $documentName
if (Test-LooksLikeMojibakeQuestionMarks -Value $documentName) {
$eventDocumentName = Get-BetterDocumentNameFromPrintServiceEvents -Owner $owner -PrinterName $printerName
if (Test-DocumentNameNeedsFallback -Value $documentName) {
$eventDocumentName = Get-BetterDocumentNameFromPrintServiceEvents -JobId $jobId -Owner $owner -PrinterName $printerName
if ($eventDocumentName) {
$documentName = $eventDocumentName
}
@@ -726,6 +820,7 @@ while ($true) {
documentName = $documentName
documentNameOriginal = $documentNameOriginal
owner = $owner
printJobId = $jobId
}
Evaluate-PrintRules -PrinterName $printerName -DocumentName $documentName -Owner $owner
}
@@ -789,7 +884,7 @@ while ($true) {
}
}
catch {
Write-EndpointLog ("collector error: {0}" -f $_.Exception.Message)
Write-EndpointLog ("ошибка коллектора: {0}" -f $_.Exception.Message)
}
Start-Sleep -Seconds $resolvedPollSeconds
+6 -5
View File
@@ -42,7 +42,7 @@ if (Test-Path -LiteralPath $ConfigPath) {
}
if (-not $existingConfig -and (-not $ServerHost)) {
throw 'deployment-config.json is missing. Provide -ServerHost and user parameters, or run a deploy script first.'
throw 'deployment-config.json отсутствует. Укажите -ServerHost и параметры пользователей либо сначала выполните скрипт развёртывания.'
}
$effectiveStateRoot = if ($StateRoot) { $StateRoot } elseif ($existingConfig) { [string]$existingConfig.paths.stateRoot } else { 'C:\ProgramData\ActivityWatch' }
@@ -78,7 +78,7 @@ elseif ($existingConfig) {
@($existingConfig.userTasks | ForEach-Object { [string]$_.userId })
}
else {
throw 'Target users are missing.'
throw 'Не указаны целевые пользователи.'
}
New-ActivityWatchDirectory -Path $effectiveStateRoot
@@ -96,6 +96,7 @@ Get-ActivityWatchExecutableMap -InstallRoot $effectiveInstallRoot | Out-Null
$assetResult = Copy-ActivityWatchCollectorAssets `
-CollectorScriptSource (Join-Path $PSScriptRoot 'browser-domains-native-collector.ps1') `
-EndpointCollectorScriptSource (Join-Path $PSScriptRoot 'dlp-endpoint-signals-collector.ps1') `
-SessionCollectorScriptSource (Join-Path $PSScriptRoot 'worktime-session-collector.ps1') `
-ExampleRulesSource (Join-Path $PSScriptRoot 'web-category-rules.example.json') `
-ExamplePolicySource (Join-Path $PSScriptRoot 'dlp-policy.example.json') `
-StateRoot $effectiveStateRoot `
@@ -139,6 +140,6 @@ Register-ActivityWatchUserTasks -TaskDefinitions $taskDefinitions -LaunchScriptP
Register-ActivityWatchRecoveryTask -TaskName $config.recovery.taskName -RecoveryScriptPath $effectiveRecoveryScript -ConfigPath $effectiveConfigPath
Start-ActivityWatchTasks -TaskDefinitions $taskDefinitions -RecoveryTaskName $config.recovery.taskName
Write-Host 'ActivityWatch hardening/recovery completed.'
Write-Host "Config: $effectiveConfigPath"
Write-Host "Users repaired: $($effectiveUsers -join ', ')"
Write-Host 'Укрепление и восстановление ActivityWatch завершены.'
Write-Host "Конфигурация: $effectiveConfigPath"
Write-Host "Пользователи восстановлены: $($effectiveUsers -join ', ')"
+1 -1
View File
@@ -65,7 +65,7 @@ $tasks = foreach ($taskName in $taskNames) {
else {
[pscustomobject]@{
taskName = $taskName
state = 'Missing'
state = 'Отсутствует'
present = $false
}
}
+1 -1
View File
@@ -11,7 +11,7 @@ function Get-Config {
param([string]$Path)
if (-not (Test-Path -LiteralPath $Path)) {
throw "Config not found: $Path"
throw "Конфигурация не найдена: $Path"
}
Get-Content -LiteralPath $Path -Raw | ConvertFrom-Json