GitHub Security Advisory: GHSA-f228-chmx-v6j6
Package: flowise (NPM)
Severity: HIGH (CVSS 8.3)
CWE: CWE-94: Improper Control of Generation of Code ('Code Injection')
Vulnerable versions: <= 3.0.13
Patched in: 3.1.0

Summary: Flowise: Remote code execution vulnerability in AirtableAgent.ts caused by lack of input verification when using `Pandas`.

Details:
## Description

### Summary

“AirtableAgent” is an agent function provided by FlowiseAI that retrieves search results by accessing private datasets from airtable.com. “AirtableAgent” uses Python, along with `Pyodide` and `Pandas`, to get and return results.

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.

**The point is that an attacker can bypass the intended behavior of the LLM and trigger Remote Code Execution through a simple prompt injection.**

### About Airtable

The `airtable.ts` function retrieves and processes user datasets stored on Airtable.com through its API.

![pic1](https://drive.google.com/uc?id=1pKzk2leZ_w6Zb1rL3Rm0xkQr3ty1jom9)
![pic2](https://drive.google.com/uc?id=1pConjaiW2eeWJpcHnx1LTp3_CYn846u8)
The usage of Airtable is as shown in the image above. After creating a Chatflow like above, you can ask data-related questions using prompts and receive answers.

![pic3](https://drive.google.com/uc?id=1S6cIznhnuEjXJjRHCX32Av6QkgYQza6Q)

### Details

```jsx
// packages/components/nodes/agents/AirtableAgent/AirtableAgent.ts
  let base64String = Buffer.from(JSON.stringify(airtableData)).toString('base64')

  const loggerHandler = new ConsoleCallbackHandler(options.logger)
  const callbacks = await additionalCallbacks(nodeData, options)

  const pyodide = await LoadPyodide()

  // First load the csv file and get the dataframe dictionary of column types
  // For example using titanic.csv: {'PassengerId': 'int64', 'Survived': 'int64', 'Pclass': 'int64', 'Name': 'object', 'Sex': 'object', 'Age': 'float64', 'SibSp': 'int64', 'Parch': 'int64', 'Ticket': 'object', 'Fare': 'float64', 'Cabin': 'object', 'Embarked': 'object'}
  let dataframeColDict = ''
  try {
      const code = `import pandas as pd
import base64
import json

base64_string = "${base64String}"

decoded_data = base64.b64decode(base64_string)

json_data = json.loads(decoded_data)

df = pd.DataFrame(json_data)
my_dict = df.dtypes.astype(str).to_dict()
print(my_dict)
json.dumps(my_dict)`
      dataframeColDict = await pyodide.runPythonAsync(code)
  } catch (error) {
      throw new Error(error)
  }
```

Airtable retrieves results by accessing datasets from airtable.com. When retrieving data, it is fetched as a JSON object encoded in base64. Then, when loading data, it is decoded and converted into an object using Python code.

```jsx
// packages/components/nodes/agents/AirtableAgent/AirtableAgent.ts
let pythonCode = ''
if (dataframeColDict) {
    const chain = new LLMChain({
        llm: model,
        prompt: PromptTemplate.fromTemplate(systemPrompt),
        verbose: process.env.DEBUG === 'true' ? true : false
    })
    const inputs = {
        dict: dataframeColDict,
        question: input
    }
    const res = await chain.call(inputs, [loggerHandler, ...callbacks])
    pythonCode = res?.text
    // Regex to get rid of markdown code blocks syntax
    pythonCode = python