fix(security): web server hardening, easyRadiology SSRF guard, offline provider stub - #46
Conversation
Addresses three previously unaddressed issues: - #43: build_prompt() now loads detailed anatomy templates from prompts/anatomy/*.txt (knee/shoulder/spine), with cached lookup and fallback to built-in hints. The shipped templates were dead code before. Adds an abdomen hint so all README-advertised regions are covered. - #44: add `medcheck providers` and `medcheck models` discovery commands. - #42: fix README "Report Bug" link (bug_report.md -> bug_report.yml). Updates CHANGELOG and README; adds unit tests for the new loader and CLI commands (72 passing). https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz
…versal - Add missing blank line so `ruff format --check` passes (lint CI was red). - Slug-validate the anatomy region before building a filesystem path in load_anatomy_instructions(), preventing path traversal (CodeRabbit review). - Add regression test for the traversal guard. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz
…dd offline provider stub Addresses three High-severity issues: - #26: web server now binds 127.0.0.1 by default (was 0.0.0.0); add optional X-API-Key auth on /api endpoints via MEDCHECK_API_KEY, and warn when binding to a non-loopback host without a key configured. - #28: validate the easyRadiology linkToERI download URL (HTTPS + host allowlist) and disable redirects to prevent SSRF to internal/metadata hosts. - #36: add LocalLLMProvider stub so the advertised "local" offline fallback degrades gracefully (check_available()=False, actionable NotImplementedError) instead of crashing with "No LLM provider available"; register it in the vision router and `medcheck models`. Updates README, .env.example, CHANGELOG; adds unit tests for all three (89 passing, coverage 66%). https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz
|
Warning Review limit reached
More reviews will be available in 1 minute and 27 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (13)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
…validation Addresses four Medium/High-severity privacy issues: - #27: gate external (cloud) LLM transmission behind explicit consent (--allow-cloud-llm flag, MEDCHECK_ALLOW_EXTERNAL_LLM env, interactive prompt); vision_analysis raises instead of silently sending PHI. - #29: stop logging the raw patient name to stdout — log a non-reversible hash of the patient ID instead. - #30: stop echoing portal access codes into ValueError messages. - #33: add a Pydantic AnalyzeRequest schema with validation for POST /api/analyze (auth was already added in #46). Docs: SECURITY.md "Handling of Patient Data" section, README + .env.example. Tests: +5 (consent gate, credential redaction, request validation); 93 passing. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz
…validation (#47) * feat: anatomy template loader, provider/model discovery CLI, doc fixes Addresses three previously unaddressed issues: - #43: build_prompt() now loads detailed anatomy templates from prompts/anatomy/*.txt (knee/shoulder/spine), with cached lookup and fallback to built-in hints. The shipped templates were dead code before. Adds an abdomen hint so all README-advertised regions are covered. - #44: add `medcheck providers` and `medcheck models` discovery commands. - #42: fix README "Report Bug" link (bug_report.md -> bug_report.yml). Updates CHANGELOG and README; adds unit tests for the new loader and CLI commands (72 passing). https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix: satisfy ruff format and harden anatomy template path against traversal - Add missing blank line so `ruff format --check` passes (lint CI was red). - Slug-validate the anatomy region before building a filesystem path in load_anatomy_instructions(), preventing path traversal (CodeRabbit review). - Add regression test for the traversal guard. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix(security): harden web server, validate easyRadiology downloads, add offline provider stub Addresses three High-severity issues: - #26: web server now binds 127.0.0.1 by default (was 0.0.0.0); add optional X-API-Key auth on /api endpoints via MEDCHECK_API_KEY, and warn when binding to a non-loopback host without a key configured. - #28: validate the easyRadiology linkToERI download URL (HTTPS + host allowlist) and disable redirects to prevent SSRF to internal/metadata hosts. - #36: add LocalLLMProvider stub so the advertised "local" offline fallback degrades gracefully (check_available()=False, actionable NotImplementedError) instead of crashing with "No LLM provider available"; register it in the vision router and `medcheck models`. Updates README, .env.example, CHANGELOG; adds unit tests for all three (89 passing, coverage 66%). https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix(security): PHI consent gate, log/credential redaction, API input validation Addresses four Medium/High-severity privacy issues: - #27: gate external (cloud) LLM transmission behind explicit consent (--allow-cloud-llm flag, MEDCHECK_ALLOW_EXTERNAL_LLM env, interactive prompt); vision_analysis raises instead of silently sending PHI. - #29: stop logging the raw patient name to stdout — log a non-reversible hash of the patient ID instead. - #30: stop echoing portal access codes into ValueError messages. - #33: add a Pydantic AnalyzeRequest schema with validation for POST /api/analyze (auth was already added in #46). Docs: SECURITY.md "Handling of Patient Data" section, README + .env.example. Tests: +5 (consent gate, credential redaction, request validation); 93 passing. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix: address CodeRabbit review on #47 - ingest: stop logging study description (free-text DICOM field may contain PHI) - vision_analysis: honour explicit LLM provider preference (wires up --model) instead of hardcoding "claude"; default to on-device "local" without consent so offline execution stays reachable - docs: soften compliance/anonymity wording (drop "(HIPAA/GDPR)" assertion and "non-reversible" claim; describe pseudonymisation accurately) 94 tests passing. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix: keep 'local' as default vision provider regardless of consent Consent (--allow-cloud-llm / MEDCHECK_ALLOW_EXTERNAL_LLM) now only permits falling back to an external provider; it no longer flips the implicit default from on-device 'local' to 'claude'. Addresses CodeRabbit review on #47. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz --------- Co-authored-by: Claude <noreply@anthropic.com>
…t clarity (#48) * feat: anatomy template loader, provider/model discovery CLI, doc fixes Addresses three previously unaddressed issues: - #43: build_prompt() now loads detailed anatomy templates from prompts/anatomy/*.txt (knee/shoulder/spine), with cached lookup and fallback to built-in hints. The shipped templates were dead code before. Adds an abdomen hint so all README-advertised regions are covered. - #44: add `medcheck providers` and `medcheck models` discovery commands. - #42: fix README "Report Bug" link (bug_report.md -> bug_report.yml). Updates CHANGELOG and README; adds unit tests for the new loader and CLI commands (72 passing). https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix: satisfy ruff format and harden anatomy template path against traversal - Add missing blank line so `ruff format --check` passes (lint CI was red). - Slug-validate the anatomy region before building a filesystem path in load_anatomy_instructions(), preventing path traversal (CodeRabbit review). - Add regression test for the traversal guard. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix(security): harden web server, validate easyRadiology downloads, add offline provider stub Addresses three High-severity issues: - #26: web server now binds 127.0.0.1 by default (was 0.0.0.0); add optional X-API-Key auth on /api endpoints via MEDCHECK_API_KEY, and warn when binding to a non-loopback host without a key configured. - #28: validate the easyRadiology linkToERI download URL (HTTPS + host allowlist) and disable redirects to prevent SSRF to internal/metadata hosts. - #36: add LocalLLMProvider stub so the advertised "local" offline fallback degrades gracefully (check_available()=False, actionable NotImplementedError) instead of crashing with "No LLM provider available"; register it in the vision router and `medcheck models`. Updates README, .env.example, CHANGELOG; adds unit tests for all three (89 passing, coverage 66%). https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix(security): PHI consent gate, log/credential redaction, API input validation Addresses four Medium/High-severity privacy issues: - #27: gate external (cloud) LLM transmission behind explicit consent (--allow-cloud-llm flag, MEDCHECK_ALLOW_EXTERNAL_LLM env, interactive prompt); vision_analysis raises instead of silently sending PHI. - #29: stop logging the raw patient name to stdout — log a non-reversible hash of the patient ID instead. - #30: stop echoing portal access codes into ValueError messages. - #33: add a Pydantic AnalyzeRequest schema with validation for POST /api/analyze (auth was already added in #46). Docs: SECURITY.md "Handling of Patient Data" section, README + .env.example. Tests: +5 (consent gate, credential redaction, request validation); 93 passing. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix: address CodeRabbit review on #47 - ingest: stop logging study description (free-text DICOM field may contain PHI) - vision_analysis: honour explicit LLM provider preference (wires up --model) instead of hardcoding "claude"; default to on-device "local" without consent so offline execution stays reachable - docs: soften compliance/anonymity wording (drop "(HIPAA/GDPR)" assertion and "non-reversible" claim; describe pseudonymisation accurately) 94 tests passing. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix: keep 'local' as default vision provider regardless of consent Consent (--allow-cloud-llm / MEDCHECK_ALLOW_EXTERNAL_LLM) now only permits falling back to an external provider; it no longer flips the implicit default from on-device 'local' to 'claude'. Addresses CodeRabbit review on #47. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix: LLM resiliency, non-root Docker, vendored htmx, crypto-comment clarity Addresses four Medium-severity reliability/supply-chain issues: - #38: add configurable timeout (MEDCHECK_LLM_TIMEOUT) + retry with exponential backoff (MEDCHECK_LLM_RETRIES) to all three LLM providers via a shared call_with_retries helper; transient failures no longer crash the pipeline and surface as a clear LLMProviderError. - #39: run Docker containers as a non-root 'medcheck' user. - #37: vendor htmx locally (src/medcheck/web/static/htmx.min.js) with an SRI hash instead of loading from the unpkg CDN; air-gapped friendly. - #34: clarify the misleading bandit nosec on the Crypto import (pycryptodome, not pycrypto; B413 can't distinguish the shared namespace) and add an AES-CBC round-trip test that verifies the decryption path. 102 tests passing; coverage 67%. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix: address CI + CodeRabbit review on #48 - base: only retry transient errors (timeouts/connection/429/5xx), fail fast on permanent ones (auth/4xx); add is_transient_error() helper - claude/openai: pass max_retries=0 so call_with_retries is the sole retry controller (avoids SDK retry stacking) - remove unused `# noqa: BLE001` directive (RUF100 lint failure) - add fake-SDK provider tests (claude/openai/gemini happy path + missing key), lifting LLM provider patch coverage to ~100% (fixes codecov/patch) - web test now also asserts the htmx SRI integrity + crossorigin attributes 113 tests passing. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix: guard call_with_retries against attempts<=0 (avoid UnboundLocalError) Coerce the attempt count to >=1 so the loop always runs once and failures wrap as LLMProviderError instead of raising UnboundLocalError. Addresses CodeRabbit review on #48. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz --------- Co-authored-by: Claude <noreply@anthropic.com>
* feat: anatomy template loader, provider/model discovery CLI, doc fixes Addresses three previously unaddressed issues: - #43: build_prompt() now loads detailed anatomy templates from prompts/anatomy/*.txt (knee/shoulder/spine), with cached lookup and fallback to built-in hints. The shipped templates were dead code before. Adds an abdomen hint so all README-advertised regions are covered. - #44: add `medcheck providers` and `medcheck models` discovery commands. - #42: fix README "Report Bug" link (bug_report.md -> bug_report.yml). Updates CHANGELOG and README; adds unit tests for the new loader and CLI commands (72 passing). https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix: satisfy ruff format and harden anatomy template path against traversal - Add missing blank line so `ruff format --check` passes (lint CI was red). - Slug-validate the anatomy region before building a filesystem path in load_anatomy_instructions(), preventing path traversal (CodeRabbit review). - Add regression test for the traversal guard. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix(security): harden web server, validate easyRadiology downloads, add offline provider stub Addresses three High-severity issues: - #26: web server now binds 127.0.0.1 by default (was 0.0.0.0); add optional X-API-Key auth on /api endpoints via MEDCHECK_API_KEY, and warn when binding to a non-loopback host without a key configured. - #28: validate the easyRadiology linkToERI download URL (HTTPS + host allowlist) and disable redirects to prevent SSRF to internal/metadata hosts. - #36: add LocalLLMProvider stub so the advertised "local" offline fallback degrades gracefully (check_available()=False, actionable NotImplementedError) instead of crashing with "No LLM provider available"; register it in the vision router and `medcheck models`. Updates README, .env.example, CHANGELOG; adds unit tests for all three (89 passing, coverage 66%). https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix(security): PHI consent gate, log/credential redaction, API input validation Addresses four Medium/High-severity privacy issues: - #27: gate external (cloud) LLM transmission behind explicit consent (--allow-cloud-llm flag, MEDCHECK_ALLOW_EXTERNAL_LLM env, interactive prompt); vision_analysis raises instead of silently sending PHI. - #29: stop logging the raw patient name to stdout — log a non-reversible hash of the patient ID instead. - #30: stop echoing portal access codes into ValueError messages. - #33: add a Pydantic AnalyzeRequest schema with validation for POST /api/analyze (auth was already added in #46). Docs: SECURITY.md "Handling of Patient Data" section, README + .env.example. Tests: +5 (consent gate, credential redaction, request validation); 93 passing. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix: address CodeRabbit review on #47 - ingest: stop logging study description (free-text DICOM field may contain PHI) - vision_analysis: honour explicit LLM provider preference (wires up --model) instead of hardcoding "claude"; default to on-device "local" without consent so offline execution stays reachable - docs: soften compliance/anonymity wording (drop "(HIPAA/GDPR)" assertion and "non-reversible" claim; describe pseudonymisation accurately) 94 tests passing. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix: keep 'local' as default vision provider regardless of consent Consent (--allow-cloud-llm / MEDCHECK_ALLOW_EXTERNAL_LLM) now only permits falling back to an external provider; it no longer flips the implicit default from on-device 'local' to 'claude'. Addresses CodeRabbit review on #47. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix: LLM resiliency, non-root Docker, vendored htmx, crypto-comment clarity Addresses four Medium-severity reliability/supply-chain issues: - #38: add configurable timeout (MEDCHECK_LLM_TIMEOUT) + retry with exponential backoff (MEDCHECK_LLM_RETRIES) to all three LLM providers via a shared call_with_retries helper; transient failures no longer crash the pipeline and surface as a clear LLMProviderError. - #39: run Docker containers as a non-root 'medcheck' user. - #37: vendor htmx locally (src/medcheck/web/static/htmx.min.js) with an SRI hash instead of loading from the unpkg CDN; air-gapped friendly. - #34: clarify the misleading bandit nosec on the Crypto import (pycryptodome, not pycrypto; B413 can't distinguish the shared namespace) and add an AES-CBC round-trip test that verifies the decryption path. 102 tests passing; coverage 67%. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix: address CI + CodeRabbit review on #48 - base: only retry transient errors (timeouts/connection/429/5xx), fail fast on permanent ones (auth/4xx); add is_transient_error() helper - claude/openai: pass max_retries=0 so call_with_retries is the sole retry controller (avoids SDK retry stacking) - remove unused `# noqa: BLE001` directive (RUF100 lint failure) - add fake-SDK provider tests (claude/openai/gemini happy path + missing key), lifting LLM provider patch coverage to ~100% (fixes codecov/patch) - web test now also asserts the htmx SRI integrity + crossorigin attributes 113 tests passing. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix: guard call_with_retries against attempts<=0 (avoid UnboundLocalError) Coerce the attempt count to >=1 so the loop always runs once and failures wrap as LLMProviderError instead of raising UnboundLocalError. Addresses CodeRabbit review on #48. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix(#31): do not gate easyRadiology auth on unused date of birth authenticate() required a non-empty dob, implying DOB verification — but dob is never sent to the portal or checked by this client. Gating on it created a false security boundary. Authentication now relies on the access code only; dob remains accepted but is documented as not verified. Updates README + .env.example to stop overstating DOB verification. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * docs(#31): align .env.example DOB wording with code (not verified) https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz --------- Co-authored-by: Claude <noreply@anthropic.com>
…, #12) (#50) * feat: anatomy template loader, provider/model discovery CLI, doc fixes Addresses three previously unaddressed issues: - #43: build_prompt() now loads detailed anatomy templates from prompts/anatomy/*.txt (knee/shoulder/spine), with cached lookup and fallback to built-in hints. The shipped templates were dead code before. Adds an abdomen hint so all README-advertised regions are covered. - #44: add `medcheck providers` and `medcheck models` discovery commands. - #42: fix README "Report Bug" link (bug_report.md -> bug_report.yml). Updates CHANGELOG and README; adds unit tests for the new loader and CLI commands (72 passing). https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix: satisfy ruff format and harden anatomy template path against traversal - Add missing blank line so `ruff format --check` passes (lint CI was red). - Slug-validate the anatomy region before building a filesystem path in load_anatomy_instructions(), preventing path traversal (CodeRabbit review). - Add regression test for the traversal guard. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix(security): harden web server, validate easyRadiology downloads, add offline provider stub Addresses three High-severity issues: - #26: web server now binds 127.0.0.1 by default (was 0.0.0.0); add optional X-API-Key auth on /api endpoints via MEDCHECK_API_KEY, and warn when binding to a non-loopback host without a key configured. - #28: validate the easyRadiology linkToERI download URL (HTTPS + host allowlist) and disable redirects to prevent SSRF to internal/metadata hosts. - #36: add LocalLLMProvider stub so the advertised "local" offline fallback degrades gracefully (check_available()=False, actionable NotImplementedError) instead of crashing with "No LLM provider available"; register it in the vision router and `medcheck models`. Updates README, .env.example, CHANGELOG; adds unit tests for all three (89 passing, coverage 66%). https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix(security): PHI consent gate, log/credential redaction, API input validation Addresses four Medium/High-severity privacy issues: - #27: gate external (cloud) LLM transmission behind explicit consent (--allow-cloud-llm flag, MEDCHECK_ALLOW_EXTERNAL_LLM env, interactive prompt); vision_analysis raises instead of silently sending PHI. - #29: stop logging the raw patient name to stdout — log a non-reversible hash of the patient ID instead. - #30: stop echoing portal access codes into ValueError messages. - #33: add a Pydantic AnalyzeRequest schema with validation for POST /api/analyze (auth was already added in #46). Docs: SECURITY.md "Handling of Patient Data" section, README + .env.example. Tests: +5 (consent gate, credential redaction, request validation); 93 passing. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix: address CodeRabbit review on #47 - ingest: stop logging study description (free-text DICOM field may contain PHI) - vision_analysis: honour explicit LLM provider preference (wires up --model) instead of hardcoding "claude"; default to on-device "local" without consent so offline execution stays reachable - docs: soften compliance/anonymity wording (drop "(HIPAA/GDPR)" assertion and "non-reversible" claim; describe pseudonymisation accurately) 94 tests passing. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix: keep 'local' as default vision provider regardless of consent Consent (--allow-cloud-llm / MEDCHECK_ALLOW_EXTERNAL_LLM) now only permits falling back to an external provider; it no longer flips the implicit default from on-device 'local' to 'claude'. Addresses CodeRabbit review on #47. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix: LLM resiliency, non-root Docker, vendored htmx, crypto-comment clarity Addresses four Medium-severity reliability/supply-chain issues: - #38: add configurable timeout (MEDCHECK_LLM_TIMEOUT) + retry with exponential backoff (MEDCHECK_LLM_RETRIES) to all three LLM providers via a shared call_with_retries helper; transient failures no longer crash the pipeline and surface as a clear LLMProviderError. - #39: run Docker containers as a non-root 'medcheck' user. - #37: vendor htmx locally (src/medcheck/web/static/htmx.min.js) with an SRI hash instead of loading from the unpkg CDN; air-gapped friendly. - #34: clarify the misleading bandit nosec on the Crypto import (pycryptodome, not pycrypto; B413 can't distinguish the shared namespace) and add an AES-CBC round-trip test that verifies the decryption path. 102 tests passing; coverage 67%. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix: address CI + CodeRabbit review on #48 - base: only retry transient errors (timeouts/connection/429/5xx), fail fast on permanent ones (auth/4xx); add is_transient_error() helper - claude/openai: pass max_retries=0 so call_with_retries is the sole retry controller (avoids SDK retry stacking) - remove unused `# noqa: BLE001` directive (RUF100 lint failure) - add fake-SDK provider tests (claude/openai/gemini happy path + missing key), lifting LLM provider patch coverage to ~100% (fixes codecov/patch) - web test now also asserts the htmx SRI integrity + crossorigin attributes 113 tests passing. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix: guard call_with_retries against attempts<=0 (avoid UnboundLocalError) Coerce the attempt count to >=1 so the loop always runs once and failures wrap as LLMProviderError instead of raising UnboundLocalError. Addresses CodeRabbit review on #48. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix(#31): do not gate easyRadiology auth on unused date of birth authenticate() required a non-empty dob, implying DOB verification — but dob is never sent to the portal or checked by this client. Gating on it created a false security boundary. Authentication now relies on the access code only; dob remains accepted but is documented as not verified. Updates README + .env.example to stop overstating DOB verification. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * docs(#31): align .env.example DOB wording with code (not verified) https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * feat: add hip, ankle/foot, and wrist anatomy prompt templates Closes #10, #11, #12. Adds detailed MRI analysis templates (modelled on knee.txt) that the prompt loader picks up automatically, and extends detect_anatomy() with hip/ankle/wrist keyword patterns (incl. German terms). Removes the over-broad "gelenk" keyword from the knee pattern so it no longer mis-matches Handgelenk (wrist) / Sprunggelenk (ankle); "Knie" still matches knee. 115 tests passing. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz --------- Co-authored-by: Claude <noreply@anthropic.com>
…el IDs (#75) * feat: anatomy template loader, provider/model discovery CLI, doc fixes Addresses three previously unaddressed issues: - #43: build_prompt() now loads detailed anatomy templates from prompts/anatomy/*.txt (knee/shoulder/spine), with cached lookup and fallback to built-in hints. The shipped templates were dead code before. Adds an abdomen hint so all README-advertised regions are covered. - #44: add `medcheck providers` and `medcheck models` discovery commands. - #42: fix README "Report Bug" link (bug_report.md -> bug_report.yml). Updates CHANGELOG and README; adds unit tests for the new loader and CLI commands (72 passing). https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix: satisfy ruff format and harden anatomy template path against traversal - Add missing blank line so `ruff format --check` passes (lint CI was red). - Slug-validate the anatomy region before building a filesystem path in load_anatomy_instructions(), preventing path traversal (CodeRabbit review). - Add regression test for the traversal guard. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix(security): harden web server, validate easyRadiology downloads, add offline provider stub Addresses three High-severity issues: - #26: web server now binds 127.0.0.1 by default (was 0.0.0.0); add optional X-API-Key auth on /api endpoints via MEDCHECK_API_KEY, and warn when binding to a non-loopback host without a key configured. - #28: validate the easyRadiology linkToERI download URL (HTTPS + host allowlist) and disable redirects to prevent SSRF to internal/metadata hosts. - #36: add LocalLLMProvider stub so the advertised "local" offline fallback degrades gracefully (check_available()=False, actionable NotImplementedError) instead of crashing with "No LLM provider available"; register it in the vision router and `medcheck models`. Updates README, .env.example, CHANGELOG; adds unit tests for all three (89 passing, coverage 66%). https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix(security): PHI consent gate, log/credential redaction, API input validation Addresses four Medium/High-severity privacy issues: - #27: gate external (cloud) LLM transmission behind explicit consent (--allow-cloud-llm flag, MEDCHECK_ALLOW_EXTERNAL_LLM env, interactive prompt); vision_analysis raises instead of silently sending PHI. - #29: stop logging the raw patient name to stdout — log a non-reversible hash of the patient ID instead. - #30: stop echoing portal access codes into ValueError messages. - #33: add a Pydantic AnalyzeRequest schema with validation for POST /api/analyze (auth was already added in #46). Docs: SECURITY.md "Handling of Patient Data" section, README + .env.example. Tests: +5 (consent gate, credential redaction, request validation); 93 passing. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix: address CodeRabbit review on #47 - ingest: stop logging study description (free-text DICOM field may contain PHI) - vision_analysis: honour explicit LLM provider preference (wires up --model) instead of hardcoding "claude"; default to on-device "local" without consent so offline execution stays reachable - docs: soften compliance/anonymity wording (drop "(HIPAA/GDPR)" assertion and "non-reversible" claim; describe pseudonymisation accurately) 94 tests passing. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix: keep 'local' as default vision provider regardless of consent Consent (--allow-cloud-llm / MEDCHECK_ALLOW_EXTERNAL_LLM) now only permits falling back to an external provider; it no longer flips the implicit default from on-device 'local' to 'claude'. Addresses CodeRabbit review on #47. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix: LLM resiliency, non-root Docker, vendored htmx, crypto-comment clarity Addresses four Medium-severity reliability/supply-chain issues: - #38: add configurable timeout (MEDCHECK_LLM_TIMEOUT) + retry with exponential backoff (MEDCHECK_LLM_RETRIES) to all three LLM providers via a shared call_with_retries helper; transient failures no longer crash the pipeline and surface as a clear LLMProviderError. - #39: run Docker containers as a non-root 'medcheck' user. - #37: vendor htmx locally (src/medcheck/web/static/htmx.min.js) with an SRI hash instead of loading from the unpkg CDN; air-gapped friendly. - #34: clarify the misleading bandit nosec on the Crypto import (pycryptodome, not pycrypto; B413 can't distinguish the shared namespace) and add an AES-CBC round-trip test that verifies the decryption path. 102 tests passing; coverage 67%. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix: address CI + CodeRabbit review on #48 - base: only retry transient errors (timeouts/connection/429/5xx), fail fast on permanent ones (auth/4xx); add is_transient_error() helper - claude/openai: pass max_retries=0 so call_with_retries is the sole retry controller (avoids SDK retry stacking) - remove unused `# noqa: BLE001` directive (RUF100 lint failure) - add fake-SDK provider tests (claude/openai/gemini happy path + missing key), lifting LLM provider patch coverage to ~100% (fixes codecov/patch) - web test now also asserts the htmx SRI integrity + crossorigin attributes 113 tests passing. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix: guard call_with_retries against attempts<=0 (avoid UnboundLocalError) Coerce the attempt count to >=1 so the loop always runs once and failures wrap as LLMProviderError instead of raising UnboundLocalError. Addresses CodeRabbit review on #48. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix(#31): do not gate easyRadiology auth on unused date of birth authenticate() required a non-empty dob, implying DOB verification — but dob is never sent to the portal or checked by this client. Gating on it created a false security boundary. Authentication now relies on the access code only; dob remains accepted but is documented as not verified. Updates README + .env.example to stop overstating DOB verification. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * docs(#31): align .env.example DOB wording with code (not verified) https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * feat: add hip, ankle/foot, and wrist anatomy prompt templates Closes #10, #11, #12. Adds detailed MRI analysis templates (modelled on knee.txt) that the prompt loader picks up automatically, and extends detect_anatomy() with hip/ankle/wrist keyword patterns (incl. German terms). Removes the over-broad "gelenk" keyword from the knee pattern so it no longer mis-matches Handgelenk (wrist) / Sprunggelenk (ankle); "Knie" still matches knee. 115 tests passing. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * docs: add open-source options research report Verified (against primary sources) survey of open-source resources MedCheck can adopt, mapped to the open feature issues: - local medical VLMs (#18): recommend Lingshu-7B (MIT, MRI-capable) over the research-only LLaVA-Med - DICOMDIR/CD handling (#25): pydicom FileSet + pylibjpeg/gdcm - DICOMweb providers (#13 Orthanc, #14 Google Cloud Healthcare) - #15 re-scope: OHIF is a viewer, not a data provider - ML frameworks (MONAI/TorchIO), reporting standards (FHIR/DICOM SR), datasets Includes license compatibility analysis for Apache-2.0 and flags research-only / non-commercial weights. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * chore: remove session-only research notes from repo The open-source options survey was working material for planning; it does not belong in the public repository. Findings are being applied directly as code. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz * fix: honour MEDCHECK_LLM_PROVIDER, validate LLM findings, correct model IDs Three correctness bugs from the repo-health sweep: - #73: the `analyze` command now falls back to Settings.default_llm_provider (MEDCHECK_LLM_PROVIDER) when --model is omitted, instead of silently using the offline `local` provider (which raises NotImplementedError). - #71: parse_llm_response now defensively coerces each LLM-returned structure (known fields only, type coercion, confidence clamped to [0,1], empty entries dropped) instead of StructureFinding(**s), which crashed on unexpected keys and rendered fabricated high-confidence findings verbatim. - #68/#69: default Claude model ID corrected claude-opus-4-7 -> claude-opus-4-8 and made overridable via MEDCHECK_{CLAUDE,OPENAI,GEMINI}_MODEL; updated all README/docs/web-UI references. 130 tests passing. https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz --------- Co-authored-by: Claude <noreply@anthropic.com>
Prioritised batch addressing the three open High-severity issues found by the repo-monitor/repo-health sweeps.
Closes
0.0.0.0with no authenticationlinkToERIdownload URL"local"LLM fallback is unimplemented and crashes offline runsChanges
#26 — Web server hardening
0.0.0.0→127.0.0.1(CLIserve+Settings). Network exposure is now an explicit opt-in.MEDCHECK_API_KEYis set,/apiendpoints require a matchingX-API-Keyheader (constant-time compare)./healthand/stay public. With no key configured, behaviour is unchanged (localhost dev).serveprints a warning when binding to a non-loopback host without a key.#28 — easyRadiology SSRF guard
_validate_download_url(): requireshttpsand a host on theeasyradiology.net/.deallowlist before fetching the exam ZIP.follow_redirectson the download client so a 30x bounce can't escape the allowlist (e.g. to169.254.169.254or RFC 1918).#36 — Offline provider stub
LocalLLMProvider(llm/local.py):check_available()returnsFalseso the router skips it cleanly;analyze_images()raises an actionableNotImplementedErrorpointing to Add local LLaVA-Med vision model provider #18.medcheck models. No moreRuntimeError: No LLM provider availablewhen running with zero API keys..env.examplemarklocal/LLaVA-Med as coming soon (Add local LLaVA-Med vision model provider #18).Validation
ruff check✅ ·ruff format --check✅ ·mypy✅ ·bandit -ll✅https://claude.ai/code/session_01KYcUxCGAHaTPrRHTzJCWQz
Generated by Claude Code