Safeguard
Security Guides•

Is pandas eval or DataFrame.query safe with user input?

pandas eval and DataFrame.query can run arbitrary code if given user input, per the docs. How to build filters from user choices, not string expressions.

Safeguard Security Team
4 min read

No. The pandas documentation warns that pandas.eval can run arbitrary code, making you vulnerable to code injection if you pass user input to it, and gives the same warning for DataFrame.query. If users choose filters in a dashboard, translate their choices into masks in code instead of building expression strings.

What the docs say

On the pandas.eval page, the description of the expression argument begins with a warning: this function can run arbitrary code which can make you vulnerable to code injection if you pass user input to this function. The DataFrame.query page carries a similar line: this method can run arbitrary code which can make you vulnerable to code injection if you pass user input to this function.

The default engine tries numexpr and falls back to python. The docs describe the python engine as performing operations as if you had run them through eval at top level in Python, with built-in and user-defined Python functions allowed.

Why this shows up in analytics apps

Filter boxes are popular. A text field labelled "filter rows" is an easy feature to build with df.query(user_text). It is also a feature that hands a Python expression evaluator to anyone who can reach the page.

Notebook apps, internal dashboards and chat tools that turn plain language into pandas expressions are all places where this pattern appears. In the last case the text comes from a model, and a model can be steered by content in the data it reads, so treat that output as untrusted too.

Replace expression strings with structured choices

Instead of accepting free text, accept a small structure: a column, an operator and a value. Validate each part, then build the filter with ordinary pandas operations.

ops = {
    "eq": lambda s, v: s == v,
    "gt": lambda s, v: s > v,
    "lt": lambda s, v: s < v,
}

def apply_filter(df, col, op, value):
    if col not in df.columns or op not in ops:
        raise ValueError("invalid filter")
    return df[ops[op](df[col], value)]

Now the user can only select from columns you have and operators you defined. There is no path from their input to arbitrary code.

Where query is still fine

The same method is a good tool when the expression text is written by your own team and stored in code. df.query("A <= @local_var"), as in the pandas example, is a normal use. The warning is about the source of the string, not the method itself.

Review checklist

CheckPass condition
Source of every string passed to eval or queryWritten in your repository, not typed or generated at run time
Free text filter boxesReplaced with column, operator and value controls
Model generated expressionsNever executed directly; converted to the structure above or run in a sandbox
Process privilegesThe app holds no more credentials than it needs

Where Safeguard fits

Safeguard's code scanning can help find calls where untrusted input reaches evaluation functions, and you can start with a free scan.

Quick check

Is a regular expression check enough to make a query string safe?

It is fragile. Allow listing columns and operators in code is simpler to reason about than trying to filter dangerous syntax out of a string.

Does the numexpr engine remove the risk?

Do not rely on it. The docs warn about passing user input at all, and the default engine can fall back to python.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.