security: replace unrestricted setattr with allowlist in Python backend#28083
security: replace unrestricted setattr with allowlist in Python backend#28083titaiwangms wants to merge 9 commits intomainfrom
Conversation
In backend.py and backend_rep.py, user-controlled kwargs were applied to SessionOptions/RunOptions via unrestricted setattr(), allowing arbitrary file overwrites via optimized_model_filepath (MSRC reported vulnerability). Fix: - Add _ALLOWED_SESSION_OPTIONS frozenset (13 safe properties) in backend.py - Add _ALLOWED_RUN_OPTIONS frozenset (4 safe properties) in backend_rep.py - Replace hasattr/setattr loops with allowlist checks - backend.py: blocked-but-known SessionOptions attrs raise RuntimeError; truly unknown attrs silently ignored (may be RunOptions keys from run_model) - backend_rep.py: all non-allowlisted attrs silently ignored; run_model() forwards same kwargs to both prepare() and run() Dangerous attributes now blocked: optimized_model_filepath, profile_file_prefix, enable_profiling, terminate, training_mode Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Agent-signed-off: Developer (73ab72e7) [claude-opus-4.6] Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add TestBackendKwargsAllowlist class to onnxruntime_test_python_backend.py covering the setattr allowlist fix in backend.py and backend_rep.py: - test_blocked_session_option_optimized_model_filepath_raises: verifies optimized_model_filepath is blocked with RuntimeError (file overwrite exploit) - test_blocked_session_option_profile_file_prefix_raises: verifies profile_file_prefix is blocked with RuntimeError (profiling file write) - test_unknown_kwarg_is_silently_ignored: verifies truly unknown kwargs are silently ignored and inference still produces correct output - test_safe_session_option_graph_optimization_level_is_accepted: verifies allowlisted graph_optimization_level kwarg is accepted and works - test_safe_session_option_intra_op_num_threads_is_accepted: verifies allowlisted intra_op_num_threads kwarg is accepted and works - test_blocked_run_option_terminate_is_silently_ignored: verifies that blocked RunOptions attrs are silently ignored in rep.run() so that run_model() kwargs forwarding does not raise Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Agent-signed-off: Developer (73ab72e7) [claude-opus-4.6] Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Cover the third dangerous SessionOptions attribute excluded from _ALLOWED_SESSION_OPTIONS: enable_profiling causes uncontrolled profiling JSON file writes to cwd and must raise RuntimeError when passed as a kwarg to backend.prepare(). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Agent-signed-off: Developer (73ab72e7) [claude-opus-4.6] Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- backend.py line 136: reuse existing `options` object in hasattr check instead of constructing a new SessionOptions() C++ object on each non-allowlisted kwarg - backend.py prepare() docstring: clarify that only a safe subset of SessionOptions attrs are accepted, referencing _ALLOWED_SESSION_OPTIONS - backend.py run_model() docstring: clarify that only a safe subset of RunOptions attrs are accepted, referencing _ALLOWED_RUN_OPTIONS - backend_rep.py: expand _ALLOWED_RUN_OPTIONS comment to explain WHY 'terminate' and 'training_mode' are excluded - test file: remove setattr() implementation detail from TestBackendKwargsAllowlist class docstring; describe behavior not implementation Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Agent-signed-off: Developer (73ab72e7) [claude-opus-4.6] Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Captures the allowlist pattern used to fix the unrestricted setattr() vulnerability in onnxruntime/python/backend/. Documents the insecure hasattr/setattr pattern, the correct frozenset allowlist fix, key conventions (RuntimeError, object reuse, silent ignore for forwarded kwargs), and the dangerous SessionOptions properties that must never be allowlisted. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Agent-signed-off: Developer (73ab72e7) [claude-opus-4.6] Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Hardens the ONNX Runtime Python backend by preventing user-controlled kwargs from setting unsafe SessionOptions / RunOptions attributes via unrestricted setattr(), mitigating arbitrary file write and inference disruption vectors.
Changes:
- Added strict allowlists for
SessionOptions(raise on known-but-blocked attributes) andRunOptions(silently ignore non-allowlisted keys). - Updated backend API docstrings to document allowlisted behavior.
- Added regression tests covering blocked attributes, allowed attributes, and ignore behavior.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| onnxruntime/python/backend/backend.py | Introduces _ALLOWED_SESSION_OPTIONS and raises on blocked known SessionOptions attributes. |
| onnxruntime/python/backend/backend_rep.py | Introduces _ALLOWED_RUN_OPTIONS and ignores all non-allowlisted RunOptions keys. |
| onnxruntime/test/python/onnxruntime_test_python_backend.py | Adds tests validating allowlist, block/raise semantics, and ignore behavior. |
| .github/skills/python-kwargs-setattr-security/SKILL.md | Documents the insecure pattern and the repo’s allowlist-based remediation approach. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- backend.py: use ', '.join(sorted(...)) in RuntimeError message for
human-readable comma-separated list of allowed attributes
- test file: wrap TestBackendKwargsAllowlist docstring to fit 120 chars
(lintrunner/ruff formatting requirement)
Note: frozenset formatting (frozenset({...}) -> frozenset({...}) indented)
was already correctly applied in prior commit.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Agent-signed-off: Developer (73ab72e7) [claude-opus-4.6]
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Review: PR #28083 — security: replace unrestricted setattr with allowlist in Python backendThanks for the security fix — the core approach (frozenset allowlists replacing unrestricted 1.
|
- backend.py: import _ALLOWED_RUN_OPTIONS from backend_rep; split kwargs
in run_model() so SessionOptions and RunOptions each receive only their
own keys — fixes silent swallowing of RunOptions attrs like terminate
- backend_rep.py: update run() loop to raise RuntimeError on blocked-but-known
RunOptions attrs (same pattern as prepare()); remove stale comment about
forwarding from run_model()
- tests: rename test_blocked_run_option_terminate_is_silently_ignored to
test_blocked_run_option_terminate_raises (behavior changed); add
assertIn('not permitted') to 3 blocked-attr tests; add 3 new run_model()
end-to-end tests covering safe session option, safe run option, and
blocked run option
- skill: move SKILL.md from .github/skills/ to .agents/skills/ (correct
location matching ort-build, ort-lint, ort-test)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Agent-signed-off: Developer (73ab72e7) [claude-opus-4.6]
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Skills in this repo live under .agents/skills/ (alongside ort-build, ort-lint, ort-test). Remove the misplaced .github/skills/ copy. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Thanks @vraspar — all items addressed in the latest commits: Item 1 (kwargs split + RunOptions raise): Item 2 (run_model tests): Added 3 new tests: Item 3 (SKILL.md location): Moved from Item 4 (error message assertions): Added |
Replace hardcoded /tmp paths with proper tempfile usage: - optimized_model_filepath test: uses tempfile.NamedTemporaryFile - profile_file_prefix test: uses tempfile.TemporaryDirectory + os.path.join Add os and tempfile imports at the top of the test file. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Agent-signed-off: Developer (73ab72e7) [claude-opus-4.6] Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
|
||
| from onnxruntime import InferenceSession, SessionOptions, get_available_providers, get_device | ||
| from onnxruntime.backend.backend_rep import OnnxRuntimeBackendRep | ||
| from onnxruntime.backend.backend_rep import OnnxRuntimeBackendRep, _ALLOWED_RUN_OPTIONS |
There was a problem hiding this comment.
| from onnxruntime.backend.backend_rep import OnnxRuntimeBackendRep, _ALLOWED_RUN_OPTIONS | |
| from onnxruntime.backend.backend_rep import _ALLOWED_RUN_OPTIONS, OnnxRuntimeBackendRep |
Summary
Fixes a critical security vulnerability in the ONNX Runtime Python backend where user-controlled
kwargswere applied toSessionOptionsandRunOptionsvia unrestrictedsetattr(), allowing arbitrary file overwrites.Vulnerability
The
prepare()method inonnxruntime/python/backend/backend.pyiterated over user-controlledkwargsand usedsetattr()to apply them directly to aSessionOptionsinstance. Thehasattr()check was not a security guard — it returnedTruefor all exposed properties including dangerous ones likeoptimized_model_filepath.Attack vector:
The same pattern existed in
backend_rep.pyforRunOptions.Fix
Replaced the unrestricted
hasattr/setattrloop in both files with strict allowlists:_ALLOWED_SESSION_OPTIONS(13 safe attrs) inbackend.py_ALLOWED_RUN_OPTIONS(4 safe attrs) inbackend_rep.pyBlocked-but-known
SessionOptionsattributes raise a clearRuntimeError.RunOptionssilently ignores non-allowlisted keys becauserun_model()forwards the samekwargsto both paths.Blocked dangerous attributes:
optimized_model_filepath— triggersModel::Save(), overwrites arbitrary files with protobuf binaryprofile_file_prefix— writes profiling JSON to arbitrary pathenable_profiling— causes uncontrolled file writes to cwdterminate(RunOptions) — denies the current inference callTests
Added
TestBackendKwargsAllowlistwith 7 tests covering all 3 exploit vectors (blocked + raise), safe allowlisted attrs (accepted), unknown attrs (silently ignored), and RunOptions blocked attrs (silently ignored). All 9 tests pass (7 new + 2 pre-existing), no regressions.Files Changed
onnxruntime/python/backend/backend.pyonnxruntime/python/backend/backend_rep.pyonnxruntime/test/python/onnxruntime_test_python_backend.py