Security¶
Threat model¶
Tools registered via @expose_as_tool are, by definition, an API surface
you are choosing to make callable by an LLM — potentially one acting on
instructions from untrusted user input (prompt injection is a real
concern for any tool-using agent). This package's security posture:
- Nothing is exposed implicitly. Every tool requires an explicit
actions=[...]entry in@expose_as_tool. There is no "expose everything on this viewset" mode, and no auto-discovery of viewsets — you always know exactly what surface area a given registry grants. - Real permission enforcement, not a parallel system.
execute_tooldispatches through the viewset's actual.as_view(), sopermission_classesandauthentication_classesare enforced exactly as they would be for a genuine HTTP request — there is no separate, potentially-weaker authorization path for tool calls. SeeENFORCE_PERMISSIONSif you ever need to reason about disabling this (strongly discouraged). - The calling identity is explicit, never implicit.
execute_toolrequires you to passuser=(or accepts the anonymous default) — there is no ambient "current user" magically picked up from thread-local state, which avoids an entire class of bugs where a tool call accidentally executes as the wrong identity. - Validation is real serializer validation. Arguments passed to
create/update/partial_updatetools are validated by the actual serializer class, with the same field-level validators,validate_*methods, andvalidate()cross-field checks a normal request would go through. An LLM cannot bypass validation logic by constructing unusual argument shapes — invalid input raisesToolValidationErrorexactly as a400 Bad Requestwould for a normal client. - Schema generation never leaks write-only or unmapped data. Input
schemas only ever include fields DRF itself considers writable for
that action;
FileField/ImageFieldare excluded outright (see Troubleshooting) rather than guessed at in a way that might expose more than intended.
Prompt injection is out of scope, deliberately¶
This package secures the tool-calling boundary (schema accuracy, permission enforcement, validation) — it has no way to know whether an LLM's decision to call a particular tool with particular arguments was influenced by adversarial content in its context window. That is a model/agent-framework concern. What this package guarantees is that even if an LLM is convinced to attempt something it shouldn't, it cannot do anything the calling identity's permissions and the serializer's validation wouldn't already allow a normal API client to do.
Recommendations¶
- Scope agent identities tightly. Prefer a dedicated service account with the minimum permission set a given agent needs, rather than running tool calls as a superuser or as an arbitrary end user without review.
- Never disable
ENFORCE_PERMISSIONSfor an internet-facing agent. It exists specifically for closed, fully-trusted internal execution contexts (e.g. a background job you control end-to-end) that perform their own authorization before callingexecute_tool. - Keep destructive actions (
destroy, bulk-affecting custom actions) out of autonomous tool sets unless you have a human-in-the-loop confirmation step — see Common Patterns. - Audit tool calls. Nothing is logged by this package by default —
wrap
execute_toolat your call site if you need an audit trail (see Common Patterns).
Reporting a vulnerability¶
See SECURITY.md.