security: replace unrestricted setattr with allowlist in Python backend (#28083)
## Summary
Fixes a critical security vulnerability in the ONNX Runtime Python
backend where user-controlled `kwargs` were applied to `SessionOptions`
and `RunOptions` via unrestricted `setattr()`, allowing arbitrary file
overwrites.
## Vulnerability
The `prepare()` method in `onnxruntime/python/backend/backend.py`
iterated over user-controlled `kwargs` and used `setattr()` to apply
them directly to a `SessionOptions` instance. The `hasattr()` check was
not a security guard — it returned `True` for all exposed properties
including dangerous ones like `optimized_model_filepath`.
**Attack vector:**
```python
onnxruntime.backend.prepare(
model_path,
optimized_model_filepath="/etc/passwd", # overwrites any file with protobuf binary
graph_optimization_level=onnxruntime.GraphOptimizationLevel.ORT_ENABLE_ALL
)
```
The same pattern existed in `backend_rep.py` for `RunOptions`.
## Fix
Replaced the unrestricted `hasattr/setattr` loop in both files with
strict allowlists:
- **`_ALLOWED_SESSION_OPTIONS`** (13 safe attrs) in `backend.py`
- **`_ALLOWED_RUN_OPTIONS`** (4 safe attrs) in `backend_rep.py`
**Both `SessionOptions` and `RunOptions` use identical validation
logic** with three outcomes for each kwarg key:
- **Allowlisted** — Applied via `setattr()` (e.g.
`graph_optimization_level`, `log_severity_level`)
- **Known-but-blocked** (real attribute on the object, but not on
allowlist) — Raises `RuntimeError` (e.g. `optimized_model_filepath`,
`terminate`)
- **Completely unknown** (not a property on the object at all) —
Silently ignored for forward compatibility (e.g.
`nonexistent_option_xyz`)
**Blocked dangerous attributes:**
- `optimized_model_filepath` — triggers `Model::Save()`, overwrites
arbitrary files with protobuf binary
- `profile_file_prefix` — writes profiling JSON to arbitrary path
- `enable_profiling` — causes uncontrolled file writes to cwd
- `terminate` (RunOptions) — denies the current inference call
- `training_mode` (RunOptions) — silently switches inference behavior in
training builds
## Tests
Added `TestBackendKwargsAllowlist` with 13 new test methods covering all
exploit vectors (blocked attrs raise `RuntimeError`), safe allowlisted
attrs (accepted), unknown attrs (silently ignored), and end-to-end
`run_model()` paths for both session and run options. All 15 tests pass
(13 new + 2 pre-existing in `TestBackend`), no regressions.
## Files Changed
- `onnxruntime/python/backend/backend.py`
- `onnxruntime/python/backend/backend_rep.py`
- `onnxruntime/test/python/onnxruntime_test_python_backend.py`
- `.agents/skills/python-kwargs-setattr-security/SKILL.md`
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>