Files
clinicpro/config/packages/html_sanitizer.yaml
T
hamed 47323daa27 feat: add RichTextEditor component for rich text editing in articles
feat: create SanitizeBlogBodiesCommand to clean existing blog bodies according to current HTML sanitization policies

test: add AppointmentTreatmentSessionLinkTest to ensure appointment booking functionality works correctly with treatment session links
2026-08-08 11:40:17 +03:30

73 lines
3.9 KiB
YAML

# Sanitizer for rich text that a human authored in the admin panel and that the
# panel later renders with `dangerouslySetInnerHTML` (BlogReviewPage).
#
# آدیت ۲۰۲۶-۰۸-۰۷: بدنهٔ مقاله همان‌طور که می‌آمد ذخیره و رندر می‌شد. CSP فعلی
# (`script-src 'self'`) اسکریپت تزریقی را اجرا نمی‌کند، ولی هندلرهای inline و
# `javascript:` را کامل نمی‌بندد — و CSP لایهٔ دوم است، نه اولین دفاع. پاک‌سازی
# هنگام **ذخیره** انجام می‌شود نه هنگام نمایش، چون یک نقطهٔ ورود دارد و هر
# مصرف‌کنندهٔ بعدی (پنل، سایت عمومی، فید) خودبه‌خود امن می‌شود.
#
# فهرست عناصر = چیزی که CKEditor 5 واقعاً تولید می‌کند. عنصری که اینجا نیست حذف
# می‌شود ولی متنش می‌ماند، پس محتوای قدیمی نامفهوم نمی‌شود.
framework:
html_sanitizer:
sanitizers:
blog.body:
allow_safe_elements: true
allow_elements:
p: []
br: []
hr: []
strong: []
b: []
em: []
i: []
u: []
s: []
sub: []
sup: []
mark: []
h1: []
h2: []
h3: []
h4: []
h5: []
h6: []
ul: []
ol: ['start', 'reversed']
li: []
blockquote: []
pre: []
code: []
figure: ['class']
figcaption: []
span: []
div: []
a: ['href', 'title', 'target', 'rel']
img: ['src', 'alt', 'title', 'width', 'height']
# سه attributeِ ظاهریِ قدیمیِ جدول عمداً مجازند. هیچ‌کدام
# نمی‌توانند حاملِ اسکریپت یا URL باشند — مقدارشان عدد است — و
# بدونشان جدولِ صدها مقالهٔ موجود حاشیه و فاصله‌اش را از دست
# می‌داد. `style` همچنان ممنوع است: تنها attributeِ ظاهریِ این
# جمع که می‌تواند بارِ اجرایی حمل کند.
table: ['border', 'cellpadding', 'cellspacing']
thead: []
tbody: []
tfoot: []
tr: []
td: ['colspan', 'rowspan']
th: ['colspan', 'rowspan', 'scope']
# فقط طرح‌های امن. `javascript:` عمداً نیست.
allowed_link_schemes: ['http', 'https', 'mailto']
allowed_media_schemes: ['http', 'https', 'data']
allow_relative_links: true
# لینک بیرونی در تب جدید باز می‌شود؛ بدون این‌ها tabnabbing باز است.
force_attributes:
a:
rel: 'noopener noreferrer'
# `drop` نه `block`: block فقط تگ را برمی‌دارد و متنِ داخلش را نگه
# می‌دارد، یعنی `<script>alert(1)</script>` به متنِ `alert(1)` تبدیل
# می‌شد. برای این عناصر، خودِ محتوا هم باید برود.
drop_elements: ['script', 'style', 'iframe', 'object', 'embed', 'form', 'input', 'button', 'noscript']
max_input_length: 500000