zai-telegram-bot / code_executor.py
Ejdjdososs's picture
Upload code_executor.py with huggingface_hub
591f8bb verified
Raw
History Blame Contribute Delete
6.18 kB
"""
وحدة تنفيذ كود 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)