Files
clinicpro/config/packages/html_sanitizer.yaml
T
hamed 6876135a53 feat: add BlogBodySanitizer for HTML sanitization on article save
- Implemented BlogBodySanitizer to clean HTML content before saving articles, ensuring security against XSS attacks.
- Added tests for BlogBodySanitizer to verify that unsafe tags and attributes are stripped from the content.
- Introduced ApiLeastPrivilegeTest to ensure that unauthorized users cannot access sensitive API routes, maintaining strict access control.
2026-08-07 21:13:38 +03:30

68 lines
3.3 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']
table: []
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