{"findings":[{"severity":"Medium","file":"scripts/health/dashboard_v5/read_api.py","line":3569,"issue":"Public medication payloads expose deterministic, unkeyed SHA-256 business/preview revisions derived from internal row fields, including numeric database IDs. The supposedly opaque prescription namespace is therefore reversible when the other revision inputs are known: against the synthetic fixture, the public prescription fields plus `business_revision` recovered internal prescription ID 1 by simple enumeration. The same construction also lets a client derive preview revisions instead of obtaining an unforgeable server-issued preview token, weakening both internal-ID opacity and the server-side preview gate.","remediation":"Remove raw deterministic `business_revision` values from public responses. Generate all externally visible concurrency/preview tokens with a domain-separated HMAC under a server-held secret, covering the complete normalized payload and current database context; verify that HMAC in the worker. Add a regression test that public fields cannot distinguish candidate numeric IDs or synthesize an accepted preview token without the server secret."},{"severity":"Medium","file":"scripts/health/dashboard_v5/read_api.py","line":3521,"issue":"The `source` filter fails open. `safe_metadata_text()` returns `None` for rejected path/URL/Drive-like input, but line 3618 applies filtering only when the result is truthy. Consequently a request such as `source=https%3A%2F%2Fevil.example` returns the complete medication history, whereas an ordinary nonmatching source returns zero rows. A malformed privacy-scoping filter must not broaden PHI disclosure.","remediation":"Distinguish an omitted/empty source from a supplied-but-rejected source. Return a bounded 400 error for any nonempty source that fails metadata validation, and add endpoint tests proving malformed/path/URL/Drive-like filters never return an unfiltered payload."},{"severity":"Medium","file":"scripts/health/assets/health-assets/dashboard-v5-record.js","line":190,"issue":"The selected prescription's stable `medrx_…` identifier is copied into the canonical page URL through `history.pushState`. This leaves a durable, correlatable medication identifier in browser history and same-origin request logs despite the intended opaque-reference boundary.","remediation":"Keep prescription selection out of `location.search`; store it in ephemeral in-memory/history state and use it only when constructing the authenticated API request. If reload restoration is required, use a short-lived session-scoped handle that cannot correlate the prescription across sessions, and test that medication names, event references, prescription references, and revision tokens never appear in the visible URL."}],"verdict":"BLOCK"}