Repository navigation
Security fixes: medical OCR separator, admin auth, deployment topology #2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Apply security fixes v3 (temporary) | |
| on: | |
| pull_request: | |
| branches: [main] | |
| permissions: | |
| contents: write | |
| jobs: | |
| apply: | |
| if: github.event.pull_request.head.ref == 'security-fixes-3451e6e' && github.event.pull_request.head.repo.full_name == github.repository | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout PR branch | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.head_ref }} | |
| fetch-depth: 1 | |
| - name: Apply and verify real fixes | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| python - <<'PY' | |
| from pathlib import Path | |
| # Medical OCR: remove identity mappings with trailing separators. | |
| p = Path('hf-space/app_core.py') | |
| s = p.read_text(encoding='utf-8') | |
| replacements = { | |
| ' "ديكلوفيناك ": "ديكلوفيناك", "اوجمينتين": "اوجمنتين",': ' "اوجمينتين": "اوجمنتين",', | |
| ' "اوميبرازول ": "اوميبرازول", "سيليبريكس ": "سيليبريكس",': ' "اوميبرازول": "اوميبرازول", "سيليبريكس": "سيليبريكس",', | |
| ' "ترامادول ": "ترامادول", "كاتافلام ": "كاتافلام",': ' "كاتافلام": "كاتافلام",', | |
| ' "نوفافين ": "نوفافين", "فلاميكس ": "فلاميكس",': ' "فلاميكس": "فلاميكس",', | |
| ' "بنادول ": "بنادول", "ادفيل ": "ادفيل",': ' "بنادول": "بنادول", "ادفيل": "ادفيل",', | |
| } | |
| for old, new in replacements.items(): | |
| if old not in s: | |
| raise SystemExit(f'Expected app_core pattern not found: {old!r}') | |
| s = s.replace(old, new, 1) | |
| p.write_text(s, encoding='utf-8') | |
| # Admin system-status authentication. | |
| p = Path('app/routers/admin.py') | |
| s = p.read_text(encoding='utf-8') | |
| if 'from app.core.security import get_current_user' not in s: | |
| s = s.replace('from fastapi import APIRouter\n', 'from fastapi import APIRouter, Depends\nfrom app.core.security import get_current_user\nfrom app.db.models.auth import User\n', 1) | |
| if 'current_user: User = Depends(get_current_user)' not in s: | |
| s = s.replace('async def system_status():', 'async def system_status(current_user: User = Depends(get_current_user)):', 1) | |
| p.write_text(s, encoding='utf-8') | |
| # Separate alternate full-stack compose; do not overwrite canonical root compose. | |
| old = Path('deploy/docker-compose.prod.yml') | |
| new = Path('deploy/docker-compose.full.yml') | |
| if old.exists() and not new.exists(): | |
| old.rename(new) | |
| p = Path('deploy/deploy.sh') | |
| s = p.read_text(encoding='utf-8') | |
| marker = 'DEPLOY_DIR="$(dirname "$0")"\n' | |
| if 'COMPOSE_FILE="${DEPLOY_DIR}/docker-compose.full.yml"' not in s: | |
| s = s.replace(marker, marker + 'COMPOSE_FILE="${DEPLOY_DIR}/docker-compose.full.yml"\n', 1) | |
| old_block = '''if [[ -f "${DEPLOY_DIR}/docker-compose.prod.yml" ]]; then\n cp "${DEPLOY_DIR}/docker-compose.prod.yml" "${PROJECT_DIR}/docker-compose.prod.yml"\nfi\n''' | |
| s = s.replace(old_block, '') | |
| if 'if [[ ! -f "$COMPOSE_FILE" ]]; then' not in s: | |
| needle = 'if [[ -f "${DEPLOY_DIR}/Dockerfile" ]]; then\n cp "${DEPLOY_DIR}/Dockerfile" "${PROJECT_DIR}/Dockerfile"\nfi\n' | |
| s = s.replace(needle, needle + 'if [[ ! -f "$COMPOSE_FILE" ]]; then\n error "Full-stack compose file not found: $COMPOSE_FILE"\nfi\n', 1) | |
| s = s.replace('docker compose -f docker-compose.prod.yml', 'docker compose --project-directory "$PROJECT_DIR" -f "$COMPOSE_FILE"') | |
| p.write_text(s, encoding='utf-8') | |
| # Real behavioral tests importing the production module. | |
| t = Path('tests/security/test_medical_behavior.py') | |
| t.parent.mkdir(parents=True, exist_ok=True) | |
| t.write_text('''import sys\nfrom pathlib import Path\n\nROOT = Path(__file__).resolve().parents[2]\nsys.path.insert(0, str(ROOT / "hf-space"))\nimport app_core\n\ndef test_tramadol_dose_separator_preserved():\n out, _ = app_core._auto_correct_ocr("ترامادول 0.5 mg")\n assert out == "ترامادول 0.5 mg"\n\ndef test_medical_negation_and_dose_preserved():\n text = "لا يعطى ترامادول 0.5 mg"\n out, _ = app_core._auto_correct_ocr(text)\n assert out == text\n\ndef test_decimals_preserved():\n for text in ["0.5", "1.25", "0.75", "٠٫٥", "١٫٢٥"]:\n out, _ = app_core._auto_correct_ocr(text)\n assert out == text\n\ndef test_negations_preserved():\n for text in ["لا", "ليس", "لم", "لن", "غير", "بدون", "لا يوجد سكري"]:\n out, _ = app_core._auto_correct_ocr(text)\n assert out == text\n\ndef test_intended_ocr_correction_remains():\n out, _ = app_core._auto_correct_ocr("باراسيتبمول 500 mg")\n assert out == "باراسيتامول 500 mg"\n''', encoding='utf-8') | |
| PY | |
| python -m pip install --disable-pip-version-check pytest numpy opencv-python-headless 'gradio<6' >/dev/null | |
| pytest -q tests/security/test_medical_behavior.py | |
| # Verify intended modifications and absence of the dangerous patterns. | |
| test -f deploy/docker-compose.full.yml | |
| test -f docker-compose.prod.yml | |
| ! grep -Fq 'cp "${DEPLOY_DIR}/docker-compose.prod.yml" "${PROJECT_DIR}/docker-compose.prod.yml"' deploy/deploy.sh | |
| grep -Fq 'docker-compose.full.yml' deploy/deploy.sh | |
| grep -Fq 'Depends(get_current_user)' app/routers/admin.py | |
| ! grep -Fq '"ترامادول ": "ترامادول"' hf-space/app_core.py | |
| ! grep -Fq '"ديكلوفيناك ": "ديكلوفيناك"' hf-space/app_core.py | |
| ! grep -Fq '"اوميبرازول ": "اوميبرازول"' hf-space/app_core.py | |
| git diff --check | |
| git diff --stat | |
| - name: Remove temporary runner and commit only intended files | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| rm -f .github/workflows/_apply_security_fixes_v3.yml | |
| git config user.name 'github-actions[bot]' | |
| git config user.email '41898282+github-actions[bot]@users.noreply.github.com' | |
| # Explicitly stage ONLY the intended changes. Do not use git add -A. | |
| git add hf-space/app_core.py app/routers/admin.py deploy/deploy.sh deploy/docker-compose.full.yml tests/security/test_medical_behavior.py | |
| git add -u -- deploy/docker-compose.prod.yml .github/workflows/_apply_security_fixes_v3.yml | |
| git status --short | |
| git diff --cached --check | |
| git diff --cached --stat | |
| git diff --cached --name-only | |
| # Hard guard: only the five intended production/test paths may remain staged. | |
| test "$(git diff --cached --name-only | wc -l)" -eq 5 | |
| git diff --cached --name-only | grep -Fx 'hf-space/app_core.py' | |
| git diff --cached --name-only | grep -Fx 'app/routers/admin.py' | |
| git diff --cached --name-only | grep -Fx 'deploy/deploy.sh' | |
| git diff --cached --name-only | grep -Fx 'deploy/docker-compose.full.yml' | |
| git diff --cached --name-only | grep -Fx 'tests/security/test_medical_behavior.py' | |
| git commit -m 'fix(security): harden medical OCR, admin auth, and deployment topology' | |
| git push origin HEAD:${{ github.head_ref }} |