Privacy hardening, remote-peer IP masking and review fixes
- 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>
This commit is contained in:
90
audit/PHI_DATA_FLOW.md
Normal file
90
audit/PHI_DATA_FLOW.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# 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
|
||||
|
||||
```mermaid
|
||||
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
|
||||
|
||||
1. **PowerShell/provider memory:** includes broad query results and caught ErrorRecords. Provider buffering, automatic `$Error`, transcription and EDR behavior are not under source-only assurance.
|
||||
2. **Python memory:** bytes, decoded JSON, dictionaries, evidence strings and history can coexist; closing a worker does not prove secure erasure.
|
||||
3. **Temporary stdout:** on the local execution host, before final projection. Default deletion is not evidence of secure erasure or approved storage.
|
||||
4. **UI/desktop:** collection data are readable by the interactive operator and potentially desktop support/accessibility/screen-capture tools subject to host policy.
|
||||
5. **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.
|
||||
6. **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.
|
||||
7. **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.
|
||||
Reference in New Issue
Block a user