fix(windows): disable outlook popup and enforce smtp-only email monitoring

This commit is contained in:
igor04091968
2026-05-11 20:46:39 +03:00
parent 24dd5ae2b4
commit e0561bf865
28 changed files with 2025 additions and 22 deletions
+54 -8
View File
@@ -265,6 +265,7 @@ function Copy-ActivityWatchCollectorAssets {
[string]$CollectorScriptSource,
[Parameter(Mandatory = $true)]
[string]$EndpointCollectorScriptSource,
[string]$PolicyClientScriptSource,
[Parameter(Mandatory = $true)]
[string]$FileCollectorScriptSource,
[Parameter(Mandatory = $true)]
@@ -284,6 +285,7 @@ function Copy-ActivityWatchCollectorAssets {
$collectorTarget = Join-Path $StateRoot 'browser-domains-native-collector.ps1'
$endpointCollectorTarget = Join-Path $StateRoot 'dlp-endpoint-signals-collector.ps1'
$policyClientTarget = Join-Path $StateRoot 'dlp-policy-client.ps1'
$fileCollectorTarget = Join-Path $StateRoot 'file-operations-collector.ps1'
$sessionCollectorTarget = Join-Path $StateRoot 'worktime-session-collector.ps1'
$emailCollectorTarget = Join-Path $StateRoot 'email-outbound-collector.ps1'
@@ -294,6 +296,9 @@ function Copy-ActivityWatchCollectorAssets {
Copy-Item -LiteralPath $CollectorScriptSource -Destination $collectorTarget -Force
Copy-Item -LiteralPath $EndpointCollectorScriptSource -Destination $endpointCollectorTarget -Force
if ($PolicyClientScriptSource -and (Test-Path -LiteralPath $PolicyClientScriptSource)) {
Copy-Item -LiteralPath $PolicyClientScriptSource -Destination $policyClientTarget -Force
}
Copy-Item -LiteralPath $FileCollectorScriptSource -Destination $fileCollectorTarget -Force
Copy-Item -LiteralPath $SessionCollectorScriptSource -Destination $sessionCollectorTarget -Force
if ($EmailCollectorScriptSource -and (Test-Path -LiteralPath $EmailCollectorScriptSource)) {
@@ -321,6 +326,7 @@ function Copy-ActivityWatchCollectorAssets {
return [pscustomobject]@{
CollectorScript = $collectorTarget
EndpointCollectorScript = $endpointCollectorTarget
PolicyClientScript = $policyClientTarget
FileCollectorScript = $fileCollectorTarget
SessionCollectorScript = $sessionCollectorTarget
EmailCollectorScript = $emailCollectorTarget
@@ -349,6 +355,7 @@ function New-ActivityWatchDeploymentConfig {
[string]$CollectorScript,
[Parameter(Mandatory = $true)]
[string]$EndpointCollectorScript,
[string]$PolicyClientScript,
[Parameter(Mandatory = $true)]
[string]$FileCollectorScript,
[Parameter(Mandatory = $true)]
@@ -377,12 +384,24 @@ function New-ActivityWatchDeploymentConfig {
[Parameter(Mandatory = $true)]
[string]$RecoveryScriptPath,
[string]$AwHostname,
[ValidateSet('local', 'server')]
[string]$PolicyMode = 'local',
[bool]$PolicyEngineEnabled = $false,
[string]$PolicyEngineHost,
[int]$PolicyEnginePort = 5601,
[ValidateSet('http', 'https')]
[string]$PolicyEngineScheme = 'http',
[int]$PolicyRefreshSeconds = 300,
[string]$PolicyCachePath,
[Parameter(Mandatory = $true)]
[pscustomobject[]]$UserTasks,
[string]$PackageVersion = 'v0.13.2'
[string]$PackageVersion = 'v0.13.2',
[switch]$IntegrationTestEnabled
)
$effectiveIncidentArtifactsRoot = if ($IncidentArtifactsRoot) { $IncidentArtifactsRoot } else { Join-Path $StateRoot 'incident-artifacts' }
$effectivePolicyEngineHost = if ([string]::IsNullOrWhiteSpace($PolicyEngineHost)) { $ServerHost } else { $PolicyEngineHost }
$effectivePolicyCachePath = if ([string]::IsNullOrWhiteSpace($PolicyCachePath)) { Join-Path $StateRoot 'dlp-policy-cache.json' } else { $PolicyCachePath }
return [pscustomobject]@{
version = 1
@@ -399,6 +418,7 @@ function New-ActivityWatchDeploymentConfig {
logsRoot = $LogsRoot
collectorScript = $CollectorScript
endpointCollectorScript = $EndpointCollectorScript
policyClientScript = $PolicyClientScript
emailCollectorScript = $EmailCollectorScript
fileCollectorScript = $FileCollectorScript
sessionCollectorScript = $SessionCollectorScript
@@ -415,7 +435,7 @@ function New-ActivityWatchDeploymentConfig {
afkEnabled = $AfkEnabled
windowEnabled = $WindowEnabled
fileOpsEnabled = $FileOpsEnabled
emailEnabled = ($null -ne $EmailCollectorScript -and $EmailCollectorScript -ne '')
emailEnabled = $false
}
logging = [pscustomobject]@{
localAgentLogsEnabled = $LocalAgentLogsEnabled
@@ -437,10 +457,20 @@ function New-ActivityWatchDeploymentConfig {
incidentBucketPrefix = 'aw-dlp-incidents'
enabled = $true
}
policyEngine = [pscustomobject]@{
enabled = $PolicyEngineEnabled
mode = $PolicyMode
host = $effectivePolicyEngineHost
port = $PolicyEnginePort
scheme = $PolicyEngineScheme
refreshSeconds = $PolicyRefreshSeconds
cachePath = $effectivePolicyCachePath
}
package = [pscustomobject]@{
version = $PackageVersion
}
userTasks = @($UserTasks)
integrationTestEnabled = [bool]$IntegrationTestEnabled
}
}
@@ -1048,10 +1078,24 @@ function Set-ActivityWatchScheduledTaskAction {
[string]$Arguments
)
$taskCommand = ('"{0}" {1}' -f $Execute, $Arguments)
& schtasks.exe /Change /TN $TaskName /TR $taskCommand | Out-Null
if ($LASTEXITCODE -ne 0) {
throw "schtasks.exe /Change завершился с ошибкой для $TaskName"
$task = Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue
if (-not $task) {
return $false
}
$newAction = New-ScheduledTaskAction -Execute $Execute -Argument $Arguments
try {
# Non-interactive update path. Avoids schtasks.exe /Change password prompt for user-bound tasks.
Set-ScheduledTask -TaskName $TaskName -Action $newAction -ErrorAction Stop | Out-Null
return $true
}
catch {
$taskCommand = ('"{0}" {1}' -f $Execute, $Arguments)
& schtasks.exe /Change /TN $TaskName /TR $taskCommand | Out-Null
if ($LASTEXITCODE -ne 0) {
throw "Не удалось обновить action задачи ${TaskName}: $($_.Exception.Message)"
}
return $true
}
}
@@ -1136,8 +1180,10 @@ function Register-ActivityWatchUserTasks {
$existingTask = Get-ActivityWatchScheduledTaskByCommand -TaskName $definition.LaunchTaskName -CommandMatch $ConfigPath
if ($existingTask) {
Set-ActivityWatchScheduledTaskAction -TaskName $existingTask.TaskName -Execute $wscriptExe -Arguments $action.Arguments
continue
$updated = Set-ActivityWatchScheduledTaskAction -TaskName $existingTask.TaskName -Execute $wscriptExe -Arguments $action.Arguments
if ($updated) {
continue
}
}
Remove-ActivityWatchScheduledTask -TaskName $definition.LaunchTaskName
+23 -2
View File
@@ -26,7 +26,17 @@ param(
[bool]$LogonMarkerEnabled = $true,
[string]$AwHostname,
[string]$CustomRulesPath,
[string]$CustomPolicyPath
[string]$CustomPolicyPath,
[ValidateSet('local', 'server')]
[string]$PolicyMode = 'local',
[bool]$PolicyEngineEnabled = $false,
[string]$PolicyEngineHost,
[int]$PolicyEnginePort = 5601,
[ValidateSet('http', 'https')]
[string]$PolicyEngineScheme = 'http',
[int]$PolicyRefreshSeconds = 300,
[string]$PolicyCachePath,
[switch]$IntegrationTestEnabled
)
Set-StrictMode -Version Latest
@@ -46,6 +56,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'
$policyClientSource = Join-Path $PSScriptRoot 'dlp-policy-client.ps1'
$emailCollectorSource = Join-Path $PSScriptRoot 'email-outbound-collector.ps1'
$fileCollectorSource = Join-Path $PSScriptRoot 'file-operations-collector.ps1'
$sessionCollectorSource = Join-Path $PSScriptRoot 'worktime-session-collector.ps1'
@@ -63,6 +74,7 @@ Get-ActivityWatchExecutableMap -InstallRoot $InstallRoot | Out-Null
$assetResult = Copy-ActivityWatchCollectorAssets `
-CollectorScriptSource $collectorSource `
-EndpointCollectorScriptSource $endpointCollectorSource `
-PolicyClientScriptSource $policyClientSource `
-EmailCollectorScriptSource $emailCollectorSource `
-FileCollectorScriptSource $fileCollectorSource `
-SessionCollectorScriptSource $sessionCollectorSource `
@@ -85,6 +97,7 @@ $config = New-ActivityWatchDeploymentConfig `
-LogsRoot $logsRoot `
-CollectorScript $assetResult.CollectorScript `
-EndpointCollectorScript $assetResult.EndpointCollectorScript `
-PolicyClientScript $assetResult.PolicyClientScript `
-EmailCollectorScript $assetResult.EmailCollectorScript `
-FileCollectorScript $assetResult.FileCollectorScript `
-SessionCollectorScript $assetResult.SessionCollectorScript `
@@ -102,10 +115,18 @@ $config = New-ActivityWatchDeploymentConfig `
-IncidentArtifactsRoot $IncidentArtifactsRoot `
-LogonMarkerEnabled $LogonMarkerEnabled `
-AwHostname $AwHostname `
-PolicyMode $PolicyMode `
-PolicyEngineEnabled $PolicyEngineEnabled `
-PolicyEngineHost $PolicyEngineHost `
-PolicyEnginePort $PolicyEnginePort `
-PolicyEngineScheme $PolicyEngineScheme `
-PolicyRefreshSeconds $PolicyRefreshSeconds `
-PolicyCachePath $PolicyCachePath `
-LaunchScriptPath $launchScriptPath `
-RecoveryScriptPath $recoveryScriptPath `
-UserTasks $taskDefinitions `
-PackageVersion $Version
-PackageVersion $Version `
-IntegrationTestEnabled:$IntegrationTestEnabled
Write-ActivityWatchDeploymentConfig -Config $config -Path $configPath
Remove-LegacyActivityWatchEntries
+20 -2
View File
@@ -27,9 +27,19 @@ param(
[string]$AwHostname,
[string]$CustomRulesPath,
[string]$CustomPolicyPath,
[ValidateSet('local', 'server')]
[string]$PolicyMode = 'local',
[bool]$PolicyEngineEnabled = $false,
[string]$PolicyEngineHost,
[int]$PolicyEnginePort = 5601,
[ValidateSet('http', 'https')]
[string]$PolicyEngineScheme = 'http',
[int]$PolicyRefreshSeconds = 300,
[string]$PolicyCachePath,
[string]$ReportPath,
[switch]$SkipHardening,
[switch]$ValidateAfterDeploy
[switch]$ValidateAfterDeploy,
[switch]$IntegrationTestEnabled
)
Set-StrictMode -Version Latest
@@ -74,7 +84,15 @@ if (-not (Test-Path -LiteralPath $deployScript)) {
-LogonMarkerEnabled $LogonMarkerEnabled `
-AwHostname $AwHostname `
-CustomRulesPath $CustomRulesPath `
-CustomPolicyPath $CustomPolicyPath
-CustomPolicyPath $CustomPolicyPath `
-PolicyMode $PolicyMode `
-PolicyEngineEnabled $PolicyEngineEnabled `
-PolicyEngineHost $PolicyEngineHost `
-PolicyEnginePort $PolicyEnginePort `
-PolicyEngineScheme $PolicyEngineScheme `
-PolicyRefreshSeconds $PolicyRefreshSeconds `
-PolicyCachePath $PolicyCachePath `
-IntegrationTestEnabled:$IntegrationTestEnabled
if (-not $SkipHardening) {
& $hardeningScript `
+164 -1
View File
@@ -5,7 +5,15 @@ param(
[int]$ServerPort,
[ValidateSet('http', 'https')]
[string]$ServerScheme,
[string]$PolicyEngineHost,
[int]$PolicyEnginePort,
[ValidateSet('http', 'https')]
[string]$PolicyEngineScheme,
[string]$PolicyPath,
[ValidateSet('local', 'server')]
[string]$PolicyMode,
[int]$PolicyRefreshSeconds,
[string]$PolicyCachePath,
[string]$LogPath,
[int]$PollSeconds
)
@@ -20,6 +28,20 @@ try {
catch {
}
$policyClientModulePath = Join-Path $PSScriptRoot 'dlp-policy-client.ps1'
if (Test-Path -LiteralPath $policyClientModulePath) {
try {
Import-Module $policyClientModulePath -Force -DisableNameChecking
$script:PolicyClientAvailable = $true
}
catch {
$script:PolicyClientAvailable = $false
}
}
else {
$script:PolicyClientAvailable = $false
}
function Get-DeploymentConfig {
param([string]$Path)
if ($Path -and (Test-Path -LiteralPath $Path)) {
@@ -464,6 +486,10 @@ function Load-DlpPolicy {
}
}
$script:PolicySource = 'defaults'
$script:PolicyVersion = $null
$script:PolicyChecksum = $null
if (-not $Path -or -not (Test-Path -LiteralPath $Path)) {
Write-EndpointLog ("policy not found, using defaults: {0}" -f $Path)
return
@@ -485,12 +511,87 @@ function Load-DlpPolicy {
if ($props -contains 'usb' -and $raw.endpoint.usb) { $script:Policy.endpoint.usb = @($raw.endpoint.usb) }
if ($props -contains 'print' -and $raw.endpoint.print) { $script:Policy.endpoint.print = @($raw.endpoint.print) }
}
$script:PolicySource = 'local'
}
catch {
Write-EndpointLog ("policy parse failed: {0}" -f $_.Exception.Message)
}
}
function Apply-PolicyFromBundle {
param(
[Parameter(Mandatory = $true)]$Bundle,
[Parameter(Mandatory = $true)][string]$Source
)
if (-not $Bundle.policy) {
throw 'Policy bundle has no policy payload.'
}
$tempPath = [System.IO.Path]::GetTempFileName()
try {
$Bundle.policy | ConvertTo-Json -Depth 20 | Set-Content -LiteralPath $tempPath -Encoding UTF8
Load-DlpPolicy -Path $tempPath
$script:PolicySource = $Source
$script:PolicyVersion = if ($Bundle.PSObject.Properties.Name -contains 'version') { [string]$Bundle.version } else { $null }
$script:PolicyChecksum = if ($Bundle.PSObject.Properties.Name -contains 'checksum') { [string]$Bundle.checksum } else { $null }
}
finally {
Remove-Item -LiteralPath $tempPath -Force -ErrorAction SilentlyContinue
}
}
function Refresh-DlpPolicyFromServer {
if (-not $script:PolicyEngineEnabled) {
return $false
}
if (-not $script:PolicyClientAvailable) {
Write-EndpointLog 'policy client module unavailable, cannot use server mode'
return $false
}
try {
$bundle = Get-RemoteDlpPolicyBundle -ApiBase $script:PolicyApiBase -TimeoutSec 10
Save-CachedDlpPolicyBundle -Bundle $bundle -CachePath $script:PolicyCachePath
Apply-PolicyFromBundle -Bundle $bundle -Source 'server'
$script:LastPolicyRefreshAt = (Get-Date).ToUniversalTime()
Write-EndpointLog ("policy refreshed from server version={0} checksum={1}" -f $script:PolicyVersion, $script:PolicyChecksum)
return $true
}
catch {
Write-EndpointLog ("policy refresh failed: {0}" -f $_.Exception.Message)
return $false
}
}
function Initialize-DlpPolicy {
if ($script:PolicyMode -eq 'server') {
if (Refresh-DlpPolicyFromServer) {
return
}
if ($script:PolicyClientAvailable) {
$cached = Read-CachedDlpPolicyBundle -CachePath $script:PolicyCachePath
if ($cached) {
try {
Apply-PolicyFromBundle -Bundle $cached -Source 'cache'
Write-EndpointLog ("policy loaded from cache version={0} checksum={1}" -f $script:PolicyVersion, $script:PolicyChecksum)
return
}
catch {
Write-EndpointLog ("cached policy load failed: {0}" -f $_.Exception.Message)
}
}
}
Load-DlpPolicy -Path $script:LocalPolicyPath
$script:PolicySource = 'local-fallback'
return
}
Load-DlpPolicy -Path $script:LocalPolicyPath
}
function Should-EmitByCooldown {
param(
[string]$Fingerprint,
@@ -862,6 +963,7 @@ $resolvedServerHost = if ($ServerHost) { $ServerHost } elseif ($deploymentConfig
$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\AWatch-rus\dlp-policy.json' }
$resolvedStateRoot = if ($deploymentConfig -and $deploymentConfig.paths.PSObject.Properties.Name -contains 'stateRoot') { [string]$deploymentConfig.paths.stateRoot } else { Split-Path -Path $resolvedPolicyPath -Parent }
$resolvedPollSeconds = if ($PSBoundParameters.ContainsKey('PollSeconds')) { $PollSeconds } elseif ($deploymentConfig) { [int]$deploymentConfig.collector.pollSeconds } else { 5 }
$resolvedLogsRoot = if ($deploymentConfig) { [string]$deploymentConfig.paths.logsRoot } else { 'C:\ProgramData\AWatch-rus\logs' }
$resolvedLogPath = if ($LogPath) { $LogPath } else { Join-Path $resolvedLogsRoot ("endpoint-signals-{0}.log" -f $env:USERNAME) }
@@ -869,12 +971,20 @@ $resolvedLocalAgentLogsEnabled = if ($deploymentConfig -and $deploymentConfig.PS
$resolvedIncidentArtifactsRoot = if ($deploymentConfig -and $deploymentConfig.PSObject.Properties.Name -contains 'incidentCapture' -and $deploymentConfig.incidentCapture.PSObject.Properties.Name -contains 'artifactsRoot') { [string]$deploymentConfig.incidentCapture.artifactsRoot } else { Join-Path $env:LOCALAPPDATA 'AWatch-rus\\incident-artifacts' }
$resolvedIncidentScreenshotEnabled = if ($deploymentConfig -and $deploymentConfig.PSObject.Properties.Name -contains 'incidentCapture' -and $deploymentConfig.incidentCapture.PSObject.Properties.Name -contains 'screenshotEnabled') { [bool]$deploymentConfig.incidentCapture.screenshotEnabled } else { $true }
$resolvedHostname = if ($deploymentConfig -and $deploymentConfig.PSObject.Properties.Name -contains 'awHostname' -and -not [string]::IsNullOrWhiteSpace([string]$deploymentConfig.awHostname)) { [string]$deploymentConfig.awHostname } else { [string]$env:COMPUTERNAME }
$resolvedPolicyMode = if ($PolicyMode) { [string]$PolicyMode } elseif ($deploymentConfig -and $deploymentConfig.PSObject.Properties.Name -contains 'policyEngine' -and $deploymentConfig.policyEngine.PSObject.Properties.Name -contains 'mode') { [string]$deploymentConfig.policyEngine.mode } else { 'local' }
$resolvedPolicyEngineEnabled = if ($deploymentConfig -and $deploymentConfig.PSObject.Properties.Name -contains 'policyEngine' -and $deploymentConfig.policyEngine.PSObject.Properties.Name -contains 'enabled') { [bool]$deploymentConfig.policyEngine.enabled } else { $false }
$resolvedPolicyEngineHost = if ($PolicyEngineHost) { [string]$PolicyEngineHost } elseif ($deploymentConfig -and $deploymentConfig.PSObject.Properties.Name -contains 'policyEngine' -and $deploymentConfig.policyEngine.PSObject.Properties.Name -contains 'host') { [string]$deploymentConfig.policyEngine.host } else { $resolvedServerHost }
$resolvedPolicyEnginePort = if ($PSBoundParameters.ContainsKey('PolicyEnginePort')) { $PolicyEnginePort } elseif ($deploymentConfig -and $deploymentConfig.PSObject.Properties.Name -contains 'policyEngine' -and $deploymentConfig.policyEngine.PSObject.Properties.Name -contains 'port') { [int]$deploymentConfig.policyEngine.port } else { $resolvedServerPort }
$resolvedPolicyEngineScheme = if ($PolicyEngineScheme) { [string]$PolicyEngineScheme } elseif ($deploymentConfig -and $deploymentConfig.PSObject.Properties.Name -contains 'policyEngine' -and $deploymentConfig.policyEngine.PSObject.Properties.Name -contains 'scheme') { [string]$deploymentConfig.policyEngine.scheme } else { $resolvedServerScheme }
$resolvedPolicyRefreshSeconds = if ($PSBoundParameters.ContainsKey('PolicyRefreshSeconds')) { $PolicyRefreshSeconds } elseif ($deploymentConfig -and $deploymentConfig.PSObject.Properties.Name -contains 'policyEngine' -and $deploymentConfig.policyEngine.PSObject.Properties.Name -contains 'refreshSeconds') { [int]$deploymentConfig.policyEngine.refreshSeconds } else { 300 }
$resolvedPolicyCachePath = if ($PolicyCachePath) { [string]$PolicyCachePath } elseif ($deploymentConfig -and $deploymentConfig.PSObject.Properties.Name -contains 'policyEngine' -and $deploymentConfig.policyEngine.PSObject.Properties.Name -contains 'cachePath') { [string]$deploymentConfig.policyEngine.cachePath } else { Join-Path $resolvedStateRoot 'dlp-policy-cache.json' }
if ($resolvedLocalAgentLogsEnabled -and -not (Test-Path -LiteralPath $resolvedLogsRoot)) {
New-Item -Path $resolvedLogsRoot -ItemType Directory -Force | Out-Null
}
$script:ApiBase = '{0}://{1}:{2}/api/0' -f $resolvedServerScheme, $resolvedServerHost, $resolvedServerPort
$script:PolicyApiBase = '{0}://{1}:{2}/api/0' -f $resolvedPolicyEngineScheme, $resolvedPolicyEngineHost, $resolvedPolicyEnginePort
$script:Hostname = $resolvedHostname
$script:SessionId = (Get-Process -Id $PID).SessionId
$script:KnownBuckets = @{}
@@ -891,17 +1001,37 @@ $script:LogPath = $resolvedLogPath
$script:IncidentArtifactsRoot = $resolvedIncidentArtifactsRoot
$script:IncidentScreenshotEnabled = $resolvedIncidentScreenshotEnabled
$script:ScreenshotTypesLoaded = $false
$script:PolicyMode = $resolvedPolicyMode
$script:PolicyEngineEnabled = $resolvedPolicyEngineEnabled
$script:PolicyRefreshSeconds = [Math]::Max($resolvedPolicyRefreshSeconds, 60)
$script:PolicyCachePath = $resolvedPolicyCachePath
$script:LocalPolicyPath = $resolvedPolicyPath
$script:LastPolicyRefreshAt = [datetime]::MinValue
# Integration test flag (backward compatible - defaults to false)
$script:IntegrationTestEnabled = if ($deploymentConfig -and $deploymentConfig.PSObject.Properties.Name -contains 'integrationTestEnabled') { [bool]$deploymentConfig.integrationTestEnabled } else { $false }
Load-DlpPolicy -Path $resolvedPolicyPath
# Integration metadata tracking (backward compatible)
$script:TotalEventsProcessed = 0
$script:LastEventTime = $null
Initialize-DlpPolicy
Write-EndpointLog ("endpoint collector started against {0}" -f $script:ApiBase)
while ($true) {
try {
if ($script:PolicyMode -eq 'server' -and (($nowUtc = (Get-Date).ToUniversalTime()) - $script:LastPolicyRefreshAt).TotalSeconds -ge $script:PolicyRefreshSeconds) {
[void](Refresh-DlpPolicyFromServer)
}
$nowUtc = (Get-Date).ToUniversalTime()
if (($nowUtc - $script:LastSelfTestAt).TotalSeconds -ge $script:SelfTestIntervalSeconds) {
Send-EndpointSignalHeartbeat -SignalType 'self_test' -Data @{
collector = 'dlp-endpoint-signals'
policyEnabled = [bool]$script:Policy.defaults.enabled
policyMode = $script:PolicyMode
policySource = $script:PolicySource
policyVersion = $script:PolicyVersion
policyChecksum = $script:PolicyChecksum
}
$script:LastSelfTestAt = $nowUtc
}
@@ -921,6 +1051,8 @@ while ($true) {
clipboardHash = $clipboardHash
clipboardLength = $clipboardText.Length
}
$script:TotalEventsProcessed++
$script:LastEventTime = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fffZ')
Evaluate-ClipboardRules -ClipboardText $clipboardText -ClipboardHash $clipboardHash
}
}
@@ -942,6 +1074,8 @@ while ($true) {
driveLetter = $deviceId
volumeName = $volumeName
}
$script:TotalEventsProcessed++
$script:LastEventTime = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fffZ')
Evaluate-UsbRules -DriveLetter $deviceId -VolumeName $volumeName
}
}
@@ -981,6 +1115,8 @@ while ($true) {
documentNameOriginal = $documentNameOriginal
owner = $owner
}
$script:TotalEventsProcessed++
$script:LastEventTime = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fffZ')
Evaluate-PrintRules -PrinterName $printerName -DocumentName $documentName -Owner $owner
}
@@ -1028,6 +1164,8 @@ while ($true) {
eventRecordId = $recordId
eventSource = 'printservice-307'
}
$script:TotalEventsProcessed++
$script:LastEventTime = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fffZ')
Evaluate-PrintRules -PrinterName $printerName -DocumentName (if ($resolvedDocument) { $resolvedDocument } else { $documentName }) -Owner $owner
}
@@ -1046,5 +1184,30 @@ while ($true) {
Write-EndpointLog ("collector error: {0}" -f $_.Exception.Message)
}
# Integration metadata self-test (backward compatible)
if ($script:IntegrationTestEnabled -and (Get-Date).Minute -eq 0) {
try {
$testMetadata = @{
timestamp = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fffZ')
collector = 'dlp-endpoint-signals'
version = '1.0.0'
hostname = $env:COMPUTERNAME
username = $env:USERNAME
status = 'healthy'
checks = @{
eventsProcessed = $script:TotalEventsProcessed
lastEventTime = $script:LastEventTime
iocRulesLoaded = if ($script:IocRules) { @($script:IocRules).Count } else { 0 }
policyRulesLoaded = if ($script:Policy -and $script:Policy.endpoint) { (@($script:Policy.endpoint.clipboard).Count + @($script:Policy.endpoint.usb).Count + @($script:Policy.endpoint.print).Count) } else { 0 }
}
}
Send-EndpointSignalHeartbeat -SignalType 'integration_test' -Data $testMetadata
Write-EndpointLog "Integration metadata test sent"
}
catch {
Write-EndpointLog "Integration test failed: $($_.Exception.Message)"
}
}
Start-Sleep -Seconds $resolvedPollSeconds
}
+85
View File
@@ -0,0 +1,85 @@
[CmdletBinding()]
param()
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
function Invoke-DlpPolicyGetJson {
param(
[Parameter(Mandatory = $true)][string]$Uri,
[int]$TimeoutSec = 10
)
$request = [System.Net.HttpWebRequest]::Create($Uri)
$request.Method = 'GET'
$request.Accept = 'application/json'
$request.KeepAlive = $false
$request.Timeout = $TimeoutSec * 1000
$request.ReadWriteTimeout = $TimeoutSec * 1000
$response = $request.GetResponse()
try {
$stream = $response.GetResponseStream()
$reader = New-Object System.IO.StreamReader($stream, [System.Text.Encoding]::UTF8)
try {
$reader.ReadToEnd() | ConvertFrom-Json
}
finally {
$reader.Close()
}
}
finally {
$response.Close()
}
}
function Get-RemoteDlpPolicyBundle {
param(
[Parameter(Mandatory = $true)][string]$ApiBase,
[int]$TimeoutSec = 10
)
$bundle = Invoke-DlpPolicyGetJson -Uri ($ApiBase.TrimEnd('/') + '/dlp/policies/active') -TimeoutSec $TimeoutSec
if (-not $bundle) {
throw 'Policy engine returned empty response.'
}
if (-not $bundle.active) {
throw 'Policy engine has no active policy.'
}
if (-not $bundle.policy) {
throw 'Policy engine response has no policy payload.'
}
return $bundle
}
function Read-CachedDlpPolicyBundle {
param([Parameter(Mandatory = $true)][string]$CachePath)
if (-not (Test-Path -LiteralPath $CachePath)) {
return $null
}
try {
return Get-Content -LiteralPath $CachePath -Raw | ConvertFrom-Json
}
catch {
return $null
}
}
function Save-CachedDlpPolicyBundle {
param(
[Parameter(Mandatory = $true)]$Bundle,
[Parameter(Mandatory = $true)][string]$CachePath
)
$directory = Split-Path -Path $CachePath -Parent
if ($directory -and -not (Test-Path -LiteralPath $directory)) {
New-Item -Path $directory -ItemType Directory -Force | Out-Null
}
$json = $Bundle | ConvertTo-Json -Depth 20
Set-Content -LiteralPath $CachePath -Value $json -Encoding UTF8
}
Export-ModuleMember -Function Invoke-DlpPolicyGetJson, Get-RemoteDlpPolicyBundle, Read-CachedDlpPolicyBundle, Save-CachedDlpPolicyBundle
+44 -2
View File
@@ -26,7 +26,7 @@ param(
[string]$LogPath,
[int]$PollSeconds,
[ValidateSet('outlook', 'smtp', 'both')]
[string]$Mode = 'both'
[string]$Mode = 'smtp'
)
Set-StrictMode -Version Latest
@@ -322,8 +322,23 @@ function Invoke-EmailEnforcement {
# ---------------------------------------------------------------------------
function Initialize-OutlookCom {
if ($script:OutlookDisabled) {
return $false
}
if (-not (Test-OutlookProfileConfigured)) {
Write-CollectorLog "Outlook profile not configured for current user, Outlook mode disabled"
$script:OutlookDisabled = $true
return $false
}
if (-not (Get-Process -Name OUTLOOK -ErrorAction SilentlyContinue | Select-Object -First 1)) {
Write-CollectorLog "Outlook process not running, skipping COM initialization"
return $false
}
try {
$script:OutlookApp = New-Object -ComObject Outlook.Application
$script:OutlookApp = [Runtime.InteropServices.Marshal]::GetActiveObject('Outlook.Application')
$script:OutlookNamespace = $script:OutlookApp.GetNamespace('MAPI')
$script:SentFolder = $script:OutlookNamespace.GetDefaultFolder(5) # olFolderSentMail
Write-CollectorLog "Outlook COM initialized, Sent Items folder opened"
@@ -335,6 +350,32 @@ function Initialize-OutlookCom {
}
}
function Test-OutlookProfileConfigured {
[OutputType([bool])]
$officeRoots = @(
'HKCU:\Software\Microsoft\Office',
'HKCU:\Software\WOW6432Node\Microsoft\Office'
)
foreach ($root in $officeRoots) {
if (-not (Test-Path -LiteralPath $root)) { continue }
$versions = Get-ChildItem -LiteralPath $root -ErrorAction SilentlyContinue |
Where-Object { $_.PSChildName -match '^\d+\.\d+$' } |
Sort-Object { [version]$_.PSChildName } -Descending
foreach ($ver in $versions) {
$profilesPath = Join-Path $ver.PSPath 'Outlook\Profiles'
if (Test-Path -LiteralPath $profilesPath) {
$profiles = Get-ChildItem -LiteralPath $profilesPath -ErrorAction SilentlyContinue
if ($profiles -and $profiles.Count -gt 0) {
return $true
}
}
}
}
return $false
}
function Get-OutlookSentItems {
param([datetime]$Since)
@@ -521,6 +562,7 @@ $script:OutlookApp = $null
$script:OutlookNamespace = $null
$script:SentFolder = $null
$script:OutlookLastPoll = (Get-Date).AddMinutes(-5)
$script:OutlookDisabled = $false
Load-EmailPolicy -Path $resolvedPolicyPath
Write-CollectorLog ("email collector started mode={0} against {1}" -f $Mode, $script:ApiBase)
+5 -1
View File
@@ -18,6 +18,7 @@ $fileCollectorScript = if ($config.paths.PSObject.Properties.Name -contains 'fil
$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' }
$policyClientScript = if ($config.paths.PSObject.Properties.Name -contains 'policyClientScript') { [string]$config.paths.policyClientScript } else { Join-Path $stateRoot 'dlp-policy-client.ps1' }
$launchScript = [string]$config.paths.launchScript
$recoveryScript = [string]$config.paths.recoveryScript
@@ -44,6 +45,7 @@ $requiredFiles = @(
$sessionCollectorScript,
$rulesPath,
$policyPath,
$policyClientScript,
$launchScript,
$recoveryScript,
$ConfigPath
@@ -59,7 +61,9 @@ if ($windowExpected) {
}
$missingFiles = @(
$requiredFiles | Where-Object { -not (Test-Path -LiteralPath $_) }
$requiredFiles |
Where-Object { -not [string]::IsNullOrWhiteSpace([string]$_) } |
Where-Object { -not (Test-Path -LiteralPath $_) }
)
$processNames = @()