pdppl-pii-audit

> متى تُستعمل: "PDPPL personal-data audit: PII encryption, leaks, consent, erasure, breach. Trigger: PDPPL, PII, privacy, nationalid. بيانات شخصية، خصوصية، تشفير"

SKILL.md

GitHub

متى تُستعمل: "PDPPL personal-data audit: PII encryption, leaks, consent, erasure, breach. Trigger: PDPPL, PII, privacy, national_id. بيانات شخصية، خصوصية، تشفير"

مدقّق PDPPL لـ SchoolOS

الغرض: أن يخرج كلُّ بيانٍ شخصيٍّ في المنصّة مشفَّراً حيث يلزم، ومستوراً حيث يُعرض بالجملة، ومُدقَّقاً حيث يُصدَّر، وغائباً عن السجلّات — بأدوات المنصّة القائمة لا باختراعٍ جديد. المنصّةُ مدرسيّة: بياناتُ الطلبة بياناتُ أطفال، وهي والصحّةُ «ذاتُ طبيعةٍ خاصّة» بنصّ م.16 (المصدر: references/00-pdppl-articles.md).

متى تُستعمل ومتى لا

  • نعم: حقلٌ أو نموذجٌ جديد، مُسلسِلٌ أو قالبُ قائمة، مُصدِّرُ PDF/Excel، سطرُ تسجيل، رفعُ ملفّ، تدفّقٌ لوليّ الأمر، مراجعةُ دمج.
  • لا: صياغةُ رأيٍ قانونيٍّ ملزم — هذه إرشاداتٌ هندسيّة، والحكمُ القانونيّ للجهة المختصّة (الوكالة الوطنيّة للأمن السيبرانيّ) وللـDPO.

الإجراء

  1. شغّل الفاحص من شجرة عملك (يجد الجذرَ بـmanage.py؛ الإصدارُ القديم كان يُعلن «سليم» في كلّ شجرة عمل): python .claude/skills/pdppl-pii-audit/scripts/pii_scan.py [--app clinic] [--include-tests] النتائجُ مرشَّحات؛ الإيجابيّاتُ الكاذبة المعروفة في references/30-scanner.md.
  2. صنِّف كلَّ حقلٍ جديد: ذو طبيعةٍ خاصّة (صحّة، نفسيّ، دين، عِرق — وكلُّ ما للطفل) ← تشفيرٌ إلزاميّ ودورٌ محدود؛ تعريفيٌّ (الرقم الشخصيّ، الهاتف، العنوان، الجواز، الميلاد، الصورة) ← تشفيرٌ أو ثلاثيّةُ HMAC؛ تشغيليّ (الصفّ، الدرجة، الحضور) ← صلاحيّاتٌ لا تشفير.
  3. مرّ على المسارات الثمانية وقارن بأداتها القائمة (الجدول في references/10-platform-controls.md): التخزين · العرض الجماعيّ · التصدير · السجلّات وSentry · الصور المرفوعة · الموافقة · المحو · الاحتفاظ.
  4. شغّل الحرّاس ذوي الصلة (الأسماء في references/10-platform-controls.md) بأمر الاختبار في CLAUDE.md.
  5. اكتب الحكم: البندُ، ومسارُه:سطرُه، والمادّةُ المتحقَّقة، والأداةُ القائمة التي تعالجه، وما يحتاج قرارَ الـDPO.

القواعد وأسبابها

  • الرقمُ الشخصيّ في كشفٍ جماعيٍّ مستورٌ دائماً (|mask_id في القالب، mask_national_id في بايثون)، وكاملٌ فقط في وثيقةٍ فرديّةٍ مسمّاةٍ في tests/test_national_id_never_bulk.py. السبب: صفحةٌ بخمسين رقماً تُصوَّر وتُرسَل، والقارئ يحتاج أن يميّز لا أن يعرف. المصدر: core/privacy.py.
  • كلُّ ملفٍّ يخرج من المنصّة يترك سطرَ تدقيق بـcore.audit_export.log_export (نوعٌ، وعددُ صفوف، ورايةُ «رقمٌ كامل») — بلا اسمٍ ولا رقم. السبب: الملفُّ المنزَّل معالجةٌ تُخرج البيانَ من حوزة المنصّة.
  • لا قيمةَ شخصيّةَ في سطر تسجيل — المعرّفُ (UUID) وحده. PIIMaskingFilter يقنّع الأنماطَ (رقم، هاتف، بريد) في الإنتاج فقط، والاسمُ العربيّ لا نمطَ له فلا يُقنَّع. المصدر: core/logging_filters.py، tests/test_log_data_minimization.py.
  • AuditLog لا يُحذف ولا يُعدَّل — إلّا فصلُ هويّة الفاعل (user=None) عند المحو. السبب: أثرُ المساءلة يبقى والشخصُ يُفصل عنه. المصدر: core/models/audit.py::_ImmutableQuerySet.
  • نموذجٌ جديدٌ فيه مفتاحٌ إلى الطالب أو ملفٌّ مرفوع يُضاف إلى قائمتَي governance/erasure_service.py (_lazy_student_fk_models، _lazy_file_field_models). السبب: المحوُ يمرّ على قائمةٍ صريحة، ولا حارسَ يكتشف النموذجَ المنسيّ.
  • رقمُ المادّة من الجدول المتحقَّق لا من تعليقات الشيفرة — التعليقاتُ متضاربة (م.8 وم.11 وم.13 وم.18 وم.19 لموضوعاتٍ لا تطابقها). السبب: اقتباسٌ خاطئٌ في تقرير امتثالٍ أسوأُ من غيابه.
  • البيانُ الخاصّ الجديد (صحّيّ، نفسيّ…) قرارُ DPO قبل أن يكون شيفرة: م.16 تشترط تصريحاً من الإدارة المختصّة. والـDPO هو المالكُ نفسُه (ذاكرة user_dpo_role.md) — اعرض عليه مباشرةً.
  • العملُ في شجرة عملك وقواعدُ الإيداع والدفع في مهارتَي schoolos-flow وschoolos-git-safety.

فخاخٌ حقيقيّة

  • خطأ: phone = models.CharField(...) في نموذج وليّ أمرٍ جديد. الصواب: EncryptedTextField إن لم يُبحث به، وإلّا الثلاثيّة (خامٌ + _encrypted + _hmac) كما في core/models/user.py، وتعبئةُ القديم بأمر إدارةٍ عبر ORM (populate_phone_encryption مثالاً).
  • خطأ: البحثُ في EncryptedTextField بـfilter(field=q) — التشفيرُ غيرُ حتميّ فلا يطابق شيئاً. الصواب: hmac_field(q) على عمود _hmac.
  • خطأ: fields = "__all__" في مُسلسِل نموذجٍ فيه بياناتٌ شخصيّة. الصواب: قائمةٌ صريحة، ونسخةٌ «آمنة» بالاسم (UserSafeSerializer)، وستر الرقم كما يفعل UserBriefSerializer.to_representation.
  • خطأ: logger.info("طالب %s (national_id=%s)", user.full_name, nid) — موجودٌ اليوم في student_affairs/services.py. الصواب: logger.info("طالب id=%s", user.pk).
  • خطأ: {{ s.national_id }} داخل {% for %}. الصواب: {{ s.national_id|mask_id }} — والحارسُ يسقط الأوّل.
  • خطأ: request.FILES["photo"] يُحفظ كما وصل. الصواب: core.photo_privacy.clean_photo (يمحو EXIF/GPS) — تقريرٌ طبّيٌّ من جوّالٍ يحمل موقعَ البيت.
  • خطأ: «blood_type مشفّرة كبقيّة السجلّ الصحّيّ». الصواب: هي CharField صريحٌ اليوم (clinic/models.py) — بندٌ مفتوحٌ لقرار الـDPO لا حقيقةٌ مفروغٌ منها.
  • خطأ: «الموعدُ النظاميّ للإخطار في نصّ القانون 72 ساعة». الصواب: القانون (م.14) يوجب الإخطار؛ الـ72 ساعة من إرشاد الوكالة للجهات الخاضعة، وBreachReport.ncsa_deadline يحسبها.

المراجع

الملف متى تقرأه
references/00-pdppl-articles.md قبل أن تكتب رقمَ مادّةٍ أو تحكم أنّ شيئاً «يشترطه القانون»
references/10-platform-controls.md لتعرف الأداةَ القائمة والحارسَ لكلّ مسارٍ من الثمانية
references/20-patterns.md حين تكتب الإصلاح: حقلٌ مشفَّر، مُسلسِلٌ آمن، تسجيلٌ آمن، محوٌ، تصدير
references/30-scanner.md حين تقرأ مخرجَ الفاحص أو تعدّله
references/99-test-cases.md لاختبار تفعيل المهارة وجودة جوابها