Series: Securing MedScribe-R-Us | Part 4 of 5
P2 left us with a pipeline that generates findings. Five tools, running continuously,producing a stream of issues that get triaged, labeled, and dropped into GitHub.
The pipeline works. But a pipeline without a program is just noise with betterformatting. Someone needs to be seeing these findings, triage, and assign fixes. What happens to the findings? Who owns them? What constitutes done?How long does a Critical vulnerability have before it becomes a HIPAA incident?
P3 answers all of that — and builds out the secure architecture reference that givesthe threat model findings somewhere to actually resolve into.
Follow along on the Github: https://github.com/LeSpookyHacker/medscribe-r-us-appsec
The Vulnerability Management Program
SLA Tiers — Why the Numbers Are What They Are
MedScribe-R-Us's SLA tiers aren't arbitrary. Each one is derived from a specificoperational or regulatory constraint:
Severity | Remediation SLA |
|---|---|
🔴 Critical | 24 hours |
🟠 High | 7 Days |
🟡 Medium | 30 Days |
🟢 Low | 90 Days |
The 24-hour Critical SLA is the one that will most likely get the most questions, so I'll explainit directly. Under HIPAA 45 CFR §164.404, the breach notification clock starts themoment a breach is discovered — not when it's confirmed, not when legal reviewsit, not when the PR is merged. If a Critical vulnerability constitutes a probableHIPAA breach without remediation, that clock is already running.
Getting from discovery to remediation decision in 24 hours means the security teamhas time to assess breach status, preserve forensic evidence, and begin notificationpreparation — all before any external deadline pressure exists. 24 hours is tightenough to create urgency and loose enough to be achievable without heroics.
The SLA policy also defines when the clock starts (confirmation, not assignment),when it pauses (vendor dependency with a documented compensating control), andwhat triggers a restart (new exploit published against a paused finding). Theseedge cases are where SLA programs fall apart in practice, so they're documentedexplicitly.
Risk Scoring — CVSS Is Not Enough
CVSS base scores are a starting point. A CVSS 6.5 vulnerability in the PHI scrubbinglayer is not the same risk as a CVSS 6.5 vulnerability in the marketing website'scontact form. But CVSS doesn't know that.
MedScribe-R-Us's risk scoring model applies contextual modifiers to CVSS base scoresbefore assigning a severity tier:
Upward modifiers:
- PHI on path: +2.0
- Auth bypass: +1.5
- Clinician approval gate bypass: +2.0
- Internet-facing component: +1.0
- Active exploit in the wild: +1.5
- Cross-tenant blast radius: +2.0
Downward modifiers:
- Exploitation requires authentication: -1.0
- High-privilege auth required: -1.5
- No sensitive data accessible: -1.0
- Compensating control in place: -1.0
There's also a mandatory Critical floor: any finding with both PHI on path andInternet-facing modifiers has a minimum effective score of 9.0 regardless ofthe CVSS base. PHI exposure through an internet-facing component is always Criticalin a HIPAA environment, full stop.
To make this concrete — T-007 from the threat register, the PHI scrubbing gap, hasa CVSS base score of 6.5. Without contextual scoring, that's Medium. After applyingPHI on path (+2.0) and Auth required (-1.0), the effective score is 7.5 — High.
The difference is meaningful: Medium means 30 days to fix, High means 7 days.
The scoring table applies these modifiers to all 14 STRIDE findings from P1. It'sthe document that makes the threat register actionable rather than academic.
Tracking in GitHub
The vulnerability tracker is GitHub Issues — not because it's the ideal platform,but because it's where the code lives, integrates directly with GitHub AdvancedSecurity's SARIF pipeline, and keeps vulnerability discussions close to the codethey affect.
Please keep in mind that this whole project is in fact a project. Not every company will be using GitHub to hold all their code, or use it for vulnerability management. However, most if not all of the methodolody discussed here is directly transferable to whatever vulnerability management program in your company. So simplicity, this project using GitHub Issues.
Every vulnerability issue carries a mandatory set of labels:
- Severity:
severity:critical,severity:high, etc. - Source:
source:sast,source:dast,source:pentest,source:threat-model - Status:
status:open,status:in-remediation,status:accepted-risk, etc. - Special:
phi-exposure:yes(triggers mandatory AppSec review),sla:at-risk,sla:breached
The sla:breached label is applied automatically by a nightly GitHub Actionsworkflow that checks due dates across all open issues and posts a comment taggingthe assigned engineer and security lead. This is the accountability mechanism — SLAbreach becomes visible and tracked, not silently ignored.
Metrics — First 90 Days
Note that the following section contains both real metric data (addressing threats from P1) and selectively created fake data that simulates real life metrics
The metrics dashboard captures the program's first full reporting period. A fewnumbers worth highlighting:
64 total findings surfaced by the P2 pipeline in 30 days. Of those, 58 weretrue positives resolved within SLA, 5 were false positives, and 1 was accepted risk.The accepted risk was a CVSS 4.1 CVE in libexpat affecting an XML parsing pathnot exercised by any MedScribe service — documented, compensating control noted,90-day re-review date set.
The single Gitleaks finding. A GCP service account key fragment in a comment blockfrom an early development sprint. The key had already been rotated, but the fragmentwas still in git history. Git history was cleaned, no breach assessment required. Thefact that it was caught at all — in a comment, in a historical commit — is the point.That's what full-history scanning finds.
Custom Semgrep rules: 0% false positive rate. Tuned tightly enough to thiscodebase that every flag was a real issue. Community rules had a 17% false positiverate, slightly above target, driven by four flagged instances of a non-standard butintentional JWT implementation pattern. A suppression allowlist has been added andthe rate normalizes to ~9% in the next reporting period.
SLA compliance rate: 91%. Below the 95% target. Three Medium findings from theinitial threat model review weren't resolved within SLA because they hit during aproduct launch sprint. All three are now resolved. The dip is documented, not hidden.
The Secure Architecture Reference
IAM Design: RBAC + ABAC
The IAM design has two layers. RBAC handles the coarse-grained question — can thisrole type access clinical data? ABAC handles the fine-grained question — does thisspecific clinician have a care team relationship with this specific patient?
HIPAA's "minimum necessary" standard requires the ABAC layer. A clinician rolepermits accessing clinical data. But a cardiologist has no business accessing apatient's psychiatry notes, even if both are patients at the same health system.RBAC can't express that constraint. ABAC can.
The ABAC check sits in the authorization middleware on every clinical data request:
1Clinician requests GET /api/v1/notes/{appointment_id}2 │3 ▼4API Gateway validates JWT5(role = clinician, tenant_id = xyz)6 │7 ▼8Authorization Middleware:9 1. Extract patient_id from appointment record10 2. Query ABAC service: is this clinician on this patient's care team?11 3. NO → 403 Forbidden + audit log entry12 4. YES → request proceeds
The ABAC service maintains a 5-minute TTL cache of care team memberships, refreshedfrom the health system's FHIR CareTeam resource. Critically, it fails closed —if the ABAC service is unavailable, access is denied. Clinicians get an error.This is operationally inconvenient and the correct security posture.
Addressing T-011 (horizontal privilege escalation): the tenant_id claim inevery JWT is set server-side from the verified identity provider. It is neveraccepted from the request payload, path parameter, or query string. The authorizationmiddleware validates current_user.tenant_id against the requested resource'stenant_id on every admin endpoint. A Clinic Admin cannot traverse into anothertenant's data because the JWT scope physically prevents it.
Addressing T-001 (credential spoofing): 15-minute access token lifetime, refreshtoken rotation on every use, concurrent session limit of 3, anomalous login detectionthat triggers step-up authentication for logins more than 500 miles from the lastknown location.
Platform Admins — the users with cross-tenant access — get an additionally hardenedposture: hardware security keys only (no TOTP), 4-hour session lifetime with noextension, IP allowlist, and every session action logged to the tamper-evidentaudit trail.
PHI Data Flow — The Lifecycle Document
The PHI data flow document traces every PHI category from entry to deletion.It's the document HIPAA auditors actually want to see. For each stage of the pipeline,it records: what PHI is present, where it's stored, what encryption mechanismprotects it, who can access it, and what integrity control prevents tampering.
A few controls worth calling out specifically:
Addressing T-004 (transcript tampering): raw transcripts are stored in MongoDBwith an HMAC-SHA256 integrity hash. The collection enforces append-only writes atthe database validator layer — once a transcript is stored, it cannot be updated.Any modification attempt is rejected and logged. This means an attacker with MongoDBwrite access still cannot tamper with a stored transcript without breaking the HMAC.
Addressing T-005 (audit log repudiation): audit log entries use a separateMongoDB collection with INSERT-only permissions for the audit writer service account —no UPDATE or DELETE. Logs are replicated in real-time to GCP Cloud Logging, whichis immutable at the platform level. Each audit entry carries an HMAC signature.Breaking the HMAC chain is the only way to tamper with an audit log, and doing sois immediately detectable.
Addressing T-009 (approval gate bypass): the approved_by field on a SOAP noteis write-protected at the MongoDB validator layer. It can only be set by the NoteApproval endpoint when authenticated as the assigned clinician. The EMR IntegrationService reads this field directly from MongoDB before initiating any FHIR write — itnever trusts the approval state from the API request payload.
Addressing T-010 (FHIR patient spoofing): the SMART on FHIR token for everyEMR write is requested with patient/{patient_id} encounter/{encounter_id} scope.Before the write fires, the integration service validates the token's patient claimagainst the appointment's patient_id binding. After the write, the FHIR responseis validated to confirm the note landed on the correct patient and encounter.Binding mismatch at any step halts the write and triggers an immediate alert.
Network Security — One Rule
The GCP VPC design follows a single governing principle: the API Gateway is theonly internet ingress point.
Every internal Cloud Run service is configured with INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER— a GCP networking setting, not an application setting. GCP drops internet trafficat the infrastructure layer before it reaches the container. No amount of applicationmisconfiguration can create an internet-accessible internal service because thenetwork doesn't route the traffic.
This directly addresses T-002 (API Gateway auth bypass). The threat was that amisconfigured Cloud Run ingress setting could expose an internal service directly.The mitigation is enforcing the correct setting at the GCP infrastructure layer andauditing it automatically on every deployment.
A nightly GitHub Actions workflow verifies every Cloud Run service's ingressconfiguration via the GCP API. Any service with an unexpected ingress setting failsthe check and triggers an alert before it reaches production.
The network document also covers:
- VPC Service Controls perimeter around Cloud KMS, Secret Manager, Vertex AI, and GCS — addressing T-013. Even a leaked service account credential cannot access these resources from outside the VPC perimeter.
- Cloud Armor WAF rules — OWASP CRS rules, GCP threat intelligence IP reputation, per-IP rate limiting — addressing T-012 (DoS via large audio). Rate limits cap concurrent audio sessions per IP and per tenant.
- MongoDB Atlas private endpoint — no public MongoDB endpoint exists. The connection string uses a Private Service Connect DNS name; traffic never traverses the public internet.
- Default-deny firewall rules on both ingress and egress — Cloud Run services can only make outbound calls to Google APIs and the MongoDB private endpoint. SSRF exploitation cannot pivot to arbitrary internet infrastructure.
What P3 Closed
Nine STRIDE findings fully addressed in this phase:
T-001, T-003, T-004, T-005, T-009, T-010, T-011, T-012, T-013.
Every finding from the threat model now has a specific control implemented anddocumented. The SLA policy defines how long remediation takes. The risk scoringmodel explains why severities are what they are. The architecture documents tracethe specific design decisions back to the specific threats they address.
The three remaining findings — T-007, T-008, and T-014 — are the AI pipeline threats.Those go to P4.
*All vulnerability management policies, risk scoring documentation, IAM design,PHI lifecycle documentation, and network security architecture are in the repounder docs/vuln-mgmt/ and docs/architecture/
Follow along on the Github! https://github.com/LeSpookyHacker/medscribe-r-us-appsec
All companies, patients, and clinical scenarios are fictional.
— LeSpookyHacker
Small Glossary of Acronyms
Since you are reading this, I am going to assume you know most general AppSec acronyms, so I will only be defining some medical specific ones, or new acronyms that some people may not know yet in the security field.
| Acronym | Definition |
|---|---|
| ABAC | Attribute-Based Access Control |
| ATLAS | Adversarial Threat Landscape for AI Systems (MITRE framework) |
| BA | Business Associate (under HIPAA) |
| CSF | Cybersecurity Framework (NIST) / Common Security Framework (HITRUST) |
| CVSS | Common Vulnerability Scoring System |
| DAST | Dynamic Application Security Testing |
| DNS | Domain Name System |
| DoS | Denial of Service |
| EMR | Electronic Medical Record |
| FHIR | Fast Healthcare Interoperability Resources (R4 refers to Release 4) |
| HIPAA | Health Insurance Portability and Accountability Act |
| HITECH | Health Information Technology for Economic and Clinical Health Act |
| HITRUST | Health Information Trust Alliance |
| HMAC | Hash-based Message Authentication Code |
| MRN | Medical Record Number |
| RBAC | Role-Based Access Control |
| SARIF | Static Analysis Results Interchange Format |
| SAST | Static Application Security Testing |
| SHA | Secure Hash Algorithm |
| SLA | Service Level Agreement |
| SMART | Substitutable Medical Applications and Reusable Technologies |
| SOAP | Subjective, Objective, Assessment, and Plan (Medical clinical note format) |
| SOC | System and Organization Controls |
| STRIDE | Spoofing, Tampering, Repudiation, Information Disclosure, Denial of Service, Elevation of Privilege |
Join the Grimoire
Get notified when I publish new posts. No spam, unsubscribe anytime.