Heuristic:Guardrails ai Guardrails RAIL Argument Parsing Security
| Knowledge Sources | |
|---|---|
| Domains | Security, Debugging |
| Last Updated | 2026-02-14 12:00 GMT |
Overview
Security warning about the use of `literal_eval` for parsing RAIL XML validator arguments, with guidance on safer alternatives.
Description
The RAIL (Reliable AI Markup Language) XML format allows specifying validator arguments using Python expressions enclosed in curly braces (e.g., `{[1, 2, 3]}`). These expressions are parsed using Python's `ast.literal_eval()`, which the source code explicitly marks as "incredibly insecure" in a FIXME comment. While `literal_eval` is safer than `eval()` (it only allows literal structures, not arbitrary code execution), it still represents a potential attack surface when processing untrusted RAIL specifications.
Usage
Be aware of this heuristic when:
- Processing RAIL XML from untrusted sources (user-uploaded specs, external APIs).
- Building custom validators that accept complex arguments via RAIL syntax.
- Migrating from RAIL XML to Pydantic-based Guard definitions, which avoid this issue entirely.
The Insight (Rule of Thumb)
- Action: Prefer Pydantic-based Guard definitions (`Guard.for_pydantic()`) over RAIL XML when accepting specifications from untrusted sources. If RAIL XML must be used, validate and sanitize the input before processing.
- Value: Use `Guard.for_pydantic()` or `Guard().use()` instead of RAIL XML parsing for new code.
- Trade-off: RAIL XML provides a concise, declarative format that is easier to read for simple cases; Pydantic-based definitions are more verbose but type-safe and avoid the `literal_eval` concern.
Reasoning
The `literal_eval` function in `guardrails/utils/validator_utils.py:38` parses Python literal expressions from RAIL argument strings. While `literal_eval` restricts evaluation to safe literal structures (strings, numbers, tuples, lists, dicts, booleans, None), the FIXME comment indicates the developers consider this approach inadequate. The comment proposes two alternatives:
- Each Validator could accept a spread of argument strings and parse them independently.
- A Validator Manifest could describe argument types for safe parsing without eval.
Neither alternative has been implemented yet, making this a known technical debt item.
Evidence from source:
From `guardrails/utils/validator_utils.py:29-43`:
if t[0] == "{" and t[-1] == "}":
t = t[1:-1]
try:
# FIXME: This is incredibly insecure!
# We need a better way of escaping and parsing arguments from RAIL.
# Option 1: Each Validator could accept a spread of argument strings
# and be responsible for parsing them to the correct types.
# Option 2: We use something like the Validator Manifest that describes
# the arguments to parse the values from the string WITHOUT an eval.
t = literal_eval(t)
except (ValueError, SyntaxError, NameError) as e:
raise ValueError(
f"Python expression `{t}` is not valid, and raised an error: {e}."
)