prompt_renderer
Jinja2-based system prompt renderer with SSTI hardening.
Loads a .j2 template file once at startup and renders it on each call
with room-specific and tool-specific context variables. Uses a
SandboxedEnvironment and recursively sanitises
user-controllable values to prevent server-side template injection.
- class prompt_renderer.LoggingSandboxedEnvironment(*args, **kwargs)[source]
Bases:
SandboxedEnvironmentSandboxed Jinja2 environment that audits its creation and blocked attribute access.
Thin subclass of
jinja2.sandbox.SandboxedEnvironmentthat adds observability to prompt compilation: it announces every environment it constructs (tagged with the template name) and turns each silent sandbox refusal into a logged warning, so that a server-side template-injection probe leaves an auditable trail instead of failing quietly. The security policy itself is unchanged – only the logging is added on top.Instantiated by
PromptRenderer.__init__while loading the system-prompt template, bykg_agentic_extraction.pyfor the KG extraction prompt, and byknowledge_anchoring/template_loader.pyfor its shared sandbox; the SSTI test suite (tests/test_jinja_sandbox_ssti) also exercises it directly.- __init__(*args, **kwargs)[source]
Build a logging sandboxed Jinja2 environment and announce it.
Pops the bespoke
template_namekeyword (used only for log attribution) out of kwargs before delegating the rest of the positional and keyword arguments tojinja2.sandbox.SandboxedEnvironment.__init__(), so the parent never sees an unexpected argument. After the base environment is constructed it emits aninfolog line through the moduleloggerrecording which template this environment was created for, giving operators a breadcrumb whenever a new prompt-compilation sandbox is spun up.This constructor is invoked indirectly by
PromptRenderer.__init__, which instantiates aLoggingSandboxedEnvironmentwhile loading the system-prompt template; no other internal caller constructs it directly.- Parameters:
*args – Positional arguments forwarded verbatim to the
SandboxedEnvironmentbase class.**kwargs – Keyword arguments forwarded to the base class, except for the consumed
template_nameentry which defaults to"unknown"and is used purely for the log message.
- Return type:
None
- is_safe_attribute(obj, attr, value)[source]
Decide whether template code may read an attribute, logging denials.
Delegates the actual security judgement to
jinja2.sandbox.SandboxedEnvironment.is_safe_attribute(), which applies Jinja2’s sandbox policy (blocking dunder access and other attributes flagged unsafe). When the base class refuses access, this override emits awarningthrough the moduleloggernaming the attribute and the object type involved, turning otherwise-silent sandbox refusals into an auditable signal of a possible server-side template-injection attempt. The boolean verdict is then returned unchanged so the sandbox still enforces the same restriction.Jinja2’s sandbox machinery calls this automatically during template rendering for every attribute access; there is no direct internal caller, though the sibling sandbox in
knowledge_anchoring/template_loader.pymirrors this pattern.- Parameters:
obj – The object whose attribute is being accessed inside a template.
attr – The attribute name the template is attempting to read.
value – The resolved attribute value supplied by Jinja2.
- Returns:
Trueif the attribute access is permitted,Falseif the sandbox blocks it.- Return type:
- prompt_renderer.sanitize_context(value)[source]
Recursively strip Jinja2 metacharacters from user-controllable strings.
Replaces
{{,}},{%,%},{#,#}with Unicode full-width lookalikes so they cannot be interpreted as template syntax if an| tojsonfilter is ever omitted.Non-string leaves (ints, floats, bools,
None) pass through unchanged. Dicts and lists are walked recursively.
- class prompt_renderer.PromptRenderer(template_path, default_extras=None)[source]
Bases:
objectRender a Jinja2 system-prompt template with per-request context.
Uses
SandboxedEnvironmentto prevent template injection even if a caller accidentally passes unsanitised user data.- Parameters:
- __init__(template_path, default_extras=None)[source]
Load and compile the system-prompt template into a sandboxed renderer.
Resolves and validates the template path, then builds a
LoggingSandboxedEnvironment(with trailing-newline preservation and block trimming/stripping enabled) backed by aFileSystemLoaderrooted at the template’s parent directory, compiles the named template once, and stashes thedefault_extrasinjected into every later render. The compilation happens here so per-request renders stay cheap. Reads the filesystem to confirm the template exists and emits aninfolog line through the moduleloggeronce it is loaded.Constructed by callers across the codebase – the inference worker (
inference_main.py), message-context injection (message_processor/context_injections.py), subagent tooling (tools/subagent_tools.py), the SWORD overlay test suites, and the SSTI tests – typically once at startup withsystem_prompt.j2.- Parameters:
- Raises:
FileNotFoundError – If no file exists at
template_path.- Return type:
None
- render(context=None)[source]
Render the template with the supplied context.
All values in context are recursively sanitised to strip Jinja2 metacharacters before rendering.
The following keys are automatically injected if not already present:
current_date– today’s date inYYYY-MM-DDformat (UTC).
Keys from default_extras (set at init or later) are included but can be overridden by context.