用 Jinja2 3.1 渲染运营配置的提示词模板,模板里带了一个 range 循环和 dict.items,本地直接调 render 正常,放进 SanitizedEnvironment 后抛 SecurityError: access to attribute items of dict object is unsafe,换成 str.format 又丢了循环能力。
一开始怀疑是「range 在沙箱里需要显式加入全局白名单」,查了一圈,方向不对。
真正麻烦的地方在于,几个原因的现象几乎一样。沙箱环境禁用了 dict 的属性访问,只允许白名单里的方法。也可能是模板来自运营后台,属于不可信输入,不能用原生 Environment。
不容易想到的是,模板里混用了自定义函数,没有注册进 sandbox 的 globals。
先用原生环境确认模板本身没问题
python -c "from jinja2 import Template;print(Template('{% for k,v in d.items() %}{{k}}={{v}} {% endfor %}').render(d={'a':1}))"
接着换成沙箱环境复现拦截位置
python -c "from jinja2.sandbox import SandboxedEnvironment;e=SandboxedEnvironment();print(e.from_string('{{ d.items() }}').render(d={'a':1}))"
再往下把需要的能力显式加进白名单
python -c "from jinja2.sandbox import SandboxedEnvironment;e=SandboxedEnvironment();e.globals['range']=range;print(e.from_string('{% for i in range(3) %}{{i}}{% endfor %}').render())"
渲染失败时保留原文并告警
python -c "print({'on_error': 'keep_raw_template', 'alert': True})"
改完先别撤,确认这几项。模板能在沙箱里完成循环和取值,包含可疑属性访问的模板被拒绝且不报 500,运营后台能看到具体哪一行被拦。
提示词模板一旦可被运营编辑就成了不可信输入,沙箱的报错要翻译成人话给到编辑侧,否则大家会直接换回原生环境。线上能稳定复现的问题都不算难,难的是只在大流量下才出现的那种。
