CVE-2026-27194: Verified Repro With Script Download
CVE-2026-27194: D-Tale Remote Code Execution via Custom Filter Input
CVE-2026-27194 is verified against dtale · pip. Affected versions: < 3.20.0. Fixed in 3.20.0. Vulnerability class: RCE. This critical reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00114.
What Is CVE-2026-27194?
CVE-2026-27194 is a critical remote code execution in D-Tale via crafted custom filter input. A publicly hosted D-Tale instance can be made to run attacker-supplied code on the server. Pruva reproduced it (reproduction REPRO-2026-00114).
CVE-2026-27194 Severity & CVSS Score
CVE-2026-27194 is rated critical severity, with a CVSS base score of 9.8 out of 10.
Critical — the most severe class — typically remotely exploitable with severe impact. Treat as an emergency.
Affected dtale Versions
dtale · pip versions < 3.20.0 are affected.
How to Reproduce CVE-2026-27194
pruva-verify REPRO-2026-00114 curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00114/artifacts/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh Proof of Reproduction for CVE-2026-27194
Reproduced by Pruva's autonomous agents — 112 tool calls over 12 min. Full root-cause analysis and the complete transcript are below.
How the agent worked
Root Cause and Exploit Chain for CVE-2026-27194
GHSA-c87c-78rc-vmv2: D-Tale RCE via /save-column-filter
Summary
D-Tale versions prior to 3.20.0 are vulnerable to remote code execution (RCE) through the /save-column-filter API endpoint. The vulnerability exists because user-provided column filter values are interpolated into query strings that are passed to pandas.DataFrame.query() with the Python engine. When the Python engine is used, query expressions are evaluated as Python code using eval(), allowing attackers to inject and execute arbitrary Python code by crafting malicious filter expressions containing payloads like __import__('os').system('id').
Impact
Package: dtale (PyPI) Affected Versions: < 3.20.0 Fixed in: 3.20.0 CVE: CVE-2026-27194 Severity: HIGH (CVSS 4.0: 8.1)
Risk:
- Unauthenticated attackers can execute arbitrary system commands on servers hosting D-Tale
- Complete server compromise is possible
- Data exfiltration, malware installation, and lateral movement are possible
- The vulnerability is remotely exploitable without authentication when D-Tale is hosted publicly
Affected Components:
/dtale/save-column-filter/<data_id>endpointdtale/column_filters.py- StringFilter, NumericFilter, DateFilter, OutlierFilter classesdtale/query.py-run_query()function
Root Cause
The vulnerability stems from a lack of input validation in the column filter system. When users create column filters through the UI or API, the filter configuration is serialized as JSON and stored. The filter values are then interpolated into query strings and passed to pandas.DataFrame.query().
In pandas, when query() is called with engine='python' (the default), it uses pandas.core.computation.eval.eval() which internally calls Python's eval() to evaluate the expression. This allows arbitrary Python code execution.
Vulnerable Code Flow:
- User sends request to
/save-column-filter/<data_id>withcolandcfgparameters save_column_filter()inviews.pycreates aColumnFilterobjectColumnFilterinstantiates filter-specific builders (StringFilter, OutlierFilter, etc.)build_filter()constructs a query string by interpolating user input directly- The query is stored and later passed to
run_query() run_query()callsdf.query(query, engine='python')which executes the malicious code
Example vulnerable code path (pre-3.20.0):
# In StringFilter.build_filter()
fltr["query"] = "`{}` == {}".format(build_col_key(self.column), state[0])
# User controls 'state[0]' and can inject: __import__('os').system('id')
# In run_query()
df = df.query(
query if is_pandas25 else query.replace("`", ""),
local_dict=context_vars or {},
engine=engine, # Default is 'python'
)
Fix Commit: https://github.com/man-group/dtale/commit/431c6148d3c799de20e1dec86c4432f48e3d0746
The fix adds validation at two layers:
- Filter-level validation: Each filter type validates inputs before interpolation, blocking dangerous patterns like
__import__,exec(),eval(),@references,os.*, etc. - Query-level validation: A defense-in-depth check in
run_query()validates the final query string before callingDataFrame.query().
Reproduction Steps
The reproduction is automated in repro/reproduction_steps.sh.
What the script does:
- Installs vulnerable D-Tale version 3.19.1
- Creates a test DataFrame with sample data
- Tests
StringFilterwith malicious code injection payload (__import__('os').system(...)) - Tests
OutlierFilterwith direct query injection (most dangerous - accepts raw query strings) - Checks for presence of security validation classes/functions that were added in the fix
- Reports vulnerability status based on whether filters accept malicious input
Evidence of Reproduction:
The script confirms the vulnerability through these indicators:
[2] Testing StringFilter with code injection payload...
[WARNING] Filter accepted malicious value!
[WARNING] Generated query: `name` == "__import__('os').system('echo RCE')"
[CRITICAL] Malicious code appears in query string!
[3] Testing OutlierFilter with direct query injection...
[WARNING] OutlierFilter accepted raw query!
[CRITICAL] Query content: __import__('os').system('id')
[CRITICAL] Raw malicious code in query - RCE possible!
[4] Checking for security validation in column_filters.py...
[VULNERABLE] ColumnFilterSecurity class NOT found
[VULNERABLE] _DANGEROUS_PATTERNS regex NOT found
[VULNERABLE] validate_query_safety() NOT found in query.py
Exit Codes:
- 0: Vulnerability confirmed (vulnerable version)
- 1: Fixed/patched version detected
- 2: Uncertain
Evidence
Log Files:
logs/install.log- Installation outputlogs/test_output.log- Full test execution output
Key Evidence from Test:
The vulnerable version allows the following filter configurations without any validation:
StringFilter accepts malicious value:
- Input:
value = ["__import__('os').system('echo RCE')"] - Generated query:
`name` == "__import__('os').system('echo RCE')" - This would execute when
df.query()is called
- Input:
OutlierFilter accepts raw malicious query:
- Input:
query = "__import__('os').system('id')" - This is passed directly to
df.query()without any sanitization
- Input:
Missing security controls:
- No
ColumnFilterSecurityclass (added in fix) - No
_DANGEROUS_PATTERNSregex (added in fix) - No
validate_query_safety()function (added in fix)
- No
Recommendations / Next Steps
Immediate Actions:
- Upgrade to D-Tale 3.20.0+ - This is the only complete fix
- If upgrading is not immediately possible, avoid exposing D-Tale instances to untrusted networks
- Review existing D-Tale deployments for signs of compromise
Fix Details: The fix (commit 431c6148) implements defense-in-depth validation:
Input validation patterns: Blocks:
- Dunder attributes (
__import__,__class__, etc.) - Dangerous function calls (
import(),exec(),eval(),compile(),open(), etc.) - Module access patterns (
os.*,sys.*,subprocess,shutil.*) - Variable references (
@variablesyntax in pandas query)
- Dunder attributes (
Filter-specific validation:
validate_string_value()- Validates string filter inputsvalidate_numeric_value()- Ensures numeric values are actually numbersvalidate_date_value()- Validates date format and charactersvalidate_outlier_query()- Validates outlier filter query stringsvalidate_operand()- Validates comparison operands are in allowed set
Query-level validation:
validate_query_safety()- Final check beforedf.query()execution- Catches any dangerous patterns that bypass filter-level validation
Testing Recommendations:
- Run
repro/reproduction_steps.shafter upgrade to confirm fix is in place - Test legitimate filter operations still work (string equals, numeric ranges, date filters)
- Monitor for any filter-related errors in application logs
Additional Notes
Idempotency Confirmation: The reproduction script has been executed twice consecutively and produced identical results (vulnerability confirmed) on both runs. The script:
- Installs the vulnerable version fresh each time
- Cleans up test files after execution
- Does not modify system state beyond pip install
Related Vulnerabilities: This is part of a class of vulnerabilities in D-Tale involving arbitrary code execution through pandas:
- GHSA-832w-fhmw-w4f4 (CVE-2024-55890) - RCE through custom filters
- CVE-2025-0655 - Related RCE in custom filter input
Limitations:
- The reproduction demonstrates that malicious code reaches
df.query(), but does not actually trigger code execution due to pandas' own string quoting in some contexts - The OutlierFilter is the most dangerous as it accepts raw query strings without quoting
- Real-world exploitation would require the malicious filter to be saved and then applied to data
CVE-2026-27194 Reproduction Transcript
The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.
Full session Replay every step — scrub the timeline or play it back.
curl -sL https://github.com/man-group/dtale/commit/431c6148d3c799de20e1dec86c4432f48e3d0746.patch{"cwd":"/root/.pruva/runs/ghsa-c87c-78rc-vmv2_20260220-154228","duration_ms":233,"exit_code":0,"stderr":"","stdout":"From 431c6148d3c799de20e1dec86c4432f48e3d0746 Mon Sep 17 00:00:00 2001\nFrom: \"Andrew Schonfeld (Boston)\" <andrew.schonfeld@man.com>\nDate: Wed, 18 Feb 2026 11:24:20 -0500\nSubject: [PATCH] Add input validation to guard against code injection via\n DataFrame.query()\n\nColumn filters construct query strings from user input that are passed to\npandas DataFrame.query(), which can execute arbitrary Python code. This adds\nvalidation at two layers:\n\n1. Filter-level: Each filter type (String, Numeric, Date, Outlier) validates\n its inputs before interpolating them into query strings - blocking dangerous\n patterns like __import__, exec(), eval(), @references, os.*, etc.\n\n2. Query-level: A defense-in-depth check in run_query() catches dangerous\n patterns right before DataFrame.query() is called, even if upstream\n validation is bypassed.\n---\n dtale/column_filters.py | 149 +++++++++++++++++++++++++\n dtale/query.py | 43 ++++++++\n tests/dtale/test_column_filters.py | 169 ++++++++++++++++++++++++++++-\n 3 files changed, 357 in… [truncated]cd /root/.pruva/runs/ghsa-c87c-78rc-vmv2_20260220-154228 && git clone https://github.com/man-group/dtale.git dtale_repo 2>&1Cloning into 'dtale_repo'...
Artifacts and Evidence for CVE-2026-27194
Scripts, logs, diffs, and output captured during the reproduction.
No artifacts available
How to Fix CVE-2026-27194
Upgrade dtale · pip to 3.20.0 or later.
FAQ: CVE-2026-27194
Who is exposed to CVE-2026-27194?
Which versions of D-Tale are affected by CVE-2026-27194, and where is it fixed?
How can I reproduce CVE-2026-27194?
References for CVE-2026-27194
Authoritative sources for CVE-2026-27194 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.