Repository navigation
165 lines (149 loc) · 8.14 KB
/
Copy pathapply-security-fixes.yml
File metadata and controls
165 lines (149 loc) · 8.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
name: Apply Security Fixes
on:
push:
branches: [security-fixes-clean]
paths:
- '.github/workflows/apply-security-fixes.yml'
permissions:
contents: write
jobs:
apply:
if: ${{ !contains(github.event.head_commit.message, 'chore: apply verified security fixes') }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Apply exact source fixes
shell: bash
run: |
set -euo pipefail
python - <<'PY'
from pathlib import Path
import re
p = Path('hf-space/app_core.py')
s = p.read_text(encoding='utf-8')
old_dict = '''OCR_CORRECTIONS = {
"باراسيتبمول": "باراسيتامول", "ايبوروفين": "ايبوبروفين",
"اموكسيستلين": "اموكسيسيلين", "اموكسيسلين": "اموكسيسيلين",
"ازيثروميسين": "ازيثرومايسين", "ميتروندازول": "ميترونيدازول",
"ديكلوفيناك ": "ديكلوفيناك", "اوجمينتين": "اوجمنتين",
"اوميبرازول ": "اوميبرازول", "سيليبريكس ": "سيليبريكس",
"ترامادول ": "ترامادول", "كاتافلام ": "كاتافلام",
"نوفافين ": "نوفافين", "فلاميكس ": "فلاميكس",
"بنادول ": "بنادول", "ادفيل ": "ادفيل",
}'''
new_dict = '''OCR_CORRECTIONS = {
"باراسيتبمول": "باراسيتامول", "ايبوروفين": "ايبوبروفين",
"اموكسيستلين": "اموكسيسيلين", "اموكسيسلين": "اموكسيسيلين",
"ازيثروميسين": "ازيثرومايسين", "ميتروندازول": "ميترونيدازول",
"ديكلوفيناك": "ديكلوفيناك", "اوجمينتين": "اوجمنتين",
"اوميبرازول": "اوميبرازول", "سيليبريكس": "سيليبريكس",
"ترامادول": "ترامادول", "كاتافلام": "كاتافلام",
"نوفافين": "نوفافين", "فلاميكس": "فلاميكس",
"بنادول": "بنادول", "ادفيل": "ادفيل",
}'''
if old_dict not in s:
raise SystemExit('Expected original OCR_CORRECTIONS block not found')
s = s.replace(old_dict, new_dict, 1)
old_func = '''def _auto_correct_ocr(text: str) -> Tuple[str, List[Dict]]:
"""Apply OCR corrections + spell checker. Returns (corrected, changes)."""
changes = []
corrected = text
for wrong, right in OCR_CORRECTIONS.items():
if wrong in corrected:
count = corrected.count(wrong)
corrected = corrected.replace(wrong, right)
changes.append({"type": "ocr_fix", "from": wrong, "to": right, "count": count})
corrected = re.sub(r'[ \\t]+', ' ', corrected)
corrected = re.sub(r'\\n{3,}', '\\n\\n', corrected).strip()
return corrected, changes'''
new_func = '''def _auto_correct_ocr(text: str) -> Tuple[str, List[Dict]]:
"""Apply OCR corrections while preserving token boundaries and whitespace."""
changes = []
corrected = text
for wrong, right in OCR_CORRECTIONS.items():
if wrong == right:
continue
pattern = r'(?<!\\w)' + re.escape(wrong) + r'(?!\\w)'
count = len(re.findall(pattern, corrected))
if count:
corrected = re.sub(pattern, right, corrected)
changes.append({"type": "ocr_fix", "from": wrong, "to": right, "count": count})
corrected = re.sub(r'[ \\t]+', ' ', corrected)
corrected = re.sub(r'\\n{3,}', '\\n\\n', corrected).strip()
return corrected, changes'''
if old_func not in s:
raise SystemExit('Expected original _auto_correct_ocr function not found')
s = s.replace(old_func, new_func, 1)
p.write_text(s, encoding='utf-8')
p = Path('app/routers/admin.py')
s = p.read_text(encoding='utf-8')
if 'Depends(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)
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')
p = Path('deploy/deploy.sh')
s = p.read_text(encoding='utf-8')
s = s.replace('deploy/docker-compose.prod.yml', 'deploy/docker-compose.full.yml')
s = re.sub(r'^\s*cp\s+[^\n]*docker-compose\.full\.yml[^\n]*\n', '', s, flags=re.M)
p.write_text(s, encoding='utf-8')
old = Path('deploy/docker-compose.prod.yml')
full = Path('deploy/docker-compose.full.yml')
if old.exists() and not full.exists():
old.rename(full)
elif old.exists() and full.exists():
raise SystemExit('Both compose files exist; refusing to choose silently')
elif not full.exists():
raise SystemExit('Full compose file missing')
PY
- name: Add behavioral medical regression tests
shell: bash
run: |
set -euo pipefail
mkdir -p tests/security
cat > tests/security/test_medical_behavior.py <<'PY'
import re
from pathlib import Path
def _load_ocr_function():
source = Path('hf-space/app_core.py').read_text(encoding='utf-8')
block = source[source.index('OCR_CORRECTIONS = {'):source.index('# ── Lazy-loaded modules')]
ns = {'re': re}
exec(block + '\n' + source[source.index('def _auto_correct_ocr'):source.index('\ndef _extract_ner')], ns)
return ns['OCR_CORRECTIONS'], ns['_auto_correct_ocr']
def test_separator_preserved():
_, fn = _load_ocr_function()
assert fn('ترامادول 0.5 mg')[0] == 'ترامادول 0.5 mg'
def test_decimal_tokens_unchanged():
_, fn = _load_ocr_function()
for text in ('0.5', '1.25', '0.75', '٠٫٥', '١٫٢٥'):
assert fn(text)[0] == text
def test_negations_unchanged():
_, fn = _load_ocr_function()
for text in ('لا', 'ليس', 'لم', 'لن', 'غير', 'بدون', 'لا يوجد سكري'):
assert fn(text)[0] == text
def test_intended_ocr_correction():
_, fn = _load_ocr_function()
assert fn('باراسيتبمول 500 mg')[0] == 'باراسيتامول 500 mg'
PY
- name: Verify exact changes
shell: bash
run: |
set -euo pipefail
grep -q '"ترامادول": "ترامادول"' hf-space/app_core.py
! grep -q '"ترامادول ": "ترامادول"' hf-space/app_core.py
grep -q 'Depends(get_current_user)' app/routers/admin.py
test -f deploy/docker-compose.full.yml
! test -f deploy/docker-compose.prod.yml
! grep -q 'cp .*docker-compose.*yml.*docker-compose.prod.yml' deploy/deploy.sh
python -m pytest -q tests/security/test_medical_behavior.py
- name: Commit fixes
shell: bash
run: |
set -euo pipefail
git config user.name 'github-actions[bot]'
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
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
git diff --cached --quiet && exit 0
git commit -m 'chore: apply verified security fixes'
git push