For AI agents: a documentation index is available at the root level at /llms.txt and /llms-full.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.
  • Getting Started
    • Welcome
    • Contributing
  • Concepts
    • Columns
    • Seed Datasets
    • Agent Rollout Ingestion
    • Custom Columns
    • Validators
    • Processors
    • Person Sampling
    • Traces
    • Architecture & Performance
    • Deployment Options
    • Security
  • Tutorials
    • Overview
    • The Basics
    • Structured Outputs, Jinja Expressions, and Conditional Generation
    • Seeding with an External Dataset
    • Providing Images as Context
    • Generating Images
    • Image-to-Image Editing
  • Recipes
    • Recipe Cards
  • Plugins
    • Overview
    • Example Plugin
    • FileSystemSeedReader Plugins
    • Discover
  • Code Reference
    • Overview
  • Dev Notes
    • Overview
    • Prompt Sensitivity
    • Retriever SDG Toolkit
    • Have It Your Way
    • VLM Long Document Understanding
    • Push Datasets to Hugging Face Hub
    • Text-to-SQL for Nemotron Super
    • Async All the Way Down
    • Owning the Model Stack
NVIDIANVIDIA
Developer-friendly docs for your API
Privacy Policy | Manage My Privacy | Do Not Sell or Share My Data | Terms of Service | Accessibility | Corporate Policies | Product Security | Contact

Copyright © 2026, NVIDIA Corporation.

LogoLogoNeMo Data Designer
On this page
  • Jinja Rendering Modes
  • Compatibility Matrix
  • What SECURE Adds on Top of Standard Jinja Sandbox
  • Record Sanitization Before Render
  • Filter Allowlist
  • Template Features Removed
  • Loop Restrictions
  • AST Complexity Limits
  • self References Blocked
  • Rendered Output Guards
  • Sanitized User-Facing Errors
  • Why This Matters in Multi-User Deployments
  • When To Use NATIVE
  • Related Reading
Concepts

Security

||View as Markdown|
Previous

Deployment Options: Library vs. Microservice

Next

Tutorials Overview

Data Designer can run in two very different trust models:

  • Trusted / monolithic: The same user or team writes the config and runs the engine.
  • Untrusted / shared execution: One user submits a config and a different process, service, or team executes it.

That distinction matters for features that evaluate user-supplied configuration at runtime, such as Jinja template rendering. In a trusted local workflow, broader template flexibility may be acceptable. In a shared-service deployment, user-supplied Jinja becomes part of the engine’s remote code execution surface. A template sandbox escape would execute inside the process running Data Designer.

See Deployment Options: Library vs. Microservice for the architectures where that trust boundary changes.

Jinja Rendering Modes

Data Designer exposes the renderer choice through RunConfig:

1import data_designer.config as dd
2
3run_config = dd.RunConfig(
4 jinja_rendering_engine=dd.JinjaRenderingEngine.SECURE,
5)

SECURE is the default. Opt into NATIVE only when you are comfortable treating the config author and the engine operator as the same trust domain.

ModeWhat it usesBest fit
SECUREData Designer’s hardened renderer built on top of Jinja2’s sandboxShared services, microservices, internal platforms, or any deployment where config submission is separated from execution
NATIVEJinja2’s built-in sandbox with Data Designer’s variable whitelistLocal library usage and other trusted, monolithic workflows that want broader Jinja behavior

Treat untrusted Jinja as a security boundary If many users can submit configs to one engine, or if configs are accepted over an API and executed elsewhere, keep JinjaRenderingEngine.SECURE. In that model, Jinja templates are no longer just prompt-formatting helpers. They are untrusted user programs being evaluated by your engine.

Compatibility Matrix

NATIVE is not an unrestricted Python template engine. The matrix below shows what each mode permits, restricts, or adds on top of Jinja2’s standard sandbox behavior.

CapabilityNATIVESECURE
Jinja2 ImmutableSandboxedEnvironment baselineYesYes
References to explicitly provided dataset variables onlyYesYes
Standard Jinja built-in filter setYesSubset only
Data Designer jsonpath filterYesYes
import, macro, set, extends, block supportYesNo
Nested or recursive for loopsYesNo
Unbounded AST complexityYesNo
Template context sanitized to JSON-compatible types before renderNoYes
Empty, oversized, or built-in-like rendered output is permittedYesNo

What SECURE Adds on Top of Standard Jinja Sandbox

The SECURE renderer uses a hardened environment implemented in the renderer source file on GitHub. Compared with the standard Jinja sandbox, it adds several additional controls.

Record Sanitization Before Render

Before rendering, SECURE forces template context through a JSON-compatible serialization step. That means remote templates operate on plain data, not arbitrary Python objects.

1# Intended shape for remote template context
2record = {
3 "user": {
4 "name": "alice",
5 "roles": ["admin", "reviewer"],
6 }
7}
1# Not the kind of server-side object SECURE wants to expose directly
2record = {
3 "user": SomePythonObject(...),
4}

In a remote execution setting, exposing rich Python objects increases the risk of attribute- and method-based sandbox escapes. Jinja’s sandbox security considerations note that the sandbox is not a complete security boundary, and past escapes have included str.format (CVE-2016-10745), str.format_map (CVE-2019-10906), indirect str.format references (CVE-2024-56326), and |attr-based access to format (CVE-2025-27516); PortSwigger’s server-side template injection research covers the broader object-traversal pattern.

Filter Allowlist

SECURE keeps only a small approved subset of Jinja filters plus the Data Designer jsonpath filter. If a filter is not on that allowlist, the template is rejected. Common excluded filters are:

Disallowed filtersWhy they are excluded in SECURE
attr, xmlattrThese add dynamic attribute lookup or attribute-name construction, which widens the object-traversal surface in untrusted templates.
map, select, reject, selectattr, rejectattr, groupby, batch, slice, sumThese make templates behave more like a data-processing language and can multiply compute across large inputs.
join, format, indent, wordwrap, center, filesizeformatThese expand presentation and composition logic inside the template. SECURE keeps formatting logic narrow so templates stay close to interpolation.
default, d, dictsort, count, wordcount, pprint, tojsonThese encourage fallback logic, secondary data shaping, or debug-style output inside the template rather than in the engine or config layer.
safe, striptags, urlizeThese are primarily HTML-oriented output transforms and are unnecessary for server-side dataset rendering.

Some omitted convenience filters, such as the e alias for escape, are excluded because SECURE uses a small explicit allowlist. The current implementation does not assign each omitted filter its own separate security rationale.

Use NATIVE when full Jinja filter compatibility matters more than the additional restrictions used for untrusted template execution.

Template Features Removed

SECURE rejects import, macro, set, extends, and block.

1{% macro render_name(name) %}{{ name }}{% endmacro %}
2{{ render_name(customer_name) }}
1{% set temp = user_id %}
2{{ temp }}

Those features are useful in trusted authoring environments, but they also make user templates more expressive and stateful. In a remote execution model, SECURE intentionally narrows the language so templates stay closer to data interpolation than to a reusable programming layer.

Loop Restrictions

SECURE rejects recursive loops and nested for loops.

1{% for row in rows %}
2 {% for item in row %}
3 {{ item }}
4 {% endfor %}
5{% endfor %}

Nested and recursive loops are especially risky in shared execution because they can amplify compute cost and output size in ways that are hard to reason about from the outside.

AST Complexity Limits

SECURE statically analyzes the parsed Jinja AST and rejects templates that exceed the current limits of 600 nodes or depth 10.

1{% if a %}
2 {% if b %}
3 {% if c %}
4 {{ value }}
5 {% endif %}
6 {% endif %}
7{% endif %}

This is not about any one feature being unsafe by itself. It is about limiting how much control flow and composition untrusted templates can pack into a single server-side render operation, which helps prevent compute bombs in shared execution.

self References Blocked

SECURE rejects references to self.

1{{ self }}

The point is to avoid exposing template internals back to the submitter. In a remote setting, even accidental access to those internals is unnecessary surface area.

Rendered Output Guards

SECURE validates rendered output after template execution. It rejects empty output, very large output, and strings that look like Python built-in or function representations.

1{{ "" }}
<built-in method ...>
<function ...>

These checks matter because not all bad outcomes come from parse-time behavior. Some templates are syntactically valid but still produce output that is clearly broken, oversized, or revealing internal implementation details.

Sanitized User-Facing Errors

At the engine boundary, SECURE normalizes most template failures into a generic invalid-template message.

User provided prompt generation template is invalid.

That matters in remote execution because exception details can leak information about server-side implementation, supported objects, or internal execution paths that untrusted users do not need to see.

These controls exist because the standard sandbox is a good baseline, but shared-service deployments need a narrower and more defensive execution model.

Why This Matters in Multi-User Deployments

The security posture changes as soon as config submission and execution are separated.

Examples:

  • A centralized Data Designer service accepts configs from many users.
  • An internal platform lets users upload or edit configs that are executed by a background worker.
  • A REST API accepts Jinja-containing configs and runs them on server-side infrastructure.

In those environments, templates are no longer just local convenience syntax. They are untrusted input being evaluated by infrastructure the submitter does not control. In practice, that makes Jinja rendering a remote code execution concern, which is why SECURE exists and why it remains the default.

If you are deciding between local library usage and a shared service model, read Deployment Options: Library vs. Microservice. The library patterns are often still “trusted” deployments. The shared microservice pattern is not.

When To Use NATIVE

Use NATIVE when all of the following are true:

  • The person submitting the config is also the person running the engine, or they are in the same trusted operational boundary.
  • You want broader standard Jinja behavior than SECURE allows.
  • You understand that this is a flexibility tradeoff, not the safer default.

For example, this is often reasonable in a notebook, local script, or other single-user library workflow.

Related Reading

  • Deployment Options: Library vs. Microservice
  • Run Config Reference