fix(windows): disable outlook popup and enforce smtp-only email monitoring
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 `
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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 = @()
|
||||
|
||||
Reference in New Issue
Block a user