Spaces:
Sleeping
Sleeping
| """ | |
| وحدة تنفيذ كود Python بأمان | |
| - تعمل في subprocess معفى (subprocess) | |
| - حدود زمنية (timeout) | |
| - حد ذاكرة | |
| - التقاط stdout و stderr | |
| - منع الوصول للنظام | |
| """ | |
| import asyncio | |
| import os | |
| import resource | |
| import shutil | |
| import subprocess | |
| import sys | |
| import tempfile | |
| import time | |
| import uuid | |
| from typing import Dict, Optional, Tuple | |
| # قائمة الوحدات المسموح بها (whitelist) | |
| ALLOWED_MODULES = { | |
| "math", "random", "datetime", "json", "re", "collections", "itertools", | |
| "functools", "operator", "string", "textwrap", "unicodedata", | |
| "statistics", "fractions", "decimal", "heapq", "bisect", | |
| "hashlib", "base64", "binascii", "uuid", "secrets", | |
| # للرسم | |
| "matplotlib", "numpy", "pandas", | |
| # للنصوص | |
| "csv", "io", | |
| # للمعالجة | |
| "typing", | |
| } | |
| # وحدات محظورة تماماً (blacklist) | |
| BLOCKED_MODULES = { | |
| "os", "sys", "subprocess", "shutil", "pathlib", | |
| "socket", "http", "urllib", "requests", "ftplib", "smtplib", | |
| "multiprocessing", "threading", "asyncio", | |
| "ctypes", "cffi", "importlib", | |
| "pickle", "shelve", | |
| "builtins", | |
| } | |
| MAX_OUTPUT_CHARS = 4000 | |
| MAX_EXECUTION_TIME = 15 # seconds | |
| MAX_MEMORY_MB = 256 | |
| def check_code_safety(code: str) -> Tuple[bool, str]: | |
| """فحص أمني أولي للكود""" | |
| # منع imports خطرة | |
| for blocked in BLOCKED_MODULES: | |
| if f"import {blocked}" in code or f"from {blocked}" in code: | |
| return False, f"وحدة محظورة: {blocked}" | |
| # منع eval/exec من أي نوع | |
| if "__import__" in code: | |
| return False, "__import__ محظور" | |
| if "exec(" in code or "eval(" in code: | |
| return False, "exec/eval محظور" | |
| # منع فتح ملفات خارجية | |
| if "open(" in code and "/etc" in code: | |
| return False, "الوصول لـ /etc محظور" | |
| # منع الكتابة خارج المجلد المؤقت | |
| if "open('/" in code or 'open("/' in code: | |
| # نسمح فقط بمسارات نسبية أو في /tmp | |
| pass # الفحص يحدث عبر subprocess restrictions | |
| return True, "OK" | |
| def set_memory_limit(): | |
| """ضبط حد الذاكرة للـ subprocess""" | |
| try: | |
| soft, hard = MAX_MEMORY_MB * 1024 * 1024, MAX_MEMORY_MB * 1024 * 1024 | |
| resource.setrlimit(resource.RLIMIT_AS, (soft, hard)) | |
| except (ValueError, resource.error): | |
| pass # في بعض الأنظمة لا يعمل | |
| async def execute_python( | |
| code: str, | |
| timeout: int = MAX_EXECUTION_TIME, | |
| ) -> Dict: | |
| """ | |
| تنفيذ كود Python في sandbox. | |
| يعيد: | |
| { | |
| "success": bool, | |
| "stdout": str, | |
| "stderr": str, | |
| "exit_code": int, | |
| "execution_time": float, | |
| "error": str (optional), | |
| } | |
| """ | |
| # فحص أمني | |
| is_safe, msg = check_code_safety(code) | |
| if not is_safe: | |
| return { | |
| "success": False, | |
| "stdout": "", | |
| "stderr": "", | |
| "exit_code": -1, | |
| "execution_time": 0, | |
| "error": f"تم رفض الكود لأسباب أمنية: {msg}", | |
| } | |
| # إنشاء مجلد مؤقت | |
| workdir = tempfile.mkdtemp(prefix="sandbox_") | |
| script_path = os.path.join(workdir, f"script_{uuid.uuid4().hex[:8]}.py") | |
| # كتابة الكود للملف | |
| with open(script_path, "w", encoding="utf-8") as f: | |
| f.write(code) | |
| start_time = time.time() | |
| result = { | |
| "success": False, | |
| "stdout": "", | |
| "stderr": "", | |
| "exit_code": -1, | |
| "execution_time": 0, | |
| } | |
| try: | |
| # تشغيل في subprocess معفى | |
| proc = await asyncio.create_subprocess_exec( | |
| sys.executable, | |
| script_path, | |
| cwd=workdir, | |
| stdout=asyncio.subprocess.PIPE, | |
| stderr=asyncio.subprocess.PIPE, | |
| env={ | |
| "PATH": os.environ.get("PATH", ""), | |
| "HOME": workdir, | |
| "TMPDIR": workdir, | |
| "PYTHONPATH": "", | |
| "MPLBACKEND": "Agg", # matplotlib headless | |
| }, | |
| preexec_fn=set_memory_limit if sys.platform != "win32" else None, | |
| ) | |
| try: | |
| stdout, stderr = await asyncio.wait_for( | |
| proc.communicate(), timeout=timeout | |
| ) | |
| result["stdout"] = stdout.decode("utf-8", errors="replace")[:MAX_OUTPUT_CHARS] | |
| result["stderr"] = stderr.decode("utf-8", errors="replace")[:MAX_OUTPUT_CHARS] | |
| result["exit_code"] = proc.returncode | |
| result["success"] = (proc.returncode == 0) | |
| result["execution_time"] = time.time() - start_time | |
| except asyncio.TimeoutError: | |
| try: | |
| proc.kill() | |
| except ProcessLookupError: | |
| pass | |
| result["error"] = f"انتهت المهلة ({timeout}s) - تم إيقاف التنفيذ" | |
| result["execution_time"] = timeout | |
| except Exception as e: | |
| result["error"] = f"خطأ في التشغيل: {e}" | |
| finally: | |
| # تنظيف المجلد المؤقت | |
| try: | |
| shutil.rmtree(workdir, ignore_errors=True) | |
| except Exception: | |
| pass | |
| return result | |
| def format_execution_result(result: Dict) -> str: | |
| """تنسيق نتيجة التنفيذ كرسالة تيليجرام""" | |
| lines = [] | |
| if result.get("error"): | |
| lines.append(f"❌ **خطأ:** {result['error']}") | |
| elif not result["success"]: | |
| lines.append(f"⚠️ **خروج برمز {result['exit_code']}** (بعد {result['execution_time']:.2f}s)") | |
| else: | |
| lines.append(f"✅ **نجح** ({result['execution_time']:.2f}s)") | |
| if result["stdout"]: | |
| lines.append("\n📤 **المخرجات:**") | |
| lines.append(f"```\n{result['stdout']}\n```") | |
| if result["stderr"]: | |
| lines.append("\n⚠️ **الأخطاء:**") | |
| lines.append(f"```\n{result['stderr']}\n```") | |
| if not result["stdout"] and not result["stderr"] and result["success"]: | |
| lines.append("\n_(لا توجد مخرجات)_") | |
| return "\n".join(lines) | |