تخطَّ إلى المحتوى

الأمان والسلامة

نموذج السلامة في كاظمة بالكامل: البوابات الثلاث المستقلة للموافقة البشرية في الحلقة (HITL)، وقوائم الأدوات الخطيرة الثلاث، وسلوك الفشل المُغلق في كل مكان، والتكامل التشفيري (مهارات HMAC، وتفويض Ed25519)، وتوصيات التحصين. كل ادّعاء موثَّق بمرجع مصدري.


1. البوابات الثلاث للموافقة البشرية في الحلقة (ولماذا ثلاث)

Section titled “1. البوابات الثلاث للموافقة البشرية في الحلقة (ولماذا ثلاث)”

تملك كاظمة ثلاث آليات مستقلة للموافقة البشرية في الحلقة. يغطّي كل منها مسار تنفيذ مختلفاً. ويؤدّي كسر أيٍّ منها إلى ثغرة أمنية في الأدوات الخطيرة غير المراقَبة.

flowchart TB
subgraph "Gate A: Graph interrupt (single-agent chat)"
A1[tool_worker_node] -->|danger tool + hitl_config| A2[interrupt]
A2 -->|resume Command| A3[execute with _hitl_approved=True]
end
subgraph "Gate B: Swarm bus (/swarm dispatch)"
B1[ToolRegistry.execute] --> B2[SafetyMiddleware.check]
B2 -->|danger tool| B3[bus.request_approval]
B3 -->|adapter callback| B4[approve/reject]
end
subgraph "Gate C: Pipeline checkpoints"
C1[pipeline at hitl step] --> C2[CheckpointManager pause]
C2 -->|auto-reject timeout| C3[reject]
C2 -->|approve_checkpoint| C4[resume_pipeline]
end
البوابةالمسارمكان وجودهاسطح الموافقة
Aمحادثة الوكيل الواحد (Web/Telegram/Discord/Slack)agent/graph_builder.pyPOST /api/approve/{thread_id} أو /hitl approve|deny
Bإرسال /swarmswarm/safety.py + tool_registry.pyأزرار محوّل الناقل (Telegram/Discord/Slack)
Cمهام السرب PIPELINEswarm/checkpoint.py + checkpoint_manager.pyPOST /api/swarm/tasks/{id}/approve

2. البوابة A — بوابة الرسم البياني (محادثة الوكيل الواحد)

Section titled “2. البوابة A — بوابة الرسم البياني (محادثة الوكيل الواحد)”

tool_worker_node (graph_builder.py:336) هو مكان وجود البوابة. عندما يُمرَّر hitl_config:

  1. يُختبَر كل استدعاء أداة معلَّق بـ requires_approval(tc["name"], hitl_config) (مستوردة من kazma_core.safety.hitl، السطر 356). تُقسَم الأدوات إلى safe_tools / danger_tools (الأسطر 418-429).
  2. لكل أداة خطيرة، يُبنى قاموس approval_input (type: "hitl_approval"، اسم الأداة، الوسائط، الرسالة) ويُستدعى interrupt(approval_input) (السطر 493) — فيتوقّف الرسم البياني (suspend).
  3. عند الموافقة، يُنسخ استدعاء الأداة ويُحقن approved_tc_args["_hitl_approved"] = True (الأسطر 495-503) كي يتخطّى tool_registry.execute() فحص الناقل الزائد عن الحاجة.
  4. عند الرفض، يُعاد ToolResult مع is_error=True عبر _denied_result (الأسطر 467-475).

خاملة افتراضياً: إن كان hitl_config خاطئاً، تنتقل جميع الأدوات إلى safe_tools (السطر 429). لا تُفعَّل البوابة إلا عند تمرير hitl_config إلى build_supervisor_graph().

2.2 مواقع البناء التي تُفعّل البوابة

Section titled “2.2 مواقع البناء التي تُفعّل البوابة”

يجب تمرير hitl_config وقت البناء. يوجد ثلاثة مواقع بناء؛ اثنان يمرّرانه (مُفعَّل)، وواحد لا يمرّره (خامل):

موقع البناءالملف:السطريمرّر hitl_config؟
KazmaAgent.get_streaming_graph()agent_runner.py:530-539مُفعَّل (مسار محادثة SSE)
KazmaAgent._ensure_graph()agent_runner.py:566-576مُفعَّل (مسار التشغيل)
إعادة التجميع عند بدء التشغيل في app.pykazma-ui/kazma_ui/app.py:741-751مُفعَّل
مصنع create_supervisor_graph()graph_builder.py:974-985خامل (دخول CLI/طرف ثالث)

تصحيح أرقام أسطر AGENTS.md: تستشهد ملاحظات أقدم بـ “app.py ~line 966”. هذا غير دقيق — فـ graph_builder.py:966 هو aiosqlite.connect غير المرتبط داخل create_supervisor_app()، الذي لا يمرّر hitl_config. إعادة التجميع الفعلية عند بدء التشغيل تقع في kazma-ui/kazma_ui/app.py:741-751.

يستأنف الرسم البياني عبر LangGraph Command(resume=...). نقطة نهاية الموافقة هي POST /api/approve/{thread_id} في kazma-ui/kazma_ui/routes_direct.py:454 (الدالة approve_tool، السطر 455) — وليست في app.py:

# routes_direct.py:525-532
from langgraph.types import Command
resume_value = {"approved": approved, "reason": body.get("reason", "")}
graph_ref.ainvoke(Command(resume=resume_value), config=config)

الأمان على نقطة النهاية هذه:

  • عند ضبط KAZMA_SECRET، تتطلّب ترويسة X-Kazma-Secret (أو كوكي) مطابَقة عبر secrets.compare_digest (الأسطر 456-465).
  • إ enforced الملكية (الأسطر 480-522): يُحَلّ المالك عبر المنصّات (sender_id/owner/session_id/user_id) وتُرفَض الموافقات بين المستخدمين المختلفين بـ 403.

تُ persist الأدوار المتوقفة في الـ checkpointer (SQLite WAL) — فتصمد أمام إعادة التشغيل.


3. البوابة B — بوابة ناقل السرب (إرسال /swarm)

Section titled “3. البوابة B — بوابة ناقل السرب (إرسال /swarm)”

ToolRegistry.execute() (tool_registry.py:335):

  1. يُخرج _hitl_approved من الوسائط (السطر 349).
  2. إن لم يكن مُوافَقاً عليه مسبقاً، يستدعي get_safety() (السطر 395).
  3. للأدوات الخطيرة (safety.is_danger_tool(tool_name)، السطر 397)، يستدعي await safety.check(...) (الأسطر 400-405).
  4. إن لم تُوافَق عليها ← يُعيد is_error=True “denied by HITL approval gate” (الأسطر 406-410).
  5. الفشل المُغلق: أي استثناء في فحص السلامة يُعيد is_error=True “blocked — SafetyMiddleware unavailable” (الأسطر 411-417).

swarm/safety.py — الصنف SafetyMiddleware (السطر 47). (لا يوجد صنف باسم SafetyGate أو SafetyChecker في أي مكان — تلك التسمية في بعض الوثائق الأقدم غير دقيقة.)

الدالةالسلوك
check() (متزامنة، السطر 106)للأدوات الخطيرة: إن كان NullBusAdapter مُفعّلاً وallow_headless_danger=False ← رفض (الأسطر 147-160)؛ وإلا bus.request_approval(...) وانتظار (الأسطر 162-177).
check_sync() (السطر 179)فشل مُغلق افتراضياً. NullBusAdapter + allow_headless_danger=False ← يُعيد False (الأسطر 200-209). حتى مع محوّل حقيقي، لا يستطيع المسار المتزامن الانتظار ← يحجب. أي استثناء في الناقل ← فشل مُغلق ما لم يكن allow_headless_danger=True (الأسطر 214-223).
is_danger_tool / is_sensitive_readالأسطر 96، 100.
add_danger_tool / remove_danger_toolالأسطر 86-94.

علم المُنشئ allow_headless_danger: bool = False (السطر 70) هو مخرج الاختبار/التطوير. الافتراضي هو الفشل المُغلق.

3.3 محوّلات الناقل وأولوية المُفرد

Section titled “3.3 محوّلات الناقل وأولوية المُفرد”

محوّلات الناقل (جميعها فئة فرعية من BusAdapter، ABC في swarm/bus.py:66):

المحوّلالموقع
TelegramBusAdapterkazma-gateway/kazma_gateway/adapters/telegram_bus.py:58
DiscordBusAdapteradapters/discord_bus.py:31
SlackBusAdapteradapters/slack_bus.py:31

الناقل مُفرد على مستوى الوحدة (get_message_bus()، bus.py:282-287). يكون محوّل واحد فقط مُفعّلاً في كل مرة. توصيل الأولوية في kazma-ui/kazma_ui/app.py:506-556:

  • يُجرَّب Telegram أولاً (_bus_wired = True عند السطر 516).
  • Discord فقط if not _bus_wired (السطر 524).
  • Slack فقط if not _bus_wired (السطر 540).

الأولوية: Telegram > Discord > Slack.

يستحرس pytest (السطر 504) عن قصد من توصيل محوّلات حقيقية أثناء الاختبارات.

3.4 حلّ استدعاء الموافقة

Section titled “3.4 حلّ استدعاء الموافقة”

يُحلِّل handle_callback() لكل محوّل مُعرِّف استدعاء المنصّة ويُعيد asyncio.Event:

  • Telegram: swarm_approve_<task_id> / swarm_reject_<task_id> (telegram_bus.py:302-333).
  • Discord: أزرار components v2 (discord_bus.py:238).
  • Slack: استدعاء تفاعلي (slack_bus.py:270).

4. البوابة C — نقاط حفظ خط المعالجة

Section titled “4. البوابة C — نقاط حفظ خط المعالجة”

موثَّقة في تنسيق السرب ← نقاط حفظ خط المعالجة. الملخّص:

  • _handle_pipeline_checkpoint (engine.py:889) ← CheckpointManager.handle_pipeline_checkpoint.
  • تُسلَّح مهلة الرفض التلقائي إن كان checkpoint_timeout > 0.
  • approve_checkpoint (engine.py:920) / reject_checkpoint (engine.py:990).
  • يُعيد restore_paused_tasks() تسليح المهل بعد إعادة التشغيل.
  • نقاط النهاية: POST /api/swarm/tasks/{id}/approve / /reject (routes_tasks.py:612, 657).

5. قوائم الأدوات الخطيرة (ثلاث منها)

Section titled “5. قوائم الأدوات الخطيرة (ثلاث منها)”

تستخدم البوابات الثلاث قوائم أدوات خطيرة مختلفة. هذا متعمَّد (لكل مسار دلالات مختلفة) لكنه سهل الخطأ.

5.1 البوابة A (الرسم البياني) — kazma.yaml safety.hitl.require_approval_for

Section titled “5.1 البوابة A (الرسم البياني) — kazma.yaml safety.hitl.require_approval_for”

الافتراضي (kazma.yaml:84-94): file_write، file_delete، shell_exec، code_exec، python_exec، spawn_agent، spawn_agents، schedule_task، cancel_scheduled.

الاحتياطي في الكود إن لم يُضبط: DEFAULT_DANGER_TOOLS = ["file_write", "file_delete", "shell_exec"] (safety/hitl.py:41).

5.2 البوابة B (ناقل السرب) — _EXTENDED_DANGER

Section titled “5.2 البوابة B (ناقل السرب) — _EXTENDED_DANGER”

swarm/safety.py:23-31: DEFAULT_DANGER_TOOLS (file_write، file_delete، shell_exec) + python_exec، code_exec، spawn_agent، spawn_agents، schedule_task، cancel_scheduled، run_tests (مشغّل اختبارات MCP IDE).

بالإضافة إلى _SENSITIVE_READS = ["sqlite_query", "file_search"] (الأسطر 34-37) — مسموحة لكن مُسجَّلة.

5.3 البوابة C / MCP — classify_mcp_tool()

Section titled “5.3 البوابة C / MCP — classify_mcp_tool()”

mcp/manager.py:71-88 — مطابقة ديناميكية لنمط الاسم (أدوات MCP تُكتشَف وقت التشغيل):

  • خطر (danger) إن وُجد أيٌّ من: write, delete, remove, exec, run, shell, bash, command, kill, terminate, install, deploy, upload, download, fetch, request, post, put, patch.
  • آمنة (safe) إن وُجد أيٌّ من: read, list, search, get, info, status, check, describe, query, count, exists, help.
  • غير معروفة (unknown) خلاف ذلك.

غير المعروفة تُعدّ خطرة افتراضياً. يتطلّب UnifiedToolExecutor.execute() (manager.py:725-727) موافقة لكلٍّ من danger وunknown. لا تُضعّف هذا أبداً.


6. التكامل التشفيري (ما وراء الموافقة البشرية في الحلقة)

Section titled “6. التكامل التشفيري (ما وراء الموافقة البشرية في الحلقة)”

6.1 توقيع HMAC للمهارات (Hub)

Section titled “6.1 توقيع HMAC للمهارات (Hub)”

موثَّق في المهارات وMCP والأدوات ← التوقيع التشفيري. الملخّص:

  • يكتب kazma hub sign قيمتي checksum (SHA-256) + signature (HMAC-SHA256 فوق checksum، بمفتاح KAZMA_SECRET) في skill_manifest.yaml.
  • يتحقّق SkillLoader._load_module_from_file من كليهما عبر hmac.compare_digest (وقت ثابت) ويرفض تحميل المهارات المُعبَث بها أو غير الموقّعة عند اشتراط ذلك.

delegation/security.py (DelegationSecurity، السطر 30):

  • التوقيع: Ed25519 (الأسطر 81-119).
  • التشفير: اتفاقية مفاتيح X25519 + AES-256-GCM (الأسطر 121-161).
  • تُوقَّع الطلبات عند الإرسال (protocol.py:153)، وتُتحرَّى عند الاستلام مع فشل مُغلق عند التوقيع المفقود/غير الصالح (:179-208).

هذا تفويض بين الوكلاء — لا علاقة له بـ MCP أو المهارات.

6.3 سرّ نقطة نهاية الموافقة البشرية في الحلقة

Section titled “6.3 سرّ نقطة نهاية الموافقة البشرية في الحلقة”

نقطة النهاية POST /api/approve/{thread_id} محمية بـ KAZMA_SECRET (ترويسة/كوكي، secrets.compare_digest). مسار التوليد التلقائي: يُحلِّل get_kazma_secret() (config_store.py:36-77) البيئة KAZMA_SECRETKAZMA_AUTH_DISABLED ← تخطّي pytest ← security.secret في قاعدة البيانات ← توليد تلقائي عبر secrets.token_hex(16).


7. سلوكيات الفشل المُغلق (الجرد)

Section titled “7. سلوكيات الفشل المُغلق (الجرد)”
المكوّنسلوك الفشل المُغلق
SafetyMiddleware.check_syncيحجب الأدوات الخطيرة عند غياب ناقل حقيقي + allow_headless_danger=False.
ToolRegistry.executeأي استثناء في فحص السلامة ← “blocked — SafetyMiddleware unavailable”.
classify_mcp_toolunknown ← يتطلّب موافقة.
SkillLoaderتوقيع مُعبَث به/مفقود ← SkillLoadError، دون تحميل.
Hub write APIغياب KAZMA_SECRET ← رفض جميع عمليات الكتابة.
استلام التفويضتوقيع مفقود/غير صالح ← رفض.
نقطة نهاية الموافقة البشرية في الحلقةموافقة بين مستخدمين ← 403؛ غياب KAZMA_SECRET ← راجع التحذير أدناه.

⚠ تحذير الإنتاج: إن كان KAZMA_SECRET غير مضبوط، فقد يُعيد get_kazma_secret() سلسلة فارغة "" وتصبح نقاط نهاية الموافقة غير مُصدَّقة. اضبط KAZMA_SECRET دائماً لأي نشر غير محلي. (لا يربط kazma serve على 0.0.0.0 إلا عند ضبط KAZMA_SECRET — وهذا عن قصد.)


يُعلن kazma-security.yaml عن الوضعية عبر scanning، وdisclosure، وbug_bounty، وhardening (8 فحوصات: secrets_in_logs، input_validation، rbac_enforcement، tls_required، dependency_audit، least_privilege، audit_trail، config_integrity). راجع الإعداد ← إعداد الأمان. يُعرّف kazma-permissions.yaml قوائم السماح/المنع لأدوات MCP المستندة إلى الأقسام (أقسام ALMuhalab) مع قواعد عبر الأقسام (require_explicit_approval، max_approval_duration_hours: 24، audit_all_access).

8.1 مُشغِّل التحصين (security/hardening.py)

Section titled “8.1 مُشغِّل التحصين (security/hardening.py)”

يُشغِّل SecurityHardeningRunner الفحوصات عند بدء التشغيل؛ المفاتيح هي run_on_startup: true، fail_on_critical: true، auto_fix: false. يُعيين كل وسم فحص yaml إلى دالة مُنفَّذة:

الفحص (وسم yaml)الدالة المُنفَّذةالخطورة
secrets_in_logscheck_no_hardcoded_secrets (مسح بالتعبيرات النمطية لـ api_key/secret/token/password/AWS/PRIVATE_KEY)critical
tls_requiredcheck_encrypted_communications (مسح TLS/SSL/HTTPS/mTLS)high
rbac_enforcementcheck_rbac_enforcementhigh
dependency_auditcheck_dependency_vulnerabilitiesDependencyScannercritical
(عزل MCP)check_mcp_sandboxinghigh
(بيانات مهارات manifest)check_skill_manifest_validitymedium
(تسجيل التدقيق)check_audit_logginghigh
(تصعيد الصلاحيات)check_permission_escalation (os.system، subprocess(...shell=True)، eval، exec، __import__، setattr(...__...))critical
least_privilege، config_integrity⚠ خارطة طريق (لا تنفيذ مخصّص)

يستطيع fix_issues(auto_fix) إنشاء ملف .env وتعديل .gitignore لفحص الأسرار.

8.2 مسح التبعيات (security/dependency_scanner.py)

Section titled “8.2 مسح التبعيات (security/dependency_scanner.py)”
الماسحالمصادرالذاكرة المؤقتة / التاريخ
DependencyScannerOSV (api.osv.dev)ذاكرة مؤقتة JSON في kazma-data/vuln_cache.json
DependabotStyleScannerOSV + GitHub Advisories + NVDSQLite في kazma-data/security_scan.db؛ يُلغي التكرار بـ (package, vuln_id)

كلاهما يحلّل requirements.txt وpyproject.toml. يُشغِّل DependabotStyleScanner إضافياً scan_skill_manifests() (يُعلِّمم إعدادات MCP المشبوهة: eval/exec/system في الأوامر، ومتغيرات البيئة TOKEN/SECRET/KEY، و--privileged، وnetwork: host؛ وأنماط التصعيد مثل sudo/chmod 777/setuid)، وcreate_github_issue() عبر gh CLI، وgenerate_advisory()، وcheck_for_updates().

kazma-security.yaml: فاصل scanning 24 ساعة، severity_threshold: medium، auto_create_issues: true.

8.3 تدفّق الإفصاح (security/disclosure.py)

Section titled “8.3 تدفّق الإفصاح (security/disclosure.py)”

يُ forced SQLite kazma-data/disclosure.db سلسلة الانتقال submitted → acknowledged → investigating → confirmed → patched → closed. يُولِّد publish_advisory() رمزاً نائباً لـ CVE + قالب markdown، لكن من حالتي patched/closed فقط. يُوقِّع encrypt_report() حمولة JSON باستخدام HMAC-SHA256 بمفتاح KAZMA_DISCLOSURE_KEY.

kazma-security.yaml: نافذة الاستجابة 48 ساعة، التقييم 7 أيام، عنوان URL لمفتاح PGP، القنوات المُشفَّرة (البريد + Signal)، مكافأة الثغرات 50–2000 دولار أمريكي.

تحقّق من التطبيق وقت التشغيل لفحوصات kazma-security.yaml مقابل مُشغِّل التحصين قبل الاعتماد عليها. يُعلن الملف عن السياسة؛ أكِّد أن المُشغِّل يُ forced كل فحص عند بدء التشغيل (hardening.run_on_startup: true، fail_on_critical: true).


  1. اضبط KAZMA_SECRET دائماً للنشر غير المحلي. ولّده عبر openssl rand -hex 32.
  2. شغّل خوادم MCP من نوع stdio في بيئة معزولة (sandbox). لا يتمتّع ناقل stdio بأي مصادقة ويرث بيئة العملية.
  3. فضّل MCP من نوع SSE مع مصادقة bearer لأي خادم MCP بعيد.
  4. وقّع جميع المهارات (kazma hub sign) وأبقِ KAZMA_SECRET متّسقاً عبر التحميل — وإلا يفشل التحقّق من التوقيع.
  5. أبقِ بوابات الموافقة البشرية في الحلقة (HITL) الثلاث مُفعّلة. لا تمرّر hitl_config=None في موقع بناء إنتاجي.
  6. لا تضبط allow_headless_danger=True في الإنتاج. إنه مخرج الاختبار/التطوير.
  7. اعمل بحساب المستخدم غير الجذري kazma في Docker (يقوم Dockerfile بذلك أصلاً).
  8. اربط على 127.0.0.1 ما لم يكن لديك وكيل عكسي + KAZMA_SECRET مُعدّ.

  • اسم الصنف: صنف سلامة السرب هو SafetyMiddleware، وليس SafetyGate/SafetyChecker. حُدِّث في كامل الوثيقة.
  • موقع نقطة نهاية الموافقة: POST /api/approve/{thread_id} موجود في routes_direct.py:454، وليس في app.py.
  • رقم سطر موقع بناء App.py: إعادة التجميع عند بدء التشغيل تقع في kazma-ui/kazma_ui/app.py:741-751، وليس ~966.
  • “مستويات الثقة (Trust tiers)” غير موجودة كميزة أمان — مجرّد علم منطقي certified وسلسلة إعداد trust: trusted غير مستخدَمة.
  • MCP من نوع stdio بلا مصادقة. موثَّق صراحةً؛ خطّط للعزل وفق ذلك.
  • ثلاث قوائم أدوات خطيرة متميّزة يجب أن تبقى متزامنة مع القصد. إن إضافة أداة خطرة جديدة إلى البوابة A (kazma.yaml) لا تضيفها إلى البوابة B (_EXTENDED_DANGER) أو البوابة C (المستندة إلى الأنماط).