-
Notifications
You must be signed in to change notification settings - Fork 0
Fix security vulnerabilities and bugs: replace eval() with AST evaluator, add security headers, fix duplicate message save #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| # Python | ||
| __pycache__/ | ||
| *.py[cod] | ||
| *.pyo | ||
| *.pyd | ||
| .Python | ||
| *.egg-info/ | ||
| dist/ | ||
| build/ | ||
| .venv/ | ||
| venv/ | ||
| env/ | ||
|
|
||
| # Node / WhatsApp bot | ||
| node_modules/ | ||
| .wwebjs_auth/ | ||
| .wwebjs_cache/ | ||
| npm-debug.log* | ||
|
|
||
| # Database & uploads | ||
| chat_history.db | ||
| chat_history.db-wal | ||
| chat_history.db-shm | ||
| uploads/ | ||
| Sandbox/ | ||
|
|
||
| # OS / Editor | ||
| .DS_Store | ||
| Thumbs.db | ||
| *.swp | ||
| *.swo | ||
| .idea/ | ||
| .vscode/ |
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -35,13 +35,15 @@ | |||||||||
| # IMPORTS | ||||||||||
| # ═══════════════════════════════════════════════════════════════ | ||||||||||
| import argparse | ||||||||||
| import ast as _ast | ||||||||||
| import atexit | ||||||||||
| import base64 as b64mod | ||||||||||
| import glob | ||||||||||
| import hashlib | ||||||||||
| import io | ||||||||||
| import json | ||||||||||
| import logging | ||||||||||
| import operator as _operator | ||||||||||
| import os | ||||||||||
| import queue | ||||||||||
| import signal | ||||||||||
|
|
@@ -129,6 +131,88 @@ | |||||||||
| if g.strip() | ||||||||||
| ] | ||||||||||
|
|
||||||||||
| # ═══════════════════════════════════════════════════════════════ | ||||||||||
| # GÜVENLİ HESAP MAKİNESİ — AST Tabanlı (eval() kullanılmaz) | ||||||||||
| # ═══════════════════════════════════════════════════════════════ | ||||||||||
|
|
||||||||||
| def _safe_calc_eval(expr_str: str): | ||||||||||
| """ | ||||||||||
| AST tabanlı güvenli matematik değerlendirici. | ||||||||||
| eval() kullanmaz; yalnızca sayılar, aritmetik ve math | ||||||||||
| fonksiyonlarına izin verir. | ||||||||||
| """ | ||||||||||
| import math | ||||||||||
|
|
||||||||||
| _ALLOWED_NODES = ( | ||||||||||
| _ast.Expression, _ast.Constant, | ||||||||||
| _ast.BinOp, _ast.UnaryOp, _ast.Call, | ||||||||||
| _ast.Add, _ast.Sub, _ast.Mult, _ast.Div, | ||||||||||
| _ast.Pow, _ast.Mod, _ast.FloorDiv, | ||||||||||
| _ast.USub, _ast.UAdd, | ||||||||||
| _ast.Name, _ast.Load, | ||||||||||
| ) | ||||||||||
|
|
||||||||||
| _OPS = { | ||||||||||
| _ast.Add: _operator.add, | ||||||||||
| _ast.Sub: _operator.sub, | ||||||||||
| _ast.Mult: _operator.mul, | ||||||||||
| _ast.Div: _operator.truediv, | ||||||||||
| _ast.Pow: _operator.pow, | ||||||||||
| _ast.Mod: _operator.mod, | ||||||||||
| _ast.FloorDiv: _operator.floordiv, | ||||||||||
| _ast.USub: _operator.neg, | ||||||||||
| _ast.UAdd: _operator.pos, | ||||||||||
| } | ||||||||||
|
|
||||||||||
| _NAMES: Dict[str, Any] = { | ||||||||||
| k: v for k, v in math.__dict__.items() if not k.startswith("__") | ||||||||||
| } | ||||||||||
| for _fn in (abs, round, min, max, sum, int, float): | ||||||||||
| _NAMES[_fn.__name__] = _fn | ||||||||||
|
|
||||||||||
| def _eval(node): | ||||||||||
| if not isinstance(node, _ALLOWED_NODES): | ||||||||||
| raise ValueError(f"İzin verilmeyen ifade türü: {type(node).__name__}") | ||||||||||
| if isinstance(node, _ast.Expression): | ||||||||||
| return _eval(node.body) | ||||||||||
| if isinstance(node, _ast.Constant): | ||||||||||
| if not isinstance(node.value, (int, float, complex)): | ||||||||||
| raise ValueError("Sadece sayısal sabitler desteklenir") | ||||||||||
| return node.value | ||||||||||
| if isinstance(node, _ast.BinOp): | ||||||||||
| op = _OPS.get(type(node.op)) | ||||||||||
| if op is None: | ||||||||||
| raise ValueError(f"İzin verilmeyen operatör: {type(node.op).__name__}") | ||||||||||
| return op(_eval(node.left), _eval(node.right)) | ||||||||||
| if isinstance(node, _ast.UnaryOp): | ||||||||||
| op = _OPS.get(type(node.op)) | ||||||||||
| if op is None: | ||||||||||
| raise ValueError(f"İzin verilmeyen tekli operatör: {type(node.op).__name__}") | ||||||||||
| return op(_eval(node.operand)) | ||||||||||
| if isinstance(node, _ast.Call): | ||||||||||
| if not isinstance(node.func, _ast.Name): | ||||||||||
| raise ValueError("Yalnızca basit fonksiyon çağrıları izinli") | ||||||||||
| func = _NAMES.get(node.func.id) | ||||||||||
| if func is None: | ||||||||||
| raise ValueError(f"İzin verilmeyen fonksiyon: {node.func.id}") | ||||||||||
| args = [_eval(a) for a in node.args] | ||||||||||
| if node.keywords: | ||||||||||
| raise ValueError("Anahtar kelime argümanları desteklenmiyor") | ||||||||||
| return func(*args) | ||||||||||
| if isinstance(node, _ast.Name): | ||||||||||
| val = _NAMES.get(node.id) | ||||||||||
| if val is None: | ||||||||||
| raise ValueError(f"İzin verilmeyen değişken: {node.id}") | ||||||||||
| return val | ||||||||||
| raise ValueError(f"Desteklenmeyen düğüm: {type(node).__name__}") | ||||||||||
|
|
||||||||||
| try: | ||||||||||
| tree = _ast.parse(expr_str.strip(), mode='eval') | ||||||||||
| except SyntaxError as exc: | ||||||||||
| raise ValueError(f"Sözdizimi hatası: {exc}") from exc | ||||||||||
| return _eval(tree) | ||||||||||
|
|
||||||||||
|
|
||||||||||
| # ═══════════════════════════════════════════════════════════════ | ||||||||||
| # KATMAN 1 — LRU RAM Cache (thread-safe, TTL, metriklere sahip) | ||||||||||
| # ═══════════════════════════════════════════════════════════════ | ||||||||||
|
|
@@ -251,7 +335,11 @@ def _decode(data: bytes) -> str: | |||||||||
| return payload.decode("utf-8") | ||||||||||
| except Exception as e: | ||||||||||
| log.warning(f"_decode hatası (len={len(data)}): {e}") | ||||||||||
| return data.decode("utf-8", errors="replace") | ||||||||||
| # Flag baytını atla; sadece payload kısmını çöz | ||||||||||
| try: | ||||||||||
| return data[1:].decode("utf-8", errors="replace") if len(data) > 1 else "" | ||||||||||
| except Exception: | ||||||||||
| return "" | ||||||||||
|
|
||||||||||
|
|
||||||||||
| def _compress_ratio(data: bytes) -> float: | ||||||||||
|
|
@@ -883,7 +971,9 @@ def _make_thumbnail(self, raw: bytes, mime: str, | |||||||||
| from PIL import Image | ||||||||||
| import io | ||||||||||
| img = Image.open(io.BytesIO(raw)) | ||||||||||
| img.thumbnail(size, Image.LANCZOS) | ||||||||||
| # Pillow 10+ uses Image.Resampling.LANCZOS; older versions use Image.LANCZOS | ||||||||||
| _lanczos = getattr(getattr(Image, 'Resampling', None), 'LANCZOS', None) or Image.LANCZOS | ||||||||||
| img.thumbnail(size, _lanczos) | ||||||||||
| buf = io.BytesIO() | ||||||||||
| img.save(buf, format="JPEG", quality=60, optimize=True) | ||||||||||
| return buf.getvalue() | ||||||||||
|
|
@@ -1416,9 +1506,7 @@ def _check_api_key(): | |||||||||
| if not PANEL_API_KEY: | ||||||||||
| return # auth devre dışı | ||||||||||
| if not request.path.startswith('/api/'): | ||||||||||
| return # HTML paneli korumasız bırak | ||||||||||
| if request.path == '/health': | ||||||||||
| return # sağlık kontrolü korumasız | ||||||||||
| return # HTML paneli ve /health korumasız bırak | ||||||||||
| key = request.headers.get('X-API-Key') | ||||||||||
| if not key: | ||||||||||
| key = request.args.get('api_key') | ||||||||||
|
|
@@ -1427,6 +1515,15 @@ def _check_api_key(): | |||||||||
| if key != PANEL_API_KEY: | ||||||||||
| return jsonify({"ok": False, "error": "Yetkisiz erişim"}), 401 | ||||||||||
|
|
||||||||||
|
|
||||||||||
| @app.after_request | ||||||||||
| def _add_security_headers(response): | ||||||||||
| """Her yanıta temel güvenlik başlıkları ekle.""" | ||||||||||
| response.headers.setdefault('X-Content-Type-Options', 'nosniff') | ||||||||||
| response.headers.setdefault('X-Frame-Options', 'SAMEORIGIN') | ||||||||||
| response.headers.setdefault('Referrer-Policy', 'strict-origin-when-cross-origin') | ||||||||||
| return response | ||||||||||
|
|
||||||||||
| # llama-server durumu | ||||||||||
| _llm_proc = None | ||||||||||
| _llm_queue: queue.Queue = queue.Queue(maxsize=1000) | ||||||||||
|
|
@@ -3088,20 +3185,11 @@ def execute_tool(tool_str): | |||||||||
| return {"text": "⚠ Python Sandbox uzmanı deaktif. Ayarlardan aktif edin."} | ||||||||||
|
|
||||||||||
| if name == "calculator": | ||||||||||
| import math | ||||||||||
| allowed = {k: v for k, v in math.__dict__.items() if not k.startswith("__")} | ||||||||||
| allowed["abs"] = abs | ||||||||||
| allowed["round"] = round | ||||||||||
| allowed["min"] = min | ||||||||||
| allowed["max"] = max | ||||||||||
| allowed["sum"] = sum | ||||||||||
| allowed["int"] = int | ||||||||||
| allowed["float"] = float | ||||||||||
| expr_str = t.get("expr", "") | ||||||||||
| for blocked in ("__", "import", "exec", "eval", "open", "os.", "sys.", "getattr", "setattr"): | ||||||||||
| if blocked in expr_str: | ||||||||||
| return f"❌ Güvensiz ifade: '{blocked}' kullanılamaz" | ||||||||||
| res = eval(expr_str, {"__builtins__": {}}, allowed) | ||||||||||
| try: | ||||||||||
| res = _safe_calc_eval(expr_str) | ||||||||||
| except ValueError as ve: | ||||||||||
| return {"text": f"❌ Hesaplama hatası: {ve}"} | ||||||||||
|
Comment on lines
+3191
to
+3192
|
||||||||||
| except ValueError as ve: | |
| return {"text": f"❌ Hesaplama hatası: {ve}"} | |
| except Exception as e: | |
| return {"text": f"❌ Hesaplama hatası: {e}"} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_safe_calc_evalwhitelists allmathmodule symbols (including expensive functions likefactorial,comb, etc.) and allows**on unbounded integers. Inputs likefactorial(100000)or10**10000000can cause CPU/memory exhaustion (DoS) even though code execution is blocked. Consider restricting the allowed function set to a small curated list and enforcing limits (e.g., max integer bit-length / max exponent / max factorial argument / max AST nodes).