تخطَّ إلى المحتوى
kazma.
البحث في كاظمه والتوثيق
EN

أمن المستندات

صُمّمت منصة ذكاء المستندات على افتراض أن كل مستند عدائي حتى يثبت العكس. توثّق هذه الصفحة بنية الأمان ونموذج التهديد وإجراءات الدفاع في العمق، وتذكر أين يتوقف كلٌّ منها.

ذو صلة: ذكاء المستندات · تشغيل معالجة المستندات · خريطة الأطوار


قدرات المهاجم (مُفترضة)

Section titled “قدرات المهاجم (مُفترضة)”
  • يستطيع رفع مستندات مصنوعة بعناية (PDF، DOCX، XLSX، PPTX، صور، نص عادي، CSV، HTML، RTF).
  • يستطيع تضمين محتوى خبيث: حقن التعليمات، وXXE، والماكرو، والمحتوى النشط، والملفات متعددة الصيغ (polyglot)، وقنابل الضغط، وقنابل الأرشفة، والبنى الفاسدة/المشوهة.
  • يستطيع محاولة اجتياز المسارات عبر اسم الملف أو المراجع المضمّنة.
  • يستطيع محاولة SSRF عبر علاقات OOXML الخارجية، أو روابط PDF وأفعاله، أو موارد يسمّيها مستند يُصيَّر.
  • يستطيع محاولة تجاوز التنقيح.
  • يستطيع محاولة قراءة مستندات فاعل آخر ومهامه أو التحكم فيها.

قدرات المهاجم (خارج النطاق / مُخفَّفة خارجيًا)

Section titled “قدرات المهاجم (خارج النطاق / مُخفَّفة خارجيًا)”
  • الوصول إلى نظام الملفات (عزل جذر مساحة العمل لمسارات الاستيراد).
  • عزل نظام الملفات والشبكة على مستوى نظام التشغيل لعمليات المُحلِّلات في ملف العزل الافتراضي process. ويوفّر ملف العزل container كليهما، مع إثبات (attestation) لكل نشر؛ راجع الطبقة 3 لما هو مؤهَّل وما ليس مؤهَّلًا.
  • الإفلات من النواة انطلاقًا من حاوية مهمة (الحاويات تتشارك نواة المضيف أو الآلة الافتراضية؛ ولا بيئة تشغيل gVisor/Kata مؤهَّلة).
  • القنوات الجانبية للـ GPU/الذاكرة بما يتجاوز حدود الموارد التي يفرضها نظام التشغيل.
  • تعامل ClamAV نفسه مع ملف عدائي: يعمل الماسح على مضيف كاظمه، خارج ملفّي عزل العمال كليهما.
  • ما يفعله مزوّد التحليل البعيد بالبايتات التي يتلقاها، بعد أن يفعّل المشغّل التحليل البعيد.

الطبقة 1: بوابة الاستيعاب

Section titled “الطبقة 1: بوابة الاستيعاب”
  • حدود مُطبَّقة أثناء البث: تقبل منصة المستندات 50 MiB على الأكثر لكل ملف (documents.intake.max_bytes) و10 مدخلات على الأكثر لعملية دمج واحدة (documents.intake.max_files)؛ ويأخذ رفع الويب ملفًا واحدًا لكل طلب. وتستخدم مرفقات بوابة المحادثة 20 MiB لكل ملف، و10 ملفات، و50 MiB إجمالًا لكل دور. تُفرض الحدود أثناء البث، قبل التخزين المؤقت غير المحدود. ولا تقرأ مسارات الويب التي تأخذ JSON أكثر من 2 MiB من متن الطلب.
  • احتواء مساحة العمل (مسار الاستيراد): تُحلّ الاستيرادات المحلية الآمنة لمساحة العمل عبر workspace.binding.resolve_active_root()؛ ويُرفض المسار الذي يغادر موقعه المحلول مساحةَ العمل النشطة (..، أو مسار مطلق في مكان آخر، أو رابط إلى الخارج).
  • عدم تطابق الامتداد/المحتوى: شمّ MIME يرفض امتدادات الملفات غير المتطابقة قبل أن يرى أي مُحلِّل المحتوى.
  • القبول قبل المتن: يجيب رفع الويب عن ميزانياته — الطول المُعلَن، وتفعيل الكتابات، والحدّ الأدنى للتخزين (محتسبًا الملف الوارد)، والطابور، وسقوف المستأجر، ونافذة المعدّل — قبل قراءة أي بايت، ولا يُستقبل في وقت واحد إلا عدد محدود من عمليات الرفع لكل عملية؛ ويعيش ملف التخزين المؤقت للرفع (spool) تحت جذر التخزين، مَقيسًا ومكنوسًا.
  • الاستيعاب المُستولى عليه لا يستطيع النشر: الاستيعاب الذي ثبت موت مالكه، أو انقضى عقد إيجاره (انهار مضيفه أو قُطع اتصاله)، تتراجع عنه عملية أخرى، ولا تُنشأ المهمة التي ستنشره إلا ما دام الحجز ما زال حجزه — فلا يستطيع استيعاب قديم استُؤنف أن يضع مستندًا بجانب الاسترداد أو فوقه أبدًا. وتحمل صفوفه المتبقية رمزه (token) وتُزال.
  • النطاق يأتي من الخادم: تأخذ مسارات الويب المستأجر من سياق المستأجر في الطلب، والفاعل من الهوية المسجّل دخولها، لا من متن الطلب أبدًا.
  • قراءات المهام تُفوَّض كقراءات المستندات: كل حالة مهمة وسجلّها وإلغائها وإعادة محاولتها تسمّي مستدعيها وتفحص ACL المستند (الإلغاء وإعادة المحاولة يحتاجان صلاحية الكتابة)؛ والمهمة التي لا يستطيع المستدعي قراءتها لا يمكن تمييزها عن مهمة غير موجودة (الويب، وأدوات الوكيل، والبوابة)، ومفتاح عدم التكرار (idempotency) الذي استخدمه فاعل آخر يُجاب بـ idempotency_conflict. ومسار تدقيق العمليات للمشرف فقط. وفي التثبيت ذي المشغّل الواحد يعمل كل سطح (الويب، والمحادثة، وأدوات الوكيل) بصفته المشغّل، لا بصفة كل مستخدم محادثة؛ وتحصل المستندات الأقدم على صف ACL ‏owner مُدقَّق له (documents.principal). ومع وجود مستخدمي المنصة ترفض المحادثة والوكيل بدلًا من العمل نيابة عن الجميع.

داخل عامل المُحلِّل، وقبل أن يفتح مُحلِّلٌ ملفًا، تفحص طبقة شمّ المحتوى:

الفحصالدفاع
تحليل بنيوي لـ OOXMLيرفض مسارات الأعضاء غير الآمنة (المطلقة، وحرف محرك الأقراص، و..)، والأعضاء التي هي روابط رمزية، والأرشيفات المشفّرة، وXXE ‏(<!DOCTYPE، <!ENTITY، وكذلك بترميز UTF-16/32)، والعلاقات الخارجية (TargetMode="External")، وحمولات الماكرو (vbaProject.bin، والامتدادات المفعَّل فيها الماكرو مثل .docm)، والكائنات المضمّنة (embeddings/، activeX/، oleObjects/)، والأرشيفات المتداخلة، وقنابل الضغط (الحجم بعد التمدد، ونسبة التمدد، وعدد الصور فوق ميزانيات قابلة للضبط)
سياسة PDFترفض ملفات PDF المشفّرة، والملفات متعددة الصيغ (PDF/ZIP)، والمحتوى النشط: JavaScript، ونماذج XFA، والإطلاق، وGoTo البعيد والمضمَّن (بما في ذلك فعل /GoTo أو /Thread يسمّي ملفًا آخر عبر /F)، وSubmitForm، وImportData، وأفعال rendition/rich-media/sound/movie، والملفات المضمّنة (مع /Type أو بدونه)، والروابط التي ليست http/https/mailto مطلقة (أهداف الملفات ومشاركات الشبكة تسرّب بيانات الاعتماد). ويبقى التنقل داخل المستند. وتُطابَق أسماء الأفعال والمفاتيح دون تمييز لحالة الأحرف. جولة واحدة محدودة (documents/pdf_policy.py: 100,000 كائن على الأكثر وعمق 128؛ وتُرفض البنية غير المقروءة أو غير المحسومة) تُطبَّق في العامل عند الاستيعاب، وعلى كل مدخل ومخرج تعديل (التنقيح ضمنها)، وعلى كل PDF مُصيَّر. ولا يُفتح PDF يقدّمه مستخدم إلا داخل العامل؛ والفاتحات الثلاث المتبقية داخل العملية تفحص ملفات PDF صيّرتها كاظمه نفسها للتو، وكلٌّ منها مُصرَّح به في tests/test_pdf_openers_stay_in_workers.py
صلاحية UTF-8ترفض تسلسلات البايت غير الصالحة من دون محاولة فك ترميز فقداني
التحقق من نوع المحتوىيرفض ثنائيات تدّعي أنها نص، وHTML يدّعي أنه نص عادي
عدد أعضاء الأرشيفيفرض حدًّا أقصى قابلًا للضبط لعدد أعضاء الأرشيف
التصيير (HTML/Markdown إلى PDF)لا يجلب شيئًا خارجيًا: فقط معرّفات data: والملفات في مجلد الأصول المعتمَد للمهمة؛ وأي شيء آخر يوقف التصيير (external_resource_denied)

لا إعداد يُرخي هذه الفحوص. فالإعدادان documents.security.allow_encrypted_documents وdocuments.security.allow_external_render_resources كانا يُقرآن ولا يُستشاران أبدًا؛ وأُزيلا في 2026-10-13، ولكل إعداد مستندات متبقٍّ قارئ (tests/test_document_config_fields_read.py).

الطبقة 3: تنفيذ معزول — ملفّا عزل

Section titled “الطبقة 3: تنفيذ معزول — ملفّا عزل”

يختار إعداد المشرف security.execution.document_worker_isolation (الإعدادات ← المستندات) أين تعمل كل مهمة تحليل وOCR وتصيير وتعديل؛ ويغلب KAZMA_DOCUMENT_WORKER_ISOLATION حين يُضبط. والإعداد محمي من أدوات الإعداد لدى الوكيل، ولا تغيّره استعادة الإعدادات أبدًا؛ والإعداد الذي لا يمكن قراءته يرفض المستندات. ويُبلغ GET /api/documents/ops/readiness وGET /api/documents/health عن ملف العزل النافذ (worker_isolation): وضعه، وهل يصمد عزل نظام الملفات والشبكة، ولملف الحاوية إثبات الوسيط. ويقرأ hostile_document_profile في الجاهزية unqualified للملف process، وcontainer_broker (أو unavailable ما دام الوسيط غير جاهز) للملف container.

ويعمل الفحص البنيوي المسبق لـ PDF/OOXML والإنقاذ المحلي بـ Docling أيضًا في عامل المُحلِّل المحدود. ويتطلب فحص PDF بنية مقروءة، ويتتبّع الأفعال المتداخلة وغير المباشرة والمتسلسلة ضمن ميزانيات اجتياز. ولا تستطيع تبعيات الفحص المفقودة أو البنية غير المقروءة أن تجيز ملفًا.

process (الافتراضي): عملية فرعية على المضيف، بلا سجن لنظام الملفات أو الشبكة

Section titled “process (الافتراضي): عملية فرعية على المضيف، بلا سجن لنظام الملفات أو الشبكة”
  • python -I (وضع معزول — لا حزم موقع للمستخدم).
  • بيئة منقّاة: لا يرث العامل إلا قائمة سماح من متغيرات النظام (PATH، والإعدادات المحلية (locale)، ومجلدات الملفات المؤقتة وما شابه)؛ ولا تُمرَّر الأسرار والمتغيرات غير الآمنة (PYTHONPATH، وLD_* وما شابه).
  • Windows: كائن مهمة (Job Object) واحد يحدّ شجرة عمليات العامل كلها — الذاكرة (documents.workers.memory_mb، افتراضيًا 1,024 MiB)، و32 عملية نشطة، وسقف صارم بمعالج منطقي واحد (معدّل المعالج في كائن المهمة حصة من الآلة كلها، لذا يُحجَّم من عدد المعالجات). يبدأ العامل معلَّقًا ولا يعمل إلا حين يمسكه كائن المهمة: فـpython.exe في البيئة الافتراضية (venv) مُطلِق (launcher)، والمهمة التي أُسندت متأخرة لحظةً أمسكت المُطلِق وحده، فكان التشغيل الأول لكل عملية قبل 2026-10-13 يجري بلا هذه الحدود (ولم يكن سقف المعالج يحدّ شيئًا). وتبقى المهلة الزمنية المضبوطة (wall-clock) الحدَّ الكلي لزمن التشغيل؛ وهذه ليست حصة إجمالية من ثواني المعالج. ويطبّق Unix حدود الذاكرة (فضاء العناوين) وزمن المعالج بـ setrlimit، لكل عملية لا لكل شجرة؛ وتحديد عدد العمليات غير مُنفَّذ هناك بعد.
  • مهلة قابلة للضبط — تُقتل العملية ولا تُترك تتسرّب.
  • stdout/stderr محدودان؛ حجم المخرجات مقيّد.
  • عند المهلة/OOM/الخروج الصامت: فشل مغلق — لا يفلت أي جزء من البيانات.

حدّ الاحتواء: حدّ العملية الفرعية لا يحصر الوصول إلى نظام ملفات المضيف، ولا يقيم فضاء أسماء شبكة / سجن جدار ناري. وبيانات الاعتماد المنقّاة وحدود الموارد لا تقيم تنفيذًا معزولًا عن الشبكة تمامًا (air-gapped). ويفشل إعداد حدود الموارد في Windows مغلقًا قبل أن تبدأ شيفرة المُحلِّل إن تعذّر ضبط كائن المهمة. وتُبلغ الجاهزية عن هذا الملف بأنه hostile_document_profile: unqualified؛ استخدم ملف الحاوية للمستندات التي لا تثق بها.

container: حاوية مُثبَتة واحدة لكل مهمة (ملف نشر Docker)

Section titled “container: حاوية مُثبَتة واحدة لكل مهمة (ملف نشر Docker)”
  • وسيط (broker) منفصل (kazma_core/documents/worker_broker.py؛ في حاويته الخاصة في deploy/docker-compose.document-broker.yml، أو في التثبيت الأصلي عملية يشغّلها الحارس بجانب كاظمه) هو العملية الوحيدة التي تمسك اتصال محرك Docker. فواجهة المحرك البرمجية سلطة جذر على المضيف: لا تُسلَّم إلى كاظمه أبدًا. ويستمع وسيط الحارس على loopback فقط، ويصادق كل طلب بملف رمز لا يقرؤه إلا حساب الحارس، ويعمل ببيئة مقلّصة إلى ما يحتاجه Python وواجهة docker السطرية (تبقى أسرار الحارس نفسه خارجها).
  • ترسل كاظمه الطلب والملفات التي يسمّيها بالضبط في تدفّق واحد محدود ومفحوص البصمات (worker_bundle.py). ولا يُركَّب تخزين المستندات المشترك أبدًا؛ وstorage_root المؤقت للمُحلِّل دليل خاص بكل مهمة.
  • تعمل كل مهمة في حاوية جديدة من صورة واحدة، مثبَّتة بمعرّف صورتها حين يبدأ الوسيط: --network none، وبلا تركيبات (mounts)، وجذر للقراءة فقط، وtmpfs على /tmp محدود الحجم (nosuid,nodev,noexec)، وذاكرة مساوية لحدّ الذاكرة+التبديل (لا تبديل)، وحدود للمعالج وPID، وحدود ulimit لـ nofile وcore، و--ipc none، وفضاء أسماء cgroup خاص، وبلا سجل حاوية، وإسقاط كل قدرة (capability)، وno-new-privileges، وuid 10001، وملف seccomp الافتراضي في Docker، و--init لحصد العمليات المتفرعة. ويضبط مشغّل الوسيط الحدود عند تشغيله (الافتراضيات: 2 GiB ذاكرة، ومعالج واحد، و256 PID، و768 MiB لـ /tmp، ومهمتان في وقت واحد). ويستطيع العميل أن يطلب واحدًا من ثلاثة عمال ثابتين (المُحلِّل، والتعديل، والمُصيِّر) ومهلة أقصر — لا صورة ولا أمرًا ولا متغير بيئة ولا تركيبًا ولا حدًّا أبدًا؛ والعامل الرابع، المسبار (probe)، لا يعمل إلا لإثبات الوسيط نفسه وتقرير المحرك (ولمهام الإجهاد من وسيط بُدئ بـ --qualification، لا في الإنتاج أبدًا).
  • الإثبات: قبل أن يخدم، يشغّل الوسيط مسبارًا داخل حاوية مهمة حقيقية، ويفحص الهوية (uid وgid يساويان 10001 بالضبط، لا مجرد أنها ليست جذرًا)، والقدرات (المجموعات الفعّالة والمسموحة والمحيطة والحدّية كلها فارغة)، وno_new_privs، وseccomp، وواجهات الشبكة (lo فقط)، ورفض اتصال خارجي واستعلام DNS، ومسارات المضيف الظاهرة، وكل تركيب، والجذر للقراءة فقط، وحدود cgroup للذاكرة/التبديل/PID/المعالج، وحجم tmpfs. وأي عدم تطابق يرفض بدء الوسيط.
  • مراقب المهلة (watchdog)، وسقف المخرجات، والعميل الذي ينقطع، كلٌّ منها يقتل حاوية المهمة. وتُفحص كل حاوية (ويُبلَّغ عن القتل بسبب OOM بوصفه كذلك) وتُزال، ويُتحقَّق من الإزالة؛ والحاوية التي تتعذر إزالتها تُبقي خانتها خارج الخدمة. ويزيل الوسيط عند بدئه الحاويات التي تركها تشغيل سابق.
  • التدفق العائد غير موثوق: لا يُقبل إلا مستند نتيجة العامل وoutput.<ext> واحد، وتُحدّ الأحجام قبل كتابة أي شيء، وتُفحص البصمات، ولا يُكتب فوق أي ملف قائم.
  • فشل مغلق: الوسيط غير المضبوط، أو المتعذر الوصول إليه، أو غير المُثبَت، أو المشغول، يرفض التشغيل بخطأ مُصنَّف (worker_isolation_unavailable / worker_isolation_busy، تُعاد محاولته ضمن ميزانية الطابور المحدودة)؛ والإعداد غير الصالح يرفض كل تشغيل (worker_isolation_invalid). ولا رجوع إلى الملف process.

مؤهَّل على Docker Engine 29.8.2 (Docker Desktop، وحاويات Linux، وcgroup v2) على Windows 11، في 2026-10-13: الإثبات، وتكافؤ التحليل مع العامل المحلي على عيّنات إنجليزية/عربية/ثنائية اللغة، والمجموعة العدائية، والتعديل والتصيير، وحدود الذاكرة/PID/المعالج/المساحة المؤقتة/المهلة/المخرجات، والإلغاء عند انقطاع العميل، وتنظيف العمليات المتفرعة، وكنس الحاويات المتروكة، وتشغيل لطوبولوجيا حاوية الوسيط وحاوية العميل (الدليل وحدوده: docs/plans/DOCUMENTS_INDUSTRIAL_HARDENING_GOAL.md، F02).

مراجعة مستقلة. في 2026-10-15 اختبر مراجع مستقل (لم يشارك في تأليف كاظمه، ولا يستخدم بنية المالك التحتية) هذه الطبقات مقابل الادعاءات الواردة في docs/audits/Q04_DOCUMENTS_SECURITY_REVIEW_PACK_2026-10-15.md. صمد ثلاثة عشر ادعاءً من أربعة عشر. والملاحظة (فعل /GoTo أو /Thread يسمّي ملفًا آخر كان يمرّ من سياسة PDF) أُصلحت وأُعيد اختبارها: docs/audits/Q04_REVIEW_RESULTS_2026-10-15.md.

  • جاهزية المحركات هي جاهزية الصورة. يجمع الوسيط عند البدء تقرير الصورة نفسها عن المُحلِّلات والمُصيِّرات وأدوات التعديل وOCR (ويرفض الخدمة بدونه)؛ وفي هذا الملف تقرّر كاظمه ما تستطيع تشغيله من ذلك التقرير، لا من حزم المضيف أبدًا، وتنبّه الجاهزية إلى الصورة المبنية من شيفرة مختلفة، أو الصورة الأقدم، أو بناء احتياطي (image_code_matches، وimage_current، وimage_fallback).
  • يرفض security.execution.container_required (ملف التنفيذ الصارم) الملف process للمستندات (worker_isolation_required)؛ والسياسة التي لا يمكن قراءتها ترفض كذلك.

غير مؤهَّل / الحدود: مضيف Docker Engine على Linux (تشغّله مهمة CI ‏Document worker container profile، ولم تكن قد جرت أول مرة بعد في 2026-10-13)، وDocker بلا صلاحيات جذر (rootless)، وPodman، وKubernetes؛ والحاويات تتشارك نواة المضيف (أو الآلة الافتراضية)، ولا بيئة تشغيل gVisor أو Kata مؤهَّلة؛ وطبقة الإنقاذ المحلي بـ Docling ليست في صورة العامل؛ وكل مهمة تدفع كلفة بدء حاوية (نحو 0.35–0.8 ث على مضيف التأهيل)؛ ونقل TCP لدى الوسيط نص صريح مُصادَق بالرمز للشبكات الخاصة فقط (العنوان غير loopback يحتاج --allow-non-loopback؛ فضّل مقبس unix)؛ وصورة amd64 فقط؛ ولا اختبار تحمّل تمثيلي.

التحليل البعيد (خروج إلى طرف ثالث)

Section titled “التحليل البعيد (خروج إلى طرف ثالث)”

حين يحرز تحليلٌ درجةً دون عتبة الإنقاذ، تستطيع الطبقة البعيدة رفع بايتات المستند الأصلية إلى LlamaParse أو Reducto.

  • معطَّل افتراضيًا (documents.security.remote_parse). ومفتاح API لمزوّد في البيئة ليس موافقة. وKAZMA_REMOTE_PARSE=0 (وKAZMA_DOCLING=0 للطبقة المحلية) لا يستطيع إلا تعطيل طبقة، لا تفعيلها أبدًا.
  • يعمل في عملية كاظمه بعد أن يعود العامل، خارج ملفّي العزل كليهما، لأنه يحتاج الشبكة.
  • قبل كل مزوّد يُقرأ الإعداد الحي من جديد (فالإلغاء يوقف الرفع التالي) ويُكتب صف تدقيق egress دائم (remote_parse_attempt) يحمل مستأجر المهمة، وفاعلها، ومستندها، ونسختها، والمهمة. والإخفاق في حفظه، أو مهمة بلا فاعل، يرفض الرفع. وتُدقَّق النتيجة بوصفها remote_parse_success أو remote_parse_failure، والمزوّد الفاشل يُفسح المجال للمزوّد المضبوط التالي. والتحليل البعيد المرفوض أو الفاشل يُسجَّل بمستوى WARNING مع نوع خطئه؛ وتُحفظ القراءة المحلية.
  • عميل المزوّد يتجاهل متغيرات بيئة الوكيل (proxy)، ولا يتبع أي إعادة توجيه، ويرفض ردًّا يتجاوز 32 MiB. والنص المُنقَذ لا يحمل أرقام صفحات فعلية.
  • القراءة العابرة (مرفق محادثة) لا نطاق تدقيق دائمًا لها، فتُرفض محاولتها البعيدة قبل أن يتحرك أي بايت.

فحص البرمجيات الخبيثة (ClamAV، قابل للتوصيل)

Section titled “فحص البرمجيات الخبيثة (ClamAV، قابل للتوصيل)”

يشغّل الاستيعاب kazma_core.documents.malware.scan_if_configured على البايتات المعزولة (quarantined)، وكل تحليل في الطابور يفحص الأصل من جديد وفق السياسة النافذة حينها. والماسح هو clamdscan المحلي (المفضَّل) أو clamscan؛ ولا تذهب بايتات المستندات أبدًا إلى خدمة فحص عبر الشبكة.

الوضع (documents.security.malware_scan)السلوك
offلا فحص أبدًا
auto (الافتراضي)يفحص عندما يكون clamscan أو clamdscan على PATH؛ وإلا، وكذلك حين يفشل الماسح، يتخطى مع سطر في السجل (إلا في وضع الفشل المغلق)
onيتطلّب ماسحًا يعمل

حين يكون الفحص مطلوبًا (on، أو documents.security.malware_fail_closed=true، وهو الافتراضي تحت KAZMA_PRODUCTION=1) ويتعذر تشغيله، يُحتجز المستند ولا يُحكم عليه بأنه غير آمن أبدًا: يُرفض الرفع بـ malware_scanner_unavailable، وتُبلغ الجاهزية accepting_durable_writes: false، وتُعاد محاولة المهمة التي في الطابور ضمن ميزانية الطابور المحدودة، ثم تُنقل إلى الرسائل الميتة. والملفات المصابة تُرفض بـ malware_detected. ثبّت حزم نظام ClamAV على حدة (ليست تبعية pip).

الطبقة 4: تسييج التعليمات

Section titled “الطبقة 4: تسييج التعليمات”

يصل نص المستند إلى النموذج داخل سياج بيانات غير موثوقة، مثلًا:

<kazma:data source="document" untrusted="true">
[document content]
</kazma:data>
المسارمصدر السياج
مستند يُقرأ عبر أدوات المستندات لدى الوكيلdocument
المقتطف التلقائي لمرفق محادثة (أول 2,000 حرف، بعد تحليل DocumentService عابر بأفضل جهد)document_attachment
نتائج المستندات من بحث في مكتبة المعرفةdocument_knowledge
نص مستند في موجِّه “نظّم هذا في Markdown” (معالج نيّة المستندات وخط المعالجة)document

يسيّج كل سطح من هذه الأسطح مخرجاته بنفسه (format_untrusted_block()). وهو ليس إعدادًا: لا شيء يعطّل السياج (وأُزيل documents.security.fence_llm_content غير المستخدم في 2026-10-13).

يخبر هذا السياج النموذج بأن المحتوى بيانات ملاحَظة لا تعليمات — ما يخفف حقن التعليمات من نص المستند. وهو يقلّل نجاح الهجمات ولا يقضي عليه (الأرقام المَقيسة: سياج التوجيه).

وتمرّ دلتات تعليمات التحسّن الذاتي عبر آلية السياج نفسها بواسطة format_untrusted_block() مع فحص السلامة prompt_fence.

الطبقة 5: التحقق من التنقيح

Section titled “الطبقة 5: التحقق من التنقيح”

التنقيح عملية PDF ‏(pdf:redact، بـ PyMuPDF وPillow داخل عامل التعديل):

  1. يمرّ المصدر بسياسة PDF أولًا. وتُرفض الصفحة التي فيها صور أو رسوم متجهية (redaction_source_not_verifiable): فهي تحتاج تنقيحًا مُراجَعًا.
  2. يجب العثور على كل مصطلح بإحداثياته (وإلا redaction_term_not_located).
  3. تُحوَّل كل صفحة إلى صورة نقطية بدقة 200 dpi، وتُطلى المطابقات، ويُعاد بناء الصفحة صورةً — تنقيح فيزيائي/نقطي، لا صندوق تغطية فوق نص ما زال موجودًا.
  4. يُتحقَّق من المخرج: لا يمكن استخراج أي مصطلح من جديد ولا العثور عليه في البايتات الخام، ولا تبقى بيانات وصفية أو مرفقات أو تعليقات توضيحية أو حقول نماذج، ويبقى عدد الصفحات كما هو، وتُصيَّر كل صفحة. ثم يمرّ بسياسة PDF، ككل مخرج تعديل.
  5. النتيجة قطعة جديدة غير قابلة للتغيير — لا تُعاد كتابة الأصول بصمت. ولا تُسجَّل مصطلحات التنقيح ولا تُدقَّق أبدًا.

نموذج التأكيد:

المسارتأكيد تفاعلي؟
صفحة المستندات في واجهة الويبنعم — kazmaPrompt / kazmaConfirm قبل POST …/redact
REST API / أداة الوكيل document_redact / البوابة /documents redactلا حوار واجهة — يُعتمد المستدعي بوصفه فاعلًا مُصدَّقًا؛ وACL ما زال ينطبق

التنقيح بالتغطية غير الآمن (صناديق معتمة فوق النص من دون إزالة المحتوى الكامن) رُصد في الشيفرة السابقة للمنصة وهو ليس مسار المنصة.

الطبقة 6: ثبات سجل التدقيق

Section titled “الطبقة 6: ثبات سجل التدقيق”

document_audit_events يرفض أي تعديل بعد الإدراج في مخزني البيانات الوصفية كليهما؛ ولا يحذف الصفوف التي انقضت مدتها إلا مسح الاستبقاء. ففي SQLite ‏(documents.db) ترفضه مُشغِّلات UPDATE/DELETE، ويحذف المسح عبر راية تحكم مُحرَّسة داخل معاملته الخاصة. وفي Postgres يرفض مُشغِّل صفوف واحد (document_audit_guard، الدالة kazma_document_audit_guard) كل UPDATE وكل DELETE خارج معاملة يعلّمها المسح (SET LOCAL kazma.document_audit_prune = 'on')؛ ويقلّم المسح هناك أيضًا على دفعات محدودة. وحتى 2026-10-13 لم يكن لجدول Postgres مُشغِّل، ولم يكن التقليم يعمل إلا على SQLite (وفي Postgres كان يسجّل خطأ audit_prune ويُبقي كل صف). وpg_dump -t يحمل المُشغِّل لا دالته، لذا تُنشئ كل استعادة الدالة أولًا (db/pg_backup.KAZMA_PG_FUNCTIONS)، وتشترط بوابة أن تُصرَّح هناك كل دالة مُشغِّل في شيفرة المنتج (tests/test_document_audit_pg.py، على Postgres حقيقي، مع تمرين استعادة إلى قاعدة بيانات جديدة).

سجل التدقيق محصور بالمستأجر. والتفاصيل مقصورة على قائمة سماح من القيم الآمنة — محتوى المستند وأسماء الملفات ومصطلحات التنقيح والأسرار لا تدخل سجل التدقيق أبدًا. وكتابات التدقيق العادية بأفضل جهد (يُسجَّل الإخفاق وتستمر العملية)؛ أما تدقيق خروج التحليل البعيد فمطلوب، وإخفاقه يرفض الرفع.

  • تخزين موجَّه بالمحتوى: يُعرَّف كل كائن (blob) بـ SHA-256. المسار الفيزيائي: {root}/{kind}/sha256/{aa}/{bb}/{full_hash}.
  • التحقق من البصمة عند الكتابة: تُحسب بصمة البايتات أثناء تدفقها، وتُقارن بالبصمة والحجم المتوقَّعَين، وتُرقّى ذريًّا، وتُحسب بصمتها من جديد بعد النقل؛ وعدم التطابق يزيل الملف (BlobChecksumError). ويغطي هذا الترقيةَ من المعزول إلى الأصول.
  • إزالة التكرار: الكائن الذي سبق تخزين بصمته يعيد استخدام الملف المخزَّن بعد إعادة حساب بصمته؛ والملف المخزَّن الذي لم تعد بصمته أو حجمه مطابقين يرفض الكتابة.
  • الروابط: لا يتبع جمع المهملات رابطًا رمزيًا أو وصلة (junction) ولا يحذف عبرهما، ولا عبر ملف يغادر مساره الحقيقي المخزن، ولا يتبع نسخُ دليل البيانات في تصدير الترحيل رابطًا أبدًا. وينسخ النسخ الاحتياطي الأصلي للمستندات الكائنات التي تشير إليها لقطته من مسارات عنونة المحتوى الخاصة بها، ويتحقق من كل نسخة مقابل بصمة SHA-256 الخاصة بها.

شهادة المجموعة العدائية

Section titled “شهادة المجموعة العدائية”

تغطي مجموعة عدائية حتمية مولَّدة برمجيًا (26 حالة، kazma_core/documents/hostile_corpus.py):

الفئةالحالات
قنابل الأرشفةقنبلة ضغط، وأرشيف متداخل، وإغراق بالأعضاء
هجمات XML‏XXE، وعلاقات OOXML الخارجية
المحتوى النشطالماكرو؛ وJavaScript/الإطلاق/الملف المضمَّن في PDF، ونموذج XFA، وSubmitForm، وrendition، وملف مضمَّن بلا /Type
المراجع الخارجيةGoTo بعيد إلى مشاركة شبكة، ورابط إلى مشاركة شبكة
التشفيرPDF بتصريحات /Encrypt
متعدد الصيغملف جامع PDF/ZIP
عدم تطابق النوعبايتات HTML بامتداد .txt
الفسادOOXML مقطوع، ومرجع متقاطع مشوه، وUTF-8 غير صالح
هجمات الحدودتجاوز عدد الصفحات/البكسلات/الخلايا للميزانيات
حقن التعليماتنص شبيه بالتعليمات داخل المستندات
يونيكود/BiDiعربي-إنجليزي بامتدادات ملتبسة وعناصر تحكم bidi
ضابط التنقلPDF فيه تنقل داخل المستند ورابط https، ويجب أن يُحلَّل مع ذلك (ضابط سلبي)
انهيار المُحلِّلاختبار احتواء انهيار تركيبي

لكل حالة:

  • وصف مُراجَع.
  • تصرّف متوقع (رفض / تسييج).
  • رموز أخطاء متوقعة.
  • بصمة SHA-256 وحجم مسجَّلان.

يُقارَن البيان المُودَع (tests/fixtures/documents/hostile_manifest.json) بالبيان المولَّد حقلًا حقلًا. وتُستثنى بصمة SHA-256 والحجم من هذه المقارنة، لأن ضغط ZIP يختلف بين إصدارات zlib؛ وأي اختلاف آخر يُفشل الفحص hostile_corpus. والحالات الموسومة sniff تُفحص باستدعاء الشامّ في عملية الاعتماد؛ وحالات parse تمرّ عبر العامل المعزول ويجب أن تعود مرفوضة برمز متوقَّع أو مسيَّجة؛ وحالة sandbox تزيل نتيجة العامل ويجب أن تفشل مغلقة.

يجري الفحص في مهمة الاختبار في CI ‏(tests/test_document_certification_phase10.py) ومحليًا:

التشغيل: python scripts/certify_documents.py

ويُبلغ السكربت أيضًا عن ملف العزل المضبوط (process بحالة CONDITIONAL، لا «confined» أبدًا)، وعن مجموعة دقة الاستخراج، وعن تمرين نسخ احتياطي/استعادة لـ SQLite، ويعلّم تعدد النسخ، واختبار التحمّل متعدد الأيام، والمراجعة الخارجية بأنها NOT RUN.


الإجراءآمن؟التفصيل
ضبط documents.enabled=false✅يوقف الكتابات الدائمة الجديدة؛ والكائنات والمهام والبيانات والبيانات الوصفية القائمة محفوظة
ضبط documents.shadow=true✅وضعية الظل/الكناري؛ لا فقدان بيانات عند التبديل
ضبط documents.default_authoritative=true✅يوجّه مسار المنتج إلى المنصة؛ والتراجع يحفظ البيانات
حذف documents.db (أو جداول المستندات في Postgres)❌يدمّر البيانات الوصفية وحالة المهام وسجل التدقيق — انسخ احتياطيًا أولًا
حذف دليل مخزن المحتوى❌يدمّر جميع الكائنات — غير قابل للاستعادة من دون نسخة احتياطية

  1. شغّل المستندات التي لا تثق بها في ملف الحاوية (الإعدادات ← المستندات؛ ويشغّل الحارس الوسيط في التثبيت الأصلي)، وتأكّد من الجاهزية worker_isolation.ready وimage_code_matches: true، واضبط security.execution.container_required ليُرفض الملف process.
  2. ثبّت ClamAV ‏(clamscan/clamdscan على PATH) للإنتاج؛ وتحقّق من الجاهزية malware.available واضبط documents.security.malware_scan=on (أو أبقِ auto مع documents.security.malware_fail_closed=true، وهو الافتراضي تحت KAZMA_PRODUCTION=1).
  3. أبقِ documents.security.remote_parse معطَّلًا ما لم يكن مسموحًا بأن تذهب المستندات الأصلية إلى LlamaParse أو Reducto؛ وحين يكون مفعَّلًا، راجع صفوف egress في تدقيق العمليات.
  4. اضبط documents.capacity.storage_free_floor_bytes بتحفّظ (الافتراضي 512 MiB؛ ويفضّل كثير من المشغّلين ≥ 1 GiB).
  5. راجع documents.retention.* / documents.gc.* لأجل الامتثال.
  6. شغّل اختبار الدخان المحدود للاعتماد، ثم أهِّل النشر المقصود باختبار تحمّل حقيقي متواصل للتعافي تحت حِمل مختلط. ويبقى scripts/certify_documents.py --soak اختبار دخان ممتدًّا، لا دليلًا متعدد الأيام.
  7. راقب GET /api/documents/ops/capacity بحثًا عن أسباب التدهور.
  8. دقّق GET /api/documents/ops/audit دوريًا (للمشرف فقط).
  9. انسخ احتياطيًا حالة المستندات ومحتواها إلى جانب نسختك الاحتياطية الأساسية. تأخذ المهمة المجدولة كل ست ساعات لقطة لمخزن SQLite، أو تأخذ نسخة Postgres الاحتياطية المنسَّقة (kazma_core/documents/backup_pg.py) حين يكون المخزن المثبَّت على Postgres. وقد جرت تمارين الاستعادة على مخازن اختبارية؛ فقِس RPO/RTO على عتادك وبحجم مخزنك.
  10. تعامل مع التنقيح عبر API/الأدوات بوصفه عملًا مُميَّزًا — تأكيد الواجهة ليس بوابة على الخادم في تلك المسارات.