Add a `retain` block to privacy_policy.json (all on by default) so assessments show software, services, processes, tasks, shares/mounts and private peer IPs. Values pass an identifier-pattern filter (digit runs, SSN/phone, e-mail, charset, length, path depth). Accounts, command lines, child paths, filenames and public peer IPs stay omitted. Linux collector and Windows PowerShell collectors updated; tests cover retention, withholding and all-switches-off. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
77 lines
6.3 KiB
PowerShell
77 lines
6.3 KiB
PowerShell
#requires -Version 5.1
|
|
# AST and synthetic collector contract tests only. Does not query any real server.
|
|
$ErrorActionPreference = 'Stop'
|
|
$root = Split-Path (Split-Path $PSScriptRoot -Parent) -Parent
|
|
$files = @(Get-ChildItem -LiteralPath (Join-Path $root 'powershell') -Filter '*.ps1')
|
|
foreach ($file in $files) {
|
|
$tokens = $null; $parseErrors = $null
|
|
$null = [System.Management.Automation.Language.Parser]::ParseFile($file.FullName, [ref]$tokens, [ref]$parseErrors)
|
|
if ($parseErrors.Count) { throw ('PowerShell syntax invalid: ' + $file.Name) }
|
|
}
|
|
foreach ($file in @($files | Where-Object { $_.Name -ne 'Get-ServerInventory.ps1' })) { . $file.FullName }
|
|
$script:SamsaraIsElevated = $true
|
|
# Synthetic SECRET canaries must never serialize: run these contract tests with every `retain` switch off.
|
|
Get-SamsaraPrivacyConfiguration
|
|
foreach ($property in @($script:SamsaraPrivacy.retain.PSObject.Properties)) { $property.Value = $false }
|
|
$script:SyntheticBuild = '20348'
|
|
function Get-CimInstance {
|
|
[CmdletBinding()] param($ClassName, $Property, $Filter, $Namespace)
|
|
switch ($ClassName) {
|
|
'Win32_OperatingSystem' { [pscustomobject]@{ Caption='Synthetic Windows Server'; Version=('10.0.' + $script:SyntheticBuild); BuildNumber=$script:SyntheticBuild; ProductType=3; LastBootUpTime=[DateTime]::UtcNow.AddDays(-2) } }
|
|
'Win32_ComputerSystem' { [pscustomobject]@{ Domain='example.test'; PartOfDomain=$true } }
|
|
'Win32_NetworkAdapterConfiguration' { [pscustomobject]@{ Description='Synthetic NIC'; IPAddress=@('192.0.2.20') } }
|
|
'Win32_Service' { [pscustomobject]@{ Name='W3SVC'; DisplayName='Synthetic Web'; State='Running'; StartMode='Auto'; StartName='EXAMPLE\svc_web'; ProcessId=123; PathName='SECRET command arguments' } }
|
|
'MSFT_ScheduledTask' { [pscustomobject]@{ TaskName='SECRET'; TaskPath='\Custom\SECRET\'; State=3 } }
|
|
'Win32_LogicalDisk' { [pscustomobject]@{ DeviceID='C:'; Size=100GB; FreeSpace=50GB; FileSystem='NTFS' } }
|
|
default { throw 'Unexpected synthetic CIM query' }
|
|
}
|
|
}
|
|
function Get-WindowsFeature { [CmdletBinding()] param() [pscustomobject]@{ Name='Web-Server'; DisplayName='Synthetic Web Server'; Installed=$true; InstallState='Installed'; FeatureType='Role' } }
|
|
function Get-Process { [CmdletBinding()] param() [pscustomobject]@{ Id=123; ProcessName='SyntheticWorker'; CommandLine='SECRET' } }
|
|
function Get-NetTCPConnection { [CmdletBinding()] param() [pscustomobject]@{ State='Established'; LocalAddress='192.0.2.20'; LocalPort=443; RemoteAddress='192.0.2.21'; RemotePort=50000; OwningProcess=123 } }
|
|
function Get-ScheduledTask { throw 'Forbidden task object enumeration' }
|
|
function Get-Volume { throw 'Forbidden volume label enumeration' }
|
|
function Get-SmbShare { [CmdletBinding()] param() [pscustomobject]@{ Name='SECRET'; Path='C:\SECRET'; Special=$false } }
|
|
function Get-SmbOpenFile { throw 'Forbidden open-file enumeration' }
|
|
function Get-ChildItem { throw 'Forbidden certificate enumeration' }
|
|
function Assert-True { param($Value, [string]$Message) if (-not $Value) { throw $Message } }
|
|
$collectors = [ordered]@{
|
|
system={param($r) Get-SamsaraSystem $r}; roles={param($r) Get-SamsaraRoles $r}
|
|
services={param($r) Get-SamsaraServices $r}; network={param($r) Get-SamsaraNetwork $r}
|
|
tasks={param($r) Get-SamsaraTasks $r}; storage={param($r) Get-SamsaraStorage $r}
|
|
shares={param($r) Get-SamsaraShares $r}; certificates={param($r) Get-SamsaraCertificates $r}
|
|
}
|
|
foreach ($name in $collectors.Keys) {
|
|
$section = Invoke-SamsaraSection $name $collectors[$name]
|
|
$expected = if ($name -eq 'certificates') { 'not_collected' } elseif ($name -in @('tasks','storage','shares')) { 'partial' } else { 'complete' }
|
|
Assert-True ($section.status -eq $expected) ('Synthetic section failed: ' + $name)
|
|
Assert-True ($section.started_at -and $section.completed_at) 'Missing timestamps'
|
|
Assert-True (($section | ConvertTo-Json -Depth 12) -notmatch 'SECRET') 'Secret field serialized'
|
|
if ($name -eq 'network') { Assert-True ($section.data[0].process -eq 'processes-123') 'Process correlation failed' }
|
|
if ($name -eq 'shares') { Assert-True ($null -eq $section.data[0].observed_open_files) 'Unexpected open-file evidence' }
|
|
if ($name -eq 'tasks') { Assert-True ($null -eq $section.data[0].executables) 'Task action leaked' }
|
|
}
|
|
foreach ($build in @('14393','17763','20348','26100')) {
|
|
$script:SyntheticBuild=$build
|
|
$section=Invoke-SamsaraSection 'system' {param($r) Get-SamsaraSystem $r}
|
|
Assert-True ($section.status -eq 'complete') 'Supported build mapping failed'
|
|
}
|
|
# Test registry projection with in-memory objects; never open a real registry hive.
|
|
$fakeEntry = New-Object PSObject
|
|
$fakeEntry | Add-Member ScriptMethod GetValue { param($key,$default,$options) switch ($key) { 'DisplayName' {'Synthetic application'}; 'DisplayVersion' {'1.0'}; 'Publisher' {'Example'}; 'InstallDate' {'20260101'}; default {throw 'Unexpected registry value requested'} } }
|
|
$fakeEntry | Add-Member ScriptMethod Dispose { }
|
|
$fakeUninstall = New-Object PSObject
|
|
$fakeUninstall | Add-Member ScriptMethod GetSubKeyNames { @('Example') }
|
|
$fakeUninstall | Add-Member ScriptMethod OpenSubKey { param($name,$writable) if ($writable) {throw 'Writable registry access'}; return $fakeEntry }
|
|
$section=Invoke-SamsaraSection 'software' {param($r) Invoke-SamsaraRead $r 'Synthetic registry view' { Read-SamsaraSoftwareEntries $r $fakeUninstall 'Registry64' } }
|
|
Assert-True ($section.status -eq 'complete' -and $section.data[0].name -eq 'products-1') 'Registry metadata projection failed'
|
|
function Get-SmbShare { [CmdletBinding()] param() throw [UnauthorizedAccessException]::new('SECRET protected details') }
|
|
$section=Invoke-SamsaraSection 'shares' {param($r) Get-SamsaraShares $r}
|
|
Assert-True ($section.status -eq 'partial' -and $section.data.Count -eq 0) 'Denied share enumeration was not partial/empty'
|
|
Assert-True ($section.errors[0].code -eq 'access_denied') 'Permission error not categorized'
|
|
Assert-True (($section | ConvertTo-Json -Depth 12) -notmatch 'SECRET') 'Raw error exposed'
|
|
$script:SamsaraIsElevated=$false
|
|
$section=Invoke-SamsaraSection 'services' {param($r) Get-SamsaraServices $r}
|
|
Assert-True ($section.status -eq 'partial' -and $section.permission_limitations.Count -gt 0) 'Non-elevated scope not marked partial'
|
|
Write-Output 'PowerShell AST and synthetic collector tests passed. No real Windows inventory was collected.'
|