GitHub Security Advisory: GHSA-p433-9wv8-28xj
Package: onnx (PIP)
Severity: MODERATE (CVSS 5.5)
CWE: CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal'), CWE-61: UNIX Symbolic Link (Symlink) Following
Vulnerable versions: < 1.21.0
Patched in: 1.21.0

Summary: ONNX: External Data Symlink Traversal

Details:
Summary
- Issue: Symlink traversal in external data loading allows reading files outside the model directory.
- Affected code: `onnx/onnx/checker.cc: resolve_external_data_location` used via Python `onnx.external_data_helper.load_external_data_for_model`.
- Impact: Arbitrary file read (confidentiality breach) when a model’s external data path resolves to a symlink targeting a file outside the model directory.

Root Cause
- The function `resolve_external_data_location(base_dir, location, tensor_name)` intends to ensure that external data files reside within `base_dir`. It:
  - Rejects empty/absolute paths
  - Normalizes the relative path and rejects `..`
  - Builds `data_path = base_dir / relative_path`
  - Checks `exists(data_path)` and `is_regular_file(data_path)`
- However, `std::filesystem::is_regular_file(path)` follows symlinks to their targets. A symlink placed inside `base_dir` that points to a file outside `base_dir` will pass the checks and be returned. The Python loader then opens the path and reads the target file.

Code Reference
- File: onnx/onnx/checker.cc:970-1060
- Key logic:
  - Normalization: `auto relative_path = file_path.lexically_normal().make_preferred();`
  - Existence: `std::filesystem::exists(data_path)`
  - Regular file check: `std::filesystem::is_regular_file(data_path)`
  - Returned path is later opened in Python: `external_data_helper.load_external_data_for_tensor`.

Proof of Concept (PoC)
- File: `onnx_external_data_symlink_traversal_poc.py`
- Behavior: Creates a model with an external tensor pointing to `tensor.bin`. In the model directory, creates `tensor.bin` as a symlink to `/etc/hosts` (or similar). Calls `load_external_data_for_model(model, base_dir)`. Confirms that `tensor.raw_data` contains content from the target outside the model directory.
- Run:
  - `python3 onnx_external_data_symlink_traversal_poc.py`
  - Expected: `[!!!] VULNERABILITY CONFIRMED: external_data symlink escaped base_dir`

onnx_external_data_symlink_traversal_poc.py

```python
#!/usr/bin/env python3
"""
ONNX External Data Symlink Traversal PoC

Finding: load_external_data_for_model() (via c_checker._resolve_external_data_location)
does not reject symlinks. A relative location that is a symlink inside the
model directory can target a file outside the directory and will be read.

Impact: Arbitrary file read outside model_dir when external data files are
obtained from attacker-controlled archives (zip/tar) that create symlinks.

This PoC:
 - Creates a model with a tensor using external_data location 'tensor.bin'
 - Creates 'tensor.bin' as a symlink to a system file (e.g., /etc/hosts)
 - Calls load_external_data_for_model(model, base_dir)
 - Confirms that tensor.raw_data contains the content of the outside file

Safe: only reads a benign system file if present.
"""

import os
import sys
import tempfile
import pathlib

# Ensure we import installed onnx, not the local cloned package
_here = os.path.dirname(os.path.abspath(__file__))
if _here in