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:
2026-11-04 18:50:24 -08:00
parent 7fed3b9ab9
commit 610a3bb447
51 changed files with 2719 additions and 604 deletions

202
docs/PRIVACY_HARDENING.md Normal file
View File

@@ -0,0 +1,202 @@
# Privacy hardening and production acceptance
This document describes implemented controls, not a HIPAA compliance determination.
The documents in `audit/` remain the historical audit of the earlier source snapshot.
Do not replace their findings with an unsupported claim that all issues are resolved.
## What changed
- A new `core/privacy.py` builds an explicit field projection before results reach Qt,
analysis/history, imports, HTML, JSON, and local CLI output. Export recomputes
findings from minimized inventory; old findings and a privacy marker cannot bypass
the filter. Unexpected schema fields are rejected at import; legacy command fields
are accepted only to discard them. Timestamps are checked without optional plugins.
- Host/domain/adapter identities, accounts, display descriptions, custom service,
software and task names, certificate identities and unapproved paths are omitted
or replaced with assessment-local numbered labels. Known application families,
OS family/version/build, role identifiers, states, PIDs, ports, numeric IPs,
storage capacities, row counts, timestamps and categorical errors remain useful.
Unknown software is not automatically interpreted as inactive or harmless.
- Windows queries no longer request service account/display-name fields. Task CIM
queries request only TaskName, TaskPath and State; actions, arguments, principals,
triggers and task XML are not requested. Disk queries omit volume labels.
Registry reads disable environment expansion and omit publisher/install-date text.
The PowerShell modules project names and paths before emitting JSON.
- Certificate enumeration and SMB open-file/session enumeration are disabled.
Certificates are explicitly `not_collected`; tasks, storage and shares have
partial scope. Share consumers and schedule timing must be verified separately.
Existing imported aggregate share counts can still support a finding, but their
provenance is unverified. They do not cause the new collector to enumerate files.
- Linux default collection no longer reads cron command files or detailed systemd
timer properties. Timer state remains available; commands and timings need a
separate approved scheduler review. Existing schedule parsers are retained for
synthetic tests, but are not called by the default collection workflow.
- Supervised collector stdout uses bounded memory pipes instead of inventory
temporary files. Stderr is discarded. Windows child environment has an explicit
small allowlist instead of copying all environment variables. Timeouts,
cancellation and overflow terminate/reap the owned process.
- Reports contain minimized data only. HTML does not create raw sidecars. Output
is exclusive (no overwrite), restricted to the configured local report root, and
checks common network mounts, symlinks and Windows reparse points. POSIX root
mode is 0700 and files 0600. Windows roots require a protected DACL allowing only
the owner, SYSTEM and Administrators; child ACLs are checked before payload write.
No automatic privilege elevation or existing ACL changes are performed.
- History retains at most ten assessments by default, and has a clear action.
Clearing references is not secure memory erasure. Existing exported files are not
automatically deleted. Scoring-policy titles are canonicalized to prevent a
second free-text report channel.
- Requirements now use the Widgets-capable `PySide6_Essentials`, rather than the
umbrella package that pulls in WebEngine, PDF and Addons. Direct versions are
pinned; observed development transitive constraints are recorded separately.
- Remote peer IP addresses are masked: only loopback, this host's own and wildcard
addresses stay numeric; every other peer is reduced to `remote_scope` `private` or
`public`, because client IPs on patient-facing servers can be identifiers. Local
listener addresses and the host's own IPs are still retained.
## Configure approved metadata and report storage
Deployment administrators review and protect these bundled files against untrusted
changes: `samsara/assets/privacy_policy.json` and `privacy_catalogue.json`. The
catalogue is a finite vocabulary of infrastructure names, not executable code.
Keep the checkout, Python environment and scripts protected by deployment controls.
An attacker who can replace application code/policy is outside this boundary.
The policy defaults to no approved names/paths and an application-local `reports`
folder. This is a laboratory default, not a CHS-approved production location. Set
`report_directory` to an approved **absolute local** directory for deployment.
Only files directly inside it can be saved; choose a new `.html` or `.json` filename.
Do not put the directory under OneDrive, another sync client, a network mount or an
unreviewed backup policy. Encrypted local storage and access/retention policy remain
organizational requirements.
For example, a reviewed Windows configuration may include:
```json
{
"version": "1.0",
"report_directory": "C:\\SamsaraReports",
"approved_hostnames": [],
"approved_share_roots": ["D:\\DepartmentShares"],
"approved_service_names": [],
"approved_software_names": [],
"max_session_assessments": 10
}
```
This preserves **only the exact configured root** `D:\DepartmentShares`. It does
not authorize any child path, filename or file-content scan. Empty allowlists are
preferred. Add an exact hostname/custom service/software name only after privacy
review establishes it is approved infrastructure metadata. There is no automatic
patient-name detector: neither a path nor a free-text name can be proven safe from
its format. Known product names are canonicalized without retaining custom suffixes.
On Linux, the configured root is created with mode 0700 if its parent already
exists. An existing root with group/other permissions is rejected. Fix permissions
on the report directory only after checking that it is the intended directory.
On Windows, provision a **new** local folder before exporting. The following is a
manual setup example to be reviewed under the organization's normal approval
process, not an action the collector performs on assessed infrastructure:
```powershell
$reportRoot = 'C:\SamsaraReports'
if (Test-Path -LiteralPath $reportRoot) { throw 'Choose a new report directory.' }
$null = New-Item -ItemType Directory -Path $reportRoot
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
try {
$acl = New-Object Security.AccessControl.DirectorySecurity
$acl.SetOwner($identity.User)
$acl.SetAccessRuleProtection($true, $false)
foreach ($sid in @($identity.User, [Security.Principal.SecurityIdentifier]::new('S-1-5-18'), [Security.Principal.SecurityIdentifier]::new('S-1-5-32-544'))) {
$rule = [Security.AccessControl.FileSystemAccessRule]::new($sid, 'FullControl', 'ContainerInherit,ObjectInherit', 'None', 'Allow')
$acl.AddAccessRule($rule)
}
Set-Acl -LiteralPath $reportRoot -AclObject $acl
} finally { $identity.Dispose() }
```
These commands change the chosen **local output folder's** ACL and must not be run
against data shares. Configure the matching path in the policy. The ctypes ACL
reader and inheritance behavior are implemented but **not tested on Windows here**.
Before production, verify both allowed and denied cases, inherited file ACLs,
standard-user access, reparse/mapped-drive rejection and directory replacement
races on each intended Windows execution environment. Export fails closed when it
cannot establish the configured checks; absence of an exception is not a complete
filesystem-security attestation.
## Limits and unresolved production blockers
1. **No PHI-free guarantee.** Names/paths returned by CIM, Get-SmbShare, process
listings and registry values can enter collector memory before reduction.
Legacy JSON is parsed in memory before filtering. Explicit properties reduce
requested fields but provider internals are unverified. Approved names/roots
and retained numeric IPs can themselves be sensitive in organizational context.
2. **No absolute read-only OS guarantee.** Application collection commands request
reads, not infrastructure changes. Native providers/OS can write audit logs,
caches or other incidental state. Python bytecode, OS paging/crash dumps, Qt and
endpoint tools are outside the no-inventory-spool control. A bounded buffer is
not secure memory. Disable/restrict crash dumps and use encrypted storage under
organizational policy, rather than claiming that data never touch disk.
3. **No complete egress guarantee.** There is no first-party upload, telemetry or
remote collector. Filesystem checks reject common remote paths but cannot prove
a disk has no cloud-sync/backup/virtualized backend. Native Windows providers,
revocation behavior, domain services and OS integrations need monitored host
validation. Use enterprise egress policy and approved local output storage.
4. **Runtime validation outstanding.** No Windows execution host or PowerShell
runtime was available. Revised CIM projections, least-privilege visibility,
native synthetic tests and output ACL enforcement must pass on approved lab
hosts. Do not substitute synthetic Python results for that evidence.
5. **Supply chain approval outstanding.** No vulnerability database queries,
package installations/updates or external transmissions were performed. Direct
pins/observed constraints are not a hash-verified platform lockfile, complete
native SBOM, signed release or CVE clearance. Existing development environments
may still contain the former Qt umbrella/Addons packages; changing requirements
does not uninstall them. Build clean environments through approved provisioning.
6. **Organizational review outstanding.** CHS security/privacy approval, least-
privilege grants, retention/deletion policy, approved metadata, endpoint and
backup controls, incident response and HIPAA determination remain external
acceptance gates. A low retirement score always needs historical validation,
application ownership and administrator approval.
Do not import known patient-bearing legacy inventories into this tool merely to
redact them. Have the data owner handle those artifacts using approved procedures.
Old reports/sidecars/bundles are not scrubbed or removed by this change. The
historical `Samsara.bundle`, if present, may predate these controls; distribute a
reviewed build of the current source rather than an older archive.
## Verification and later authorized supply-chain checks
Run synthetic/offscreen tests from the repository root:
```bash
QT_QPA_PLATFORM=offscreen .venv/bin/python -m unittest discover -s tests -v
```
On an authorized Windows **lab** Server with an approved report directory:
```bat
.venv\Scripts\python.exe -m unittest discover -s tests -v
.venv\Scripts\python.exe tools\validate_windows_host.py --output-dir C:\SamsaraReports
```
The second command performs a real assessment of the execution host; it is not a
synthetic test. No real Windows host has been assessed during this change.
After obtaining approval for tooling/downloads and advisory-database access, in a
separate release environment use, for example:
```bash
python -m piptools compile --generate-hashes --output-file requirements.lock requirements.txt
python -m pip install --require-hashes -r requirements.lock
python -m pip_audit -r requirements.lock --format json --output vulnerability-results.json
cyclonedx-py environment --output-format JSON --output-file sbom.cdx.json
```
Repeat lock generation/clean installs for each target Python/OS/architecture.
Inventory Qt native libraries/plugins and PowerShell/OS modules as well; a Python
package SBOM alone is incomplete. SBOM/advisory checks can expose package metadata
externally: do not run them against production inventory or without authorization.
Triage findings against actual packages, keep advisory timestamps, sign the
reviewed distribution, and retain acceptance evidence. These commands have not
been run as part of the hardening work.