动态创建脚本节点需通过可控上下文、安全求值与宿主能力绑定实现,核心是冻结快照的只读 ScriptHostContext 对象配合白名单 AST 求值器,禁用 eval、赋值、import 等危险操作,确保节点独立沙箱化。

在动态创建脚本节点时利用表达式注入特异性的运行时宿主原语,核心是**不直接拼接字符串执行,而是通过可控上下文 + 安全求值 + 宿主能力绑定**实现。关键不在“注入”,而在“受控暴露”。
明确宿主原语的边界与封装
运行时宿主原语(如当前线程ID、调用栈深度、配置上下文、事件触发源等)不能裸露为全局变量供任意表达式读取。应统一由宿主提供只读、类型明确、带访问控制的上下文对象:
- 定义一个轻量级上下文类(如 ScriptHostContext),其属性全部惰性计算或初始化时快照(如
.timestamp、.node_id、.trigger_event) - 禁止该对象支持
__dict__动态写入或setattr - 在表达式求值前,仅将该实例以受限名称(如 host)传入
eval()的globals或locals
用 ast.literal_eval 或自定义 AST 求值器替代 eval
避免使用原始 eval(),改用更安全的替代方案:
- 若表达式仅含字面量(数字、字符串、元组、列表、字典、布尔、None),优先用
ast.literal_eval()—— 它拒绝任何函数调用和属性访问,天然防注入 - 若需访问
host.xxx,可基于ast.NodeVisitor实现白名单属性访问器:只允许Attribute节点中value.id == 'host'且attr在预设列表(如['node_id', 'timestamp'])内 - 示例:解析
"host.node_id + '_v2'"可行;"host.os.system('rm -rf /')"直接抛异常
动态节点创建时绑定上下文快照
脚本节点不是在执行时才获取宿主状态,而是在创建瞬间完成上下文捕获与冻结:
- 调用
create_script_node(expression: str)时,立即生成host = ScriptHostContext(snapshot=True) - 将
expression和冻结后的host一起存入节点对象,而非保存原始字符串+延迟求值 - 后续每次运行该节点,都复用同一份快照(保证可重现),或按需生成新快照(如设置
fresh_context=True)
禁止跨节点隐式共享与副作用
每个动态脚本节点应视为独立沙箱:
- 不共享
locals字典,不允许多节点共用同一个host实例(除非显式传参) - 禁止表达式中出现赋值语句(
=)、import、def等语句型语法——AST 分析阶段即拦截 - 若需状态传递,必须显式通过参数(如
run(node, context=...))或返回值链式传递,不依赖隐式作用域污染

















