- Add privacy projection (core/privacy.py), private report storage, bounded
process capture; omit identities, free text and unapproved paths
- Mask remote peer IPs: only loopback/host/wildcard stay numeric, others are
reduced to remote_scope private/public (schema + scoring updated)
- Map ss TCP states to canonical names; ASCII-only numeric fields; fix
"Microsoft Visual C++" product matching (Python and PowerShell)
- Merge 7fed3b9: -Diagnostic switch, serialization exit code 3, PS7 module
path, UI error detail; keep hardened task/certificate collectors
- Add audit docs, privacy docs and regression tests
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
19 KiB
PHI exposure and data flow
Offline source review, 2026-10-30. Findings reference the working-tree snapshot identified in SECURITY_AUDIT.md. No collectors, patient datasets or production reports were executed/read. Potential exposure is not an assertion that actual PHI was present. Infrastructure identifiers can be sensitive without being PHI; privacy classification depends on patient linkage and healthcare context. No field-name allowlist or HTML escape proves a value PHI-free.
Lifecycle and trust boundaries
flowchart TD
W[Windows CIM / registry / tasks / SMB / certificates] --> P[PowerShell provider objects in memory]
P --> J[Selected JSON fields on stdout]
J --> T[Python TemporaryFile on execution host]
T --> V[JSON parse / schema / shallow field projection]
I[User-selected JSON inventory] --> V
L[Linux metadata / full cron text / utility output] --> M[Python collection memory and temp output]
M --> A[Assessment evidence and summaries]
V --> A
A --> U[Qt widgets and session history]
A --> H[HTML actionable report]
A --> F[Full JSON assessment]
V --> R[Automatic full-inventory JSON sidecar]
H --> D[User-selected filesystem destination]
F --> D
R --> D
P -. runtime unverified .-> X[OS transcripts / EDR / dumps / pagefile]
U -. runtime unverified .-> X
This is a code-derived diagram, not a captured runtime trace. Filesystem destinations may be local or network-backed. The diagrams' arrows do not indicate a confirmed external disclosure. No application-managed privacy boundary exists between retained free text and report output.
Windows collector-by-collector inventory
For all rows below: the section driver serializes selected fields at powershell/Get-ServerInventory.ps1:31-54; the Python worker captures stdout in temporary storage at samsara/collectors/windows.py:60-86; projection occurs at :175, not at acquisition. Raw ErrorRecords/provider objects remain local process memory; exact native memory, logging and persistence behavior is Unverified.
| File / function and source lines | Actual data source and fields selected | Potential PHI / severity | Filtering and memory versus export | Recommended disposition |
|---|---|---|---|---|
powershell/Get-ServerSystem.ps1:1-34, Get-SamsaraSystem |
[Environment]::MachineName; local Win32_OperatingSystem Caption/Version/BuildNumber/LastBootUpTime/ProductType; Win32_ComputerSystem Domain/PartOfDomain; Win32_NetworkAdapterConfiguration Description/IPAddress for IPEnabled adapters |
Medium, potentially High where patient/device naming is used: hostname, domain, adapter description and IP/patient-device linkage | Explicit CIM properties reduce scope. OS identity/build values are constrained indirectly, but names/descriptions/addresses are not privacy-filtered. Selected strings reach PowerShell memory, temp JSON, Python, UI and full JSON. OS name/hostname also appear in HTML. | Prefer approved opaque host/interface identifiers and numeric OS attributes. Require naming-policy assurance; suppress unapproved descriptions and raw addresses where not essential. |
powershell/Get-ServerRoles.ps1:1-10, Get-SamsaraRoles |
Local Get-WindowsFeature, Installed filter; Name/DisplayName/InstallState/FeatureType |
Low PHI likelihood for built-in catalogue entries; still unrestricted provider strings and confidential role inventory | Projects fields and selects installed entries. All values enter temp/memory/raw JSON; findings may repeat role names in HTML. No content redaction. | Approve fixed feature IDs/enums, not arbitrary display text. Keep catalogue and integrity controls. |
powershell/Get-ServerServices.ps1:1-11, Get-SamsaraServices |
Local CIM Win32_Service Name/DisplayName/State/StartMode/StartName/ProcessId |
High conditional exposure: custom service names/display names and account names may identify a patient or patient-specific workflow | Explicit property query excludes Description and PathName. No service executable arguments requested. Retained names/accounts are unredacted in memory/temp/full JSON; analyzer evidence embeds service/account strings (samsara/analyzers/dependencies.py:118-126). |
Use approved service IDs, known catalogue aliases and role-based identities; otherwise omit/tokenize controlled identifiers. Do not label accounts automatically PHI or automatically safe. |
powershell/Get-ServerNetwork.ps1:1-16, Get-SamsaraNetwork |
Local Get-Process Id/ProcessName; Get-NetTCPConnection State/LocalAddress/LocalPort/RemoteAddress/RemotePort/OwningProcess |
Medium, potentially High with patient-named executables or bedside/patient endpoint correlation | No packet payload, DNS lookup, process command line or StartInfo is explicitly accessed. Process objects are received before name/PID projection. Names/endpoints flow through temp/memory and full JSON; evidence can repeat endpoint pairs in HTML (dependencies.py:220-240). |
Approve endpoint granularity, mask/tokenize addresses if not required, constrain process names and retain aggregate activity where possible. A port/process heuristic is not privacy screening. |
powershell/Get-ServerTasks.ps1:1-20, Get-SamsaraTasks |
Local Get-ScheduledTask; TaskName/TaskPath, Settings.Enabled, State, Principal.UserId, Actions.Execute, selected trigger class/start/enabled/interval/day/repetition fields |
High: task folder/name/account/action basename can contain patient identifiers; full task definitions may also contain PHI-bearing arguments, descriptions or event subscriptions | Whole task/Actions/Triggers objects precede projection; exact provider materialization of omitted fields is Unverified. Arguments/XML/subscriptions are not serialized by the collector. Execute is reduced to a regex-approved basename (Inventory.Common.ps1:29-34), but allowed filenames can themselves contain identifiers. Selected values enter temp/memory/JSON/HTML evidence. |
Disable unrestricted task-definition acquisition until approved. Minimize at source, avoid event payloads/arguments, map to approved task IDs and approved trigger enums. Redacting after retrieval does not meet a no-PHI-processing claim. |
powershell/Get-ServerSoftware.ps1:1-37, Read-SamsaraSoftwareEntries / Get-SamsaraSoftware |
HKLM uninstall registrations under explicit Registry64 and Registry32; subkey names read to enumerate; DisplayName/Publisher/DisplayVersion/InstallDate; registry view label | High conditional exposure: custom registration display strings can carry identifiers or secrets | Only four named values are selected, not uninstall commands/product repair data; no Win32_Product. Registry handles read-only/disposed. No value/type/content privacy rule; GetValue default handling of expandable registry strings needs review. Selected values enter temp, memory and raw JSON; name hints can enter HTML. |
Fixed approved product identifiers/version formats; reject unapproved types and expandable substitutions; omit custom free text unless classified and approved. |
powershell/Get-ServerStorage.ps1:1-12, Get-SamsaraStorage |
Local Get-Volume label/drive letter or path, filesystem, Size/SizeRemaining |
High conditional exposure through filesystem labels or mounted path names | Does not enumerate files or read volume contents. Labels/paths remain unrestricted and enter temp/memory/full JSON. | Collect numeric capacity and approved volume IDs; suppress free-text labels/full paths unless necessary and approved. |
powershell/Get-ServerStorage.ps1:14-47, Get-SamsaraShares share inventory |
Local Get-SmbShare Name/Path/Special; type label and capacity placeholders |
High: share name/path may itself identify a patient or clinical case | Includes administrative shares in raw inventory; exclusion from scoring is not exclusion from collection/export. Full configured paths are retained in temp/memory/full JSON and can enter actionable evidence/HTML. No content privacy filtering. | Approved opaque share IDs and activity summaries; explicit authorization for detailed share paths; do not confuse low risk-score impact with low privacy exposure. |
powershell/Get-ServerStorage.ps1:22-43, Get-SamsaraShares activity query |
Unfiltered local Get-SmbOpenFile objects; accesses full Path, ShareRelativePath and ClientComputerName. Reduces matched entries to observed_open_files, distinct observed_client_count, activity_source/time |
High, even when final output is aggregate: patient-related file/relative names and client metadata can enter memory before filtering | $openFiles retains all returned objects during processing, including entries outside selected business shares. ClientUserName/other fields may be returned by the provider though not projected or used. Full paths/client names are not copied into selected JSON; only counts/time/source and configured share paths survive. PowerShell errors/OS memory/logging remain potential secondary sinks. |
If no PHI processing is required, disable this query or use a separately approved upstream aggregation source that does not expose patient-bearing records. If retained, treat as sensitive processing; prove native field acquisition, aggregation correctness and logging behavior with synthetic canaries. |
powershell/Get-ServerCertificates.ps1:1-17, Get-SamsaraCertificates |
Get-ChildItem Cert:\LocalMachine, then each returned store PSPath; Subject/Issuer/NotAfter/Thumbprint/store name |
High conditional exposure: subject/issuer can contain patient identity or clinical context | No explicit private-key/Extensions property read or key export. Public certificate objects precede projection, so public extension/SAN materialization in provider memory is Unverified. Selected full subject/issuer strings are not redacted; enter temp/memory/raw JSON. | Prefer expiry/count/store/approved purpose IDs. Omit subject/issuer or require a reviewed certificate naming/data policy. Do not claim certificate metadata inherently excludes PHI. |
powershell/Inventory.Common.ps1:4-26, safe-error helpers |
Catch provider ErrorRecord; inspect category/exception type/HResult; retain static scope, code and fixed message | Medium processing potential; low application-serialized-error exposure for current fixed helper calls | Raw ErrorRecord exists in PowerShell and can remain in $Error/engine memory. Only fixed categories/messages are serialized. Direct driver/module-load failures and external logging are not completely covered. |
Keep categorical errors; prohibit raw exception/TargetObject/stack serialization; verify transcripts/EDR and all failure branches with synthetic strings. |
Sources not explicitly queried by current Windows collectors
| Requested area | Source-review conclusion | Remaining caveat |
|---|---|---|
| Windows event logs / application logs | No Get-WinEvent, Get-EventLog, generic log-file reader or event-record collector identified |
Task definitions may contain event-query metadata; OS logging is a separate output concern, not an implemented input collector. |
| SQL Server clinical/application data | No SQL client, query, database connection, table/listener probing or clinical database metadata collector | SQL/service/software names are examined as strings; those names can still be identifying. |
| Process command lines | Not explicitly collected; service PathName and task Arguments are not serialized | Task definition objects and error contexts precede projection. Do not generalize output exclusion into proof of no in-memory access. |
| Arbitrary Windows file contents | No general file reader against assessed volumes | SMB open-file metadata and paths are queried; app/import/report/policy paths can point to arbitrary operator-readable files. |
| Secrets/private keys | No explicit credential prompt, password store or private-key export | Inherited environment, free text, registry expansion, task provider objects and files can carry secrets incidentally. |
Linux, imported data and downstream processing
| Path | Fields / processing | PHI exposure and recommendation |
|---|---|---|
samsara/collectors/linux.py:100-195 |
Hostname, /etc/os-release, /proc/stat, ip -j address, systemd service descriptions/state, TCP ss -p, RPM/dpkg text, /proc/self/mountinfo |
High conditional exposure through names/descriptions/mount sources/paths. Utility output is written to temporary files (:29-49). No packet payload or mounted remote file contents intentionally read. Apply the same approved field and source rules as Windows. |
samsara/collectors/linux_schedules.py:9-74 |
Reads full /etc/crontab and regular files in /etc/cron.d; parses complete command strings with shlex; exports safe executable/script names, account, source path/line, schedule |
High processing potential: arguments, inline code and environment content are in memory before omission. Filename character checks are not PHI detection. Preliminary symlink checks are not an atomic no-follow guarantee. Do not run this Linux path on healthcare hosts under a no-PHI-processing claim without redesign/approval. |
samsara/collectors/linux_schedules.py:77-95 |
Reads system timer IDs, calendar/next occurrence, associated service | Medium conditional free-text names; no jobs executed. Approve identifiers; keep source data bounded. |
samsara/collectors/importer.py:10-53 |
Reads up to 10 MiB from arbitrary file, deserializes full report wrapper/inventory, then normalizes diagnostics and shallowly projects fields | High: the complete input enters raw bytes/JSON memory before redaction. Permitted free text and check names remain; some nested allowed values lack a closed schema. Accept only approved data contracts; reject unknown structures, remote paths and unsafe values before normal processing as far as feasible. |
samsara/models/inventory.py:22-41; schema |
Property-name selection, not privacy/value enforcement | High control gap: an allowed field can contain patient text or, where weakly typed, a nested unapproved object. Close schemas and field-value contracts; data minimization must also occur upstream. |
samsara/analyzers/dependencies.py:93-243; assessment.py:9-78; summary.py:9-37 |
String normalization/classification; evidence includes names/accounts/share paths/network endpoints; JSON contains policy and evidence references | High onward exposure: analysis copies/derives information rather than de-identifying it. Privacy projection must precede evidence construction and apply to every output view. Risk classification is not PHI classification. |
samsara/analyzers/policy.py:13-44 |
User-selected JSON; free policy name plus constrained numeric rules | Medium: imported policy text reaches memory/UI/report; not an assessment collector but a possible unapproved free-text channel. Limit policy provenance/approved names and protect deployable files. |
samsara/ui/window.py:401-479 |
Raw data and assessments remain in memory/history; widgets display strings and detail tables | High conditional exposure to users, screenshots, accessibility tooling, dumps/pagefiles; no session retention bound/privacy view. Plain-text rendering reduces injection, not disclosure. |
samsara/reporting/export.py:15-29, :39-86 |
Complete raw inventory sidecar, complete JSON assessment, selected HTML evidence and check text | High: HTML is not the complete disclosure surface. Raw sidecar/JSON retain more data. html.escape does not redact PHI. Export no approved root/ACL/encryption/retention policy. |
tools/validate_windows_host.py:24-45 |
Full private inventory/report; separate release/status/count summary printed and saved | Full outputs remain High conditional exposure. The summary deliberately excludes direct host/account/IP/path/certificate identifiers but is not formally de-identified or independently privacy-reviewed. Do not advertise any generated artifact as safe to publish without review. |
| Bundled demos/tests | Synthetic fixtures and explicit fake-secret test strings | No actual PHI established in the reviewed fixtures. A synthetic flag, filename or sample label is not a technical privacy control for modified/imported content. |
Secondary sinks and data residency
- PowerShell/provider memory: includes broad query results and caught ErrorRecords. Provider buffering, automatic
$Error, transcription and EDR behavior are not under source-only assurance. - Python memory: bytes, decoded JSON, dictionaries, evidence strings and history can coexist; closing a worker does not prove secure erasure.
- Temporary stdout: on the local execution host, before final projection. Default deletion is not evidence of secure erasure or approved storage.
- UI/desktop: collection data are readable by the interactive operator and potentially desktop support/accessibility/screen-capture tools subject to host policy.
- Export files: full JSON, sidecars and HTML have normal inherited permissions; content hash is not encryption/pseudonymization. Copies, backup, indexing and retention are unmanaged by the app.
- Operating-system logs/dumps: Unverified. Do not disable required security logging as a privacy workaround; validate approved logging destinations and prevent unapproved data acquisition first.
- Network filesystems: user-selected input/output paths can relocate data and cause OS authentication/network traffic without an HTTP library in Samsara.
PHI-safe design recommendation
Adopt one explicit CHS-approved model: either restrict acquisition to pre-approved non-patient-bearing fields/sources so patient values never enter the application, or formally treat Samsara as a potentially sensitive/PHI-processing tool with approved safeguards. The latter does not satisfy an absolute promise of no PHI processing.
A safer architecture uses source-level minimal numeric/enumerated observations, approved opaque infrastructure identifiers, default-disabled high-risk SMB/task/certificate free-text acquisition, strict typed/depth-limited contracts, centralized privacy projection before analysis and all exports, and a separately authorized raw-evidence workflow. If pseudonymization requires reading a sensitive original value, acknowledge that original processing and protect key management outside the repository. Pattern-based redaction alone cannot prove PHI absence. Test with unmistakably synthetic privacy canaries, not real patients; inspect memory/temp/EDR/report surfaces under the intended host policy before claiming the control works.
A further diagnostic path is samsara/core/detection.py:41-49 plus samsara/ui/window.py:31-52: OS/profile errors include the detected OS name and ValueError text in UI status. Current explicit validation messages are largely fixed, but this path can repeat a source-provided OS-name string. There is no dedicated application file logger; external PowerShell/OS/desktop logs are not thereby excluded. Generic unexpected worker exceptions are replaced with fixed text.