{ "findings": [ { "severity": "High", "file": "scripts/health/migrate_sprint7c_f_medication_schema.py", "line": 106, "issue": "PHI-bearing backup and restore databases are created through ordinary sqlite3 connections before their mode is restricted. On an error, chmod is never reached and the partial database remains behind. A deterministic probe with umask 022 and a corrupt source left the target at mode 0644 inside a 0755 directory. The code also accepts arbitrary output directories without enforcing containment under a dedicated private root or rejecting symlink/race substitutions.", "remediation": "Require all artifacts to be canonically contained beneath a configured owner-only private root. Validate every path component without following symlinks, pre-create each output inode using O_CREAT|O_EXCL|O_NOFOLLOW with mode 0600, verify owner/type/mode before SQLite opens it, use owner-only 0700 directories, and securely remove incomplete outputs on every exception. Apply the same protections to backup, restore-proof, migrated candidate, and report targets." }, { "severity": "Medium", "file": "scripts/health/dashboard_v5/medication_schema.py", "line": 165, "issue": "The event immutability trigger only fires when OLD.business_revision is non-null, while no event UPDATE validation trigger handles a legacy row transitioning from NULL to a revision. A legacy row can therefore be updated into an allegedly structured event with an invalid status, missing medication relation, or malformed revision, after which it becomes immutable. A synthetic probe successfully committed event_type='INVALID', medication_id=NULL, and a non-hex revision.", "remediation": "Prohibit updates that transition medication_administrations.business_revision from NULL to non-null, or add a BEFORE UPDATE validator that enforces the complete insert contract before allowing that transition. Prefer creating structured events only through INSERT and retain legacy rows unchanged." }, { "severity": "Medium", "file": "scripts/health/dashboard_v5/medication_contract.py", "line": 145, "issue": "The preview revision binds only the prescription, planned event, and correction target. It does not bind the action's timestamp, status, dose, route, injection details, lot number, note, confirmation flags, or correction content. The worker therefore cannot prove that the submitted medical event is the exact event shown in the user-visible confirmation preview; the later action_hash only provides replay idempotency after receipt.", "remediation": "Have the server issue a short-lived, one-time preview token over the canonical complete action payload plus all context revisions, session identity, and expiry. Verify and consume that token atomically in the same BEGIN IMMEDIATE transaction as the event insert. Do not treat client-side frozen state or duplicate_confirmed as proof of preview confirmation." }, { "severity": "Medium", "file": "scripts/health/dashboard_v5/medication_contract.py", "line": 85, "issue": "Medication and event references are unkeyed hashes of predictable internal integer IDs plus values disclosed in the same API response, such as medication name and date. An authenticated client can brute-force the small integer ID space and recover the underlying database IDs, defeating the intended opaque identifier boundary and potentially enabling correlation with legacy numeric namespaces.", "remediation": "Use persistent random opaque identifiers or a secret-keyed HMAC with domain separation. Never derive public identifiers from guessable internal IDs and publicly returned fields, and test that public medication identifiers cannot be transformed into legacy numeric routes." }, { "severity": "Medium", "file": "scripts/health/assets/health-assets/dashboard-v5-record.js", "line": 778, "issue": "The medication filter uses the private medication name as a query-string and URL-state value. Medication names can consequently persist in browser history and be copied into request/access logs or diagnostic captures even though opaque prescription references are already available.", "remediation": "Use the opaque prescription reference as the filter value and keep the medication name display-only. Ensure Referrer-Policy: no-referrer, Cache-Control: no-store, and redacted request logging remain enforced, and migrate or clear existing name-bearing URL state." }, { "severity": "Low", "file": "scripts/health/migrate_sprint7c_f_medication_schema.py", "line": 232, "issue": "The migration report includes absolute backup and restore-proof paths and is always printed to stdout. Those paths may disclose private health-storage layout through shell history, CI/systemd logs, or captured migration evidence.", "remediation": "Return opaque artifact labels or paths relative to the approved private root, and make detailed path output an explicit local-only diagnostic option. Avoid printing private paths to stdout by default." } ], "verdict": "BLOCK: the uncommitted Sprint 7C-F candidate has a confirmed PHI file-permission failure path plus unresolved structured-event integrity, preview-binding, identifier-opacity, and URL privacy weaknesses. No SQL or DOM-based HTML/JavaScript injection was found in the reviewed diff; SQL values are parameterized and displayed database values use text nodes. The focused pytest suite could not be executed because pytest is unavailable in the review environment, and the review did not modify repository files." }