Files
oc-sentinel/installer/runtime-run-ocsentinel-monitor.ps1
OfficeCom Codex 91c5794502
Some checks failed
OfficeCom Sentinel Client / validate-client (push) Successful in 23s
OfficeCom Sentinel Client / build-client-windows (push) Failing after 18s
Add daily scans and Ninja burst mode
2026-07-26 02:17:03 +02:00

236 lines
7.5 KiB
PowerShell

param(
[int]$LookbackDays = 7,
[int]$TopCount = 10,
[string]$OutputPath = "..\reports\ocsentinel-summary.json",
[string]$ConfigPath = "..\config\ocsentinel-settings.json",
[string]$ClientConfigPath = "..\config\ocsentinel-client.json",
[string]$SecretPath = "",
[ValidateSet("disabled", "auto", "required")]
[string]$UploadMode = "auto",
[string]$VulnerabilityCsvPath = "",
[string]$MirrorRoot = "",
[switch]$SuppressTriggerExit,
[ValidateSet("status", "attack-only", "cve-critical", "attack-plus-cve")]
[string]$Mode = "status"
)
$ErrorActionPreference = "Stop"
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$runnerScript = Join-Path $scriptDir "run-ocsentinel.ps1"
$outputFullPath = [System.IO.Path]::GetFullPath((Join-Path $scriptDir $OutputPath))
$script:NinjaFieldBackend = $null
$script:NinjaCliPath = "C:\ProgramData\NinjaRMMAgent\ninjarmm-cli.exe"
function Resolve-PathLike {
param(
[Parameter(Mandatory)]
[string]$PathValue,
[Parameter(Mandatory)]
[string]$BasePath
)
if ([string]::IsNullOrWhiteSpace($PathValue)) {
return $PathValue
}
if ([System.IO.Path]::IsPathRooted($PathValue) -or $PathValue.StartsWith("\\")) {
return [System.IO.Path]::GetFullPath($PathValue)
}
return [System.IO.Path]::GetFullPath((Join-Path $BasePath $PathValue))
}
function Initialize-NinjaFieldWriter {
if ($null -ne $script:NinjaFieldBackend) {
return
}
if (Get-Command -Name "Set-NinjaProperty" -ErrorAction SilentlyContinue) {
$script:NinjaFieldBackend = "powershell-modern"
return
}
if (Get-Command -Name "Ninja-Property-Set" -ErrorAction SilentlyContinue) {
$script:NinjaFieldBackend = "powershell-legacy"
return
}
if (Test-Path $script:NinjaCliPath) {
$script:NinjaFieldBackend = "cli"
return
}
$script:NinjaFieldBackend = "none"
}
function Set-NinjaCustomFieldValue {
param(
[Parameter(Mandatory)]
[string]$Name,
[AllowEmptyString()]
[object]$Value,
[Parameter(Mandatory)]
[string]$Type
)
Initialize-NinjaFieldWriter
switch ($script:NinjaFieldBackend) {
"powershell-modern" {
Set-NinjaProperty -Name $Name -Value $Value -Type $Type -Force | Out-Null
return $true
}
"powershell-legacy" {
Ninja-Property-Set -Name $Name -Value $Value | Out-Null
return $true
}
"cli" {
& $script:NinjaCliPath set $Name $Value | Out-Null
return $LASTEXITCODE -eq 0
}
default {
return $false
}
}
}
function Publish-NinjaCustomFields {
param(
[Parameter(Mandatory)]
[pscustomobject]$Report,
[Parameter(Mandatory)]
[string]$Mode,
[Parameter(Mandatory)]
[bool]$Triggered,
[Parameter(Mandatory)]
[string]$Reason
)
Initialize-NinjaFieldWriter
if ($script:NinjaFieldBackend -eq "none") {
Write-Host "Ninja custom fields: skipped (Ninja field writer not available)."
return
}
$generatedAtUtc = ""
if ($Report.GeneratedAtLocal) {
try {
$generatedAtUtc = ([DateTimeOffset]$Report.GeneratedAtLocal).ToUniversalTime().ToString("o")
}
catch {
$generatedAtUtc = [string]$Report.GeneratedAtLocal
}
}
$fieldValues = @(
[pscustomobject]@{ Name = "ocsentinelstatus"; Type = "Text"; Value = [string]$Report.AlertState }
[pscustomobject]@{ Name = "ocsentinelreason"; Type = "Text"; Value = $Reason }
[pscustomobject]@{ Name = "ocsentinelbasestatus"; Type = "Text"; Value = [string]$Report.BaseAlertState }
[pscustomobject]@{ Name = "ocsentinelevents"; Type = "Integer"; Value = [int]$Report.TotalEvents }
[pscustomobject]@{ Name = "ocsentineluniqueips"; Type = "Integer"; Value = [int]$Report.UniqueIpCount }
[pscustomobject]@{ Name = "ocsentinelcvecritical"; Type = "Integer"; Value = [int]$Report.VulnerabilityCorrelation.CriticalCount }
[pscustomobject]@{ Name = "ocsentinelcvetotal"; Type = "Integer"; Value = [int]$Report.VulnerabilityCorrelation.TotalCount }
[pscustomobject]@{ Name = "ocsentinelmode"; Type = "Text"; Value = $Mode }
[pscustomobject]@{ Name = "ocsentineltriggered"; Type = "Checkbox"; Value = $Triggered }
[pscustomobject]@{ Name = "ocsentinellastscanutc"; Type = "DateTime"; Value = $generatedAtUtc }
)
$updated = 0
foreach ($entry in $fieldValues) {
try {
if (Set-NinjaCustomFieldValue -Name $entry.Name -Value $entry.Value -Type $entry.Type) {
$updated++
}
}
catch {
Write-Warning "Failed to set Ninja custom field '$($entry.Name)': $($_.Exception.Message)"
}
}
Write-Host "Ninja custom fields: updated $updated field(s) via $script:NinjaFieldBackend."
}
$runnerArgs = @(
"-ExecutionPolicy", "Bypass",
"-File", $runnerScript,
"-LookbackDays", $LookbackDays,
"-TopCount", $TopCount,
"-OutputPath", $OutputPath,
"-ConfigPath", $ConfigPath,
"-ClientConfigPath", $ClientConfigPath,
"-UploadMode", $UploadMode
)
if (-not [string]::IsNullOrWhiteSpace($SecretPath)) {
$runnerArgs += @("-SecretPath", $SecretPath)
}
if (-not [string]::IsNullOrWhiteSpace($VulnerabilityCsvPath)) {
$runnerArgs += @("-VulnerabilityCsvPath", $VulnerabilityCsvPath)
}
if (-not [string]::IsNullOrWhiteSpace($MirrorRoot)) {
$runnerArgs += @("-MirrorRoot", (Resolve-PathLike -PathValue $MirrorRoot -BasePath $scriptDir))
}
$null = & powershell @runnerArgs
$runnerExitCode = $LASTEXITCODE
if (-not (Test-Path $outputFullPath)) {
throw "Expected report file was not created: $outputFullPath"
}
$report = Get-Content $outputFullPath -Raw | ConvertFrom-Json
$status = [string]$report.AlertState
$baseStatus = [string]$report.BaseAlertState
$events = [int]$report.TotalEvents
$uniqueIps = [int]$report.UniqueIpCount
$criticalCves = [int]$report.VulnerabilityCorrelation.CriticalCount
$totalCves = [int]$report.VulnerabilityCorrelation.TotalCount
$monitorTriggered = $false
$monitorReason = ""
switch ($Mode) {
"status" {
$monitorTriggered = $status -ne "ok"
$monitorReason = "Final status is $status. $($report.AlertReason)"
}
"attack-only" {
$monitorTriggered = $baseStatus -ne "ok"
$monitorReason = "Base attack status is $baseStatus. $($report.BaseAlertReason)"
}
"cve-critical" {
$monitorTriggered = $criticalCves -gt 0
$monitorReason = "Critical/high CVE count is $criticalCves out of total CVEs $totalCves."
}
"attack-plus-cve" {
$monitorTriggered = ($events -gt 0 -and $criticalCves -gt 0)
$monitorReason = "Attack events=$events and critical/high CVEs=$criticalCves."
}
}
Publish-NinjaCustomFields -Report $report -Mode $Mode -Triggered $monitorTriggered -Reason $monitorReason
Write-Host ""
Write-Host "OfficeCom Sentinel monitor mode: $Mode"
Write-Host "Triggered: $monitorTriggered"
Write-Host "Reason: $monitorReason"
Write-Host "Status: $status"
Write-Host "Base status: $baseStatus"
Write-Host "Events: $events"
Write-Host "Unique IPs: $uniqueIps"
Write-Host "Critical/High CVEs: $criticalCves"
Write-Host "Total CVEs: $totalCves"
Write-Host "Report: $outputFullPath"
Write-Host "Runner exit code: $runnerExitCode"
if ($monitorTriggered -and -not $SuppressTriggerExit) {
exit 1
}
exit $runnerExitCode