CVE-2026-41138
Flowise AirtableAgent RCE from unsanitized input executed via Pandas.
- CVSS 8.8
- CWE-94
- Input Validation and Sanitization
- Remote
Flowise is a drag & drop user interface to build a customized large language model flow. Prior to 3.1.0, there is a remote code execution vulnerability in AirtableAgent.ts caused by lack of input verification when using Pandas. The user’s input is directly applied to the question parameter within the prompt template and it is reflected to the Python code without any sanitization. This vulnerability is fixed in 3.1.0.
- CWE
- CWE-94
- CVSS base score
- 8.8
- Published
- 2026-04-23
- OWASP
- A03 Injection
- Orthogonal defect classification
- Checking
- Code defect classification
- Missing Check
- Category
- Input Validation and Sanitization
- Subcategory
- Command Injection
- Accessibility scope
- Remote
- Impact
- Arbitrary Code Execution
- Affected component
- Flowise
Solution
Upgrade to Flowise version 3.1.0 or later.
Vulnerable code sample
import pandas as pd
import io
import os
def vulnerable_flowise_code_execution(user_input: str):
# Simulates a backend where data is loaded into a pandas DataFrame.
# This might represent data fetched from an Airtable.
data = "column_a,column_b\n1,10\n2,20"
dataframe = pd.read_csv(io.StringIO(data))
# VULNERABILITY: The user input is directly formatted into a string
# that is intended to be a pandas query. The .query() method uses
# Python's eval() function, which executes the string as code.
# No sanitization is performed on the user_input.
generated_code_string = f'dataframe.query("{user_input}")'
# The generated string containing the user's malicious payload is executed.
print(f"Executing generated code: {generated_code_string}")
eval(generated_code_string)
# This is the malicious input provided by an attacker.
# It is crafted to:
# 1. Close the string and the query() call: `column_a > 0)`
# 2. Inject a new Python command using a semicolon: `;`
# 3. Execute an arbitrary OS command: `__import__('os').system('echo RCE_SUCCESSFUL')`
# 4. Start a new valid expression to prevent a syntax error from the trailing quote: `or (1==1`
malicious_payload = "column_a > 0); __import__('os').system('echo RCE_SUCCESSFUL'); (1==1"
# The application calls the vulnerable function with untrusted input.
vulnerable_flowise_code_execution(malicious_payload)Patched code sample
import pandas as pd
import operator
# Represents data that could be pulled from a source like Airtable
data = {'product': ['Apple', 'Banana', 'Carrot', 'Donut'], 'calories': [95, 105, 25, 452], 'protein': [0.5, 1.3, 0.9, 4.9]}
df = pd.DataFrame(data)
# An allow-list of safe operators to prevent arbitrary function execution.
# The vulnerable version might have used eval() or a similar method that
# could execute any code passed in the 'op_str'.
ALLOWED_OPERATORS = {
'>': operator.gt,
'<': operator.lt,
'==': operator.eq,
'!=': operator.ne,
'>=': operator.ge,
'<=': operator.le
}
def execute_safe_query(column: str, op_str: str, value: any):
"""
This function represents the fixed, secure way to query a DataFrame.
It validates all inputs before constructing the query, preventing RCE.
"""
# 1. FIX: Validate that the requested column exists in the DataFrame.
# This prevents accessing unintended attributes or methods.
if column not in df.columns:
raise ValueError(f"Error: Column '{column}' not found.")
# 2. FIX: Validate that the requested operator is in a predefined safe list.
# This is the core of the fix, preventing arbitrary code from being executed.
if op_str not in ALLOWED_OPERATORS:
raise ValueError(f"Error: Operator '{op_str}' is not allowed.")
# 3. FIX: Use the validated components to safely build the query.
# No part of the user input is ever executed as code. It is only used as data
# (column name, a key for a dictionary, and a value for comparison).
op_func = ALLOWED_OPERATORS[op_str]
try:
# Ensure the value can be compared with the column's data type
col_type = df[column].dtype
typed_value = col_type.type(value)
return df[op_func(df[column], typed_value)]
except (ValueError, TypeError):
raise TypeError(f"Error: Value '{value}' is not compatible with column '{column}'.")
# --- Example Usage Representing the Fixed Flow ---
# A legitimate query, parsed from a user's question.
# For example: "show products where calories are more than 100"
safe_column = 'calories'
safe_op = '>'
safe_value = 100
# This call succeeds because all inputs are valid.
safe_result = execute_safe_query(safe_column, safe_op, safe_value)
# A hypothetical malicious input. In the vulnerable version, this might have
# been injected directly into a code string for evaluation.
# For example: user_input = "calories > 0); __import__('os').system('ls');"
# The fixed approach parses the input into components first.
malicious_column_attempt = "calories"
malicious_op_attempt = "==" # A valid operator must be used to pass the op check
malicious_value_attempt = "0); __import__('os').system('echo pwned'); ("
try:
# This call will fail safely with a TypeError during value conversion,
# as the malicious string cannot be converted to an integer for comparison.
# The malicious code is never executed.
execute_safe_query(
malicious_column_attempt,
malicious_op_attempt,
malicious_value_attempt
)
except (ValueError, TypeError) as e:
# The error demonstrates the vulnerability was blocked.
passPayload
'))
import os
os.system('id')
#
Cite this entry
@misc{vaitp:cve202641138,
title = {{Flowise AirtableAgent RCE from unsanitized input executed via Pandas.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-41138},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-41138/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
