Skip to content

Commit 2f99b65

Browse files
heliotropeheliotrope
authored andcommitted
fix: plot_function补上跟calculator一致的安全沙箱
之前plot_function直接把expressions丢给sp.sympify,没有走_check_expr_safe 白名单/黑名单校验,也没有超时保护——跟calculator用的是同一个sympify攻击面, 却是唯一没设防的一条。现在每条表达式先过_check_expr_safe,通过后在原有的 _CALC_POOL子进程池里求值(15秒超时),跟calculator复用同一套沙箱机制。 被拒绝/超时/解析失败的表达式会在图上标注对应文字,不影响其他正常表达式 继续画出来。已用真实的正常/恶意/混合三种输入验证过,68个既有测试全过。
1 parent 063d0a1 commit 2f99b65

1 file changed

Lines changed: 28 additions & 8 deletions

File tree

tools.py

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -805,6 +805,21 @@ def _run_step_decomposer(problem_type: str, problem: str) -> str:
805805
# 4. 可视化工具实现
806806
# ─────────────────────────────────────────
807807

808+
def _plot_eval_impl(expr_str: str, xmin: float, xmax: float) -> tuple:
809+
"""在子进程中解析并求值单条曲线——跟 calculator 走同一个安全沙箱/进程池/超时,
810+
避免 plot_function 成为绕开 _check_expr_safe 的另一个攻击面。"""
811+
import numpy as np
812+
import sympy as sp
813+
x_sym = sp.Symbol("x")
814+
sym_expr = sp.sympify(expr_str.replace("^", "**"))
815+
fn = sp.lambdify(x_sym, sym_expr, "numpy")
816+
x_arr = np.linspace(xmin, xmax, 2000)
817+
y_arr = np.array(fn(x_arr), dtype=complex)
818+
y_arr = np.real(y_arr)
819+
y_arr[np.abs(y_arr) > 1e5] = np.nan
820+
return x_arr.tolist(), y_arr.tolist(), sp.latex(sym_expr)
821+
822+
808823
def _run_plot_function(expressions, xmin=-10, xmax=10, ymin=None, ymax=None,
809824
title="", labels=None) -> str:
810825
try:
@@ -839,22 +854,27 @@ def _run_plot_function(expressions, xmin=-10, xmax=10, ymin=None, ymax=None,
839854
# 多条曲线用实线/虚线/点划线区分,颜色只用深色系
840855
line_styles = ["-", "--", "-.", ":"]
841856
colors = ["#1a1a1a", "#2255aa", "#cc2200", "#228833", "#aa44bb"]
842-
x_sym = sp.Symbol("x")
843-
x_arr = np.linspace(float(xmin), float(xmax), 2000)
857+
_xmin, _xmax = float(xmin), float(xmax)
844858

845859
for i, expr_str in enumerate(expressions):
860+
err = _check_expr_safe(expr_str)
861+
if err:
862+
ax.text(0.05, 0.95 - i * 0.07, f"表达式被拒绝: {expr_str}{err})",
863+
transform=ax.transAxes, fontsize=9, color="red")
864+
continue
846865
try:
847-
sym_expr = sp.sympify(expr_str.replace("^", "**"))
848-
fn = sp.lambdify(x_sym, sym_expr, "numpy")
849-
y_arr = np.array(fn(x_arr), dtype=complex)
850-
y_arr = np.real(y_arr)
851-
y_arr[np.abs(y_arr) > 1e5] = np.nan
866+
fut = _CALC_POOL.submit(_plot_eval_impl, expr_str, _xmin, _xmax)
867+
x_list, y_list, latex_expr = fut.result(timeout=_CALC_TIMEOUT)
868+
x_arr, y_arr = np.array(x_list), np.array(y_list)
852869
lbl = (labels[i] if labels and i < len(labels)
853-
else f"$y = {sp.latex(sym_expr)}$")
870+
else f"$y = {latex_expr}$")
854871
ax.plot(x_arr, y_arr,
855872
color=colors[i % len(colors)],
856873
linestyle=line_styles[i % len(line_styles)],
857874
lw=1.8, label=lbl)
875+
except _FutTimeout:
876+
ax.text(0.05, 0.95 - i * 0.07, f"计算超时: {expr_str}",
877+
transform=ax.transAxes, fontsize=9, color="red")
858878
except Exception as e:
859879
ax.text(0.05, 0.95 - i * 0.07, f"解析失败: {expr_str}",
860880
transform=ax.transAxes, fontsize=9, color="red")

0 commit comments

Comments
 (0)