.agents/skills/python-kwargs-setattr-security/SKILL.md
Using hasattr(obj, k) / setattr(obj, k, v) with user-controlled kwargs is insecure. The hasattr check is NOT a security guard — it returns True for ALL exposed properties including dangerous ones.
# INSECURE — do not use
for k, v in kwargs.items():
if hasattr(options, k):
setattr(options, k, v)
Define a module-level frozenset of safe attribute names. Raise RuntimeError for known-but-blocked attrs; silently ignore unknown keys.
# Define at module level, before the class
_ALLOWED_SESSION_OPTIONS = frozenset({
"enable_cpu_mem_arena",
"enable_mem_pattern",
# ... only explicitly reviewed safe attrs
})
# In the method
for k, v in kwargs.items():
if k in _ALLOWED_SESSION_OPTIONS:
setattr(options, k, v)
elif hasattr(options, k): # reuse the existing instance, don't create new
raise RuntimeError(
f"SessionOptions attribute '{k}' is not permitted via the backend API. "
f"Allowed attributes: {', '.join(sorted(_ALLOWED_SESSION_OPTIONS))}"
)
# else: silently ignore (may be kwargs for a different config object)
hasattr(options, k) — never hasattr(ClassName(), k) (creates throwaway C++ objects per iteration)run_model() passes the same kwargs dict to both prepare() (validates SessionOptions) and rep.run() (validates RunOptions). A RunOptions kwarg unknown to SessionOptions is silently ignored by prepare() — this is correct because rep.run() will validate it. Only raise RuntimeError when the attr exists on the target object but is blocked._ALLOWED_<CLASSNAME> — ALL_CAPS, Google Styleoptimized_model_filepath — triggers Model::Save(), overwrites arbitrary filesprofile_file_prefix + enable_profiling — writes profiling JSON to arbitrary pathregister_custom_ops_library — loads arbitrary shared libraries (method, not property)onnxruntime/python/backend/backend.py — _ALLOWED_SESSION_OPTIONSonnxruntime/python/backend/backend_rep.py — _ALLOWED_RUN_OPTIONSonnxruntime/test/python/onnxruntime_test_python_backend.py — TestBackendKwargsAllowlist