If you discover a security vulnerability in MedOmni, do not open a public issue.
Email security@thegoatnote.com with:
- A description of the vulnerability
- Steps to reproduce
- Affected versions / components
- Suggested mitigation if you have one
We will acknowledge receipt within 72 hours and aim to provide a fix or remediation plan within 14 days for high-severity issues.
In scope:
- The MedOmni codebase under this repository
- The reproducibility manifest emitter (
scripts/emit_manifest.py) - The pre-commit and CI gates (
.pre-commit-config.yaml,.github/workflows/) - The API-key handling contract (CLAUDE.md §2) — any code path that leaks or commits a cloud LLM API key is in scope as a security defect
- The persona-tagged-graph schema (
findings/research/2026-04-29-medomni-v1-northstar/SPEC.md§5.5)
Out of scope:
- Vulnerabilities in upstream NVIDIA components (CUDA, RAPIDS, NeMo, TensorRT-LLM, vLLM) — please report those directly to NVIDIA
- Vulnerabilities in upstream model weights — please report those to Hugging Face / the weight publisher
- Theoretical attacks against medical-AI systems in general — the LICENSE includes the standard warranty disclaimer; medical decisions remain the responsibility of the credentialed clinician
| Version | Supported |
|---|---|
| 0.1.x | Yes |
| < 0.1.0 | No |
These rules are extracted from real incidents in this project and the broader GOATnote stack. They are non-negotiable:
Reading .env loads its contents into the conversation context, which exposes secrets to LLM
agents and to any logging surface. Use awk to inspect key names only:
awk -F= '/^[A-Z_]+=/ {print $1, "len:", length($2)}' .envDo not use cat .env, grep -E 'KEY=|TOKEN=|SECRET=' .env, or any pipeline that interpolates
the value into a shell variable that gets re-exported.
The 2026-04-29 incident (full postmortem in the private research repo) leaked an HF_TOKEN
through ssh.runpod.io's required PTY allocation. PTYs echo stdin to stdout server-side; the
client cannot disable this. Heredoc, base64, inline-env, and stdin-pipe all leak.
The durable mitigation is scripts/_runpod_ssh.sh, which carries a hard secret-grep guard. It
refuses to forward any command body matching (API_KEY|SECRET|PASSWORD|TOKEN)= patterns or
specific known-secret prefixes (hf_*, nvapi-*, sk-*, sk-ant-*, xai-*, AIza*, ghp_*,
ghs_*).
Provision pod secrets via:
- The pod provider's console environment-variable UI, or
- A separate user-controlled SSH session that is not under agent control
Never via a Claude-driven shell.
Per CLAUDE.md §2, cloud LLM keys are scoped, not forbidden: the demo BFF uses an
Anthropic key held only in Vercel project env, eval graders source keys out-of-band on the
operator's machine, and CI uses the ANTHROPIC_API_KEY_PR_REVIEW repo secret. (The historical
"exactly two secrets / no cloud LLM keys in any code path" contract described the pre-June-2026
self-hosted deployment.)
Key VALUES in the repo — OPENAI_API_KEY=sk-..., ANTHROPIC_API_KEY=sk-ant-..., etc. — are
blocked by:
- The
no-cloud-llm-keys.shpre-commit hook - The
detect-secretsbaseline (Yelp, Apache-2.0; aligns with theNVIDIA-NeMoorg convention used by NeMo, NeMo-Run, and NeMo-Curator) - TruffleHog OSS in CI — verifier-based scanning that actively calls
candidate-secret APIs to confirm a match is a live credential, not just
a regex hit (700+ verifiers, ~near-zero false-positive rate in
--results=verifiedmode). Aligns with theNVIDIAcore convention used byNVIDIA/bionemo-framework,NVIDIA/Model-Optimizer, and packaged inNVIDIA/dsx-github-actions/.../trufflehog-scan/action.yml. Replaces the previousgitleaks-actionstep (which moved to a paid-license model in 2025). - A grep CI gate on every PR
If you find yourself adding one of those keys to a script or an env-template, the design is wrong. The judge runs locally on H100. The serve runs locally on H200. External keys defeat the entire premise.
The 2026-04-27 prism42 incident and the 2026-04-29 medomni incident both involved commands that printed env-var contents to a shell that was being captured by a logging surface. Do not run:
systemctl show ... --property=Environmentcat /proc/*/environ- bare
printenvorenv(without filter) cat .envgrep -E 'KEY=|TOKEN=|SECRET='over a file containing values
Use the awk pattern above to read key names without values.
No --no-verify. If a pre-commit hook fails, fix the underlying issue and create a new commit
— not --amend. After a hook failure, the commit did not happen, so --amend would modify the
previous commit (potentially losing work).
Required status checks: lint, test, manifest-verify, secrets-scan. No force-push to
main. No deletions. enforce_admins=false allows admin bypass for solo-dev sprints; this is
intentional and reviewed quarterly.
MedOmni is a clinical decision-support codebase. Its threat model is shaped by:
- Hallucinated medical content — addressed by the constrained-decoding cite rail (SPEC §5.3 stage 9) and the output-rail Nemotron-Content-Safety-Reasoning-4B with clinical policy.
- PHI leak — the demo forbids PHI entry (persistent banner) because the current architecture sends queries to the Anthropic Claude API; the historical self-hosted deployment kept inference on-pod. Do not represent the demo as PHI-safe.
- Adversarial jailbreak — addressed by the input-rail NemoGuard JailbreakDetect.
- Methodology-side circularity — addressed by held-out fixtures with non-overlapping evidence base, cross-family judge, and N≥3 seeded trials with deterministic decoding.
- Supply-chain compromise — addressed by revision-pinned weights in the 9-layer manifest,
detect-secrets+ TruffleHog OSS dual-layer secret scanning (regex/entropy + verifier-based, both NVIDIA OSS conventions), and pinned dependency versions inpyproject.toml+uv.lock.
We follow standard 90-day coordinated disclosure. After acknowledgment:
- Day 0–14: triage + assignment
- Day 14–60: fix development + testing
- Day 60–90: coordinated release
- Day 90+: public disclosure (with credit to the reporter, if they wish)