SECURITY_GUIDE.md
This document defines the security invariants of SurrealDB and the review triggers that gate any change touching them. It is the canonical source of truth for what "secure" means in this codebase.
Audience. Contributors and maintainers writing code, human reviewers approving PRs, and AI tooling assisting with development or code review.
When to consult. Before opening or reviewing any change that touches files or behaviours matched by a section's Review Triggers — authentication, sessions, permissions, RPC/HTTP transport, the parser, function execution, storage keys, imports, or anything in the Cross-Cutting Concerns.
How to apply. Treat each invariant as a requirement that must still hold after
the change. If a code path deviates from an invariant, it must carry an explicit
// SECURITY: (or equivalent) comment explaining why and why it is safe — see
Intentional Exceptions. Otherwise flag the deviation at the severity defined in
Severity Triage.
For the general (non-security) review checklist — performance, error handling, concurrency, test coverage, dependencies — see REVIEW.md.
File references use short module names. Core engine modules (iam/, dbs/, fnc/,
buc/, rpc/, sql/, kvs/, doc/) live under surrealdb/core/src/. Server
modules (ntw/, rpc/) live under surrealdb/server/src/. When both crates
contain the same module name (e.g. rpc/), both locations are security-relevant.
When a diff touches a security-sensitive area identified by a section's Review Triggers, the reviewer should:
When multiple sections are triggered, prioritize findings as follows:
Code paths may intentionally deviate from an invariant with explicit justification.
When reviewing, do not flag a violation if the code contains an accompanying comment
(e.g. // SAFETY:, // SECURITY:) that documents why the deviation is necessary
and why it is safe. Flag the deviation if the justification is absent, unclear, or
does not address the specific invariant being violated.
Test code (#[cfg(test)], files under tests/, language-tests/) may use patterns
that are forbidden in production code (e.g. unwrap, perms bypass). These are
acceptable in test context.
SurrealDB's security model rests on four pillars:
perms=false)
must be strictly bounded.Files: iam/, sql/access*.rs, session/token handling, JWT signing/verification
Flag for detailed review when changes touch:
is_allowed)Files: iam/, dbs/options.rs (Options struct, perms flag, with_import),
ctx/context.rs (check_perms, is_allowed), document processing pipeline in
doc/ (check_permissions_table, process_table_fields, pluck_generic,
pluck_select)
perms=false, this
suppression must not extend to user-supplied expressions or user-controlled data
lookups beyond the strictly required scope.OPTION IMPORT and OPTION FORCE must require Editor-or-above at Database scope.
No Record-scoped or anonymous actor may activate import mode.USE statement that changes namespace or database must verify the actor's
Auth level grants access to the target scope.Permission enum defaults to Full. All code constructing permission objects
must do so intentionally. Omitting a PERMISSIONS clause results in full access.process_table_fields) and read path (pluck_generic, pluck_select),
including for computed fields, reduced documents, and all output modes (AFTER,
BEFORE, DIFF, FIELDS).Flag for detailed review when changes touch:
Options::check_perms, Options::is_allowed, or perms flag propagationPermission enum, its Default impl, or the Permissions structnew_with_perms(false) or with_import(true)DEFINE EVENT, DEFINE TABLE ... AS, or DEFINE FIELD ... REFERENCE processingAuth, AuthLimit, Actor, or role-checking methodsUSE statement handling in the executorprocess_table_fields, pluck_generic, or pluck_selectpurge_references or cascade processing logicauth_enabled is evaluated or propagatedreduced document mechanism or computed field handlingFiles: Executor, session management, iam/reset.rs, context resolution
use call, and HTTP header-based context selection must
verify the authenticated principal is authorized for the target namespace and
database before the switch takes effect.$auth, $token, $session, $access) must not be set,
overwritten, or removed by LET, UNSET, RPC set, or RPC unset. Protection
must apply symmetrically to both set and unset operations.surreal-ns and surreal-db headers must be validated against the
principal's authorized scope.reset, the session must be in the most restrictive state. If auth is
required, the reset session must not permit data operations until re-authentication.Flag when changes touch:
USE statement gains new semantics or session manipulationSession struct gains new security-relevant fieldsPROTECTED_PARAM_NAMES list changesFiles: Document processing pipeline (create, update, upsert, insert, delete, relate), Key API, field evaluation, events, views, cascades
Flag when changes touch:
Files: ntw/ (Axum router, middleware stack), CORS, WebSocket handlers,
GraphQL, error formatting
pub must
be documented with security implications.Flag when changes touch:
set_session, or session lifecycleclient_ip module (header sources, proxy validation)ResponseError implementationRequestBodyLimitLayer applicationFiles: rpc/, WebSocket handling, CBOR/JSON/FlatBuffers deserialization,
transaction management, notification routing
begin RPC method must cap maximum concurrent transactions per WebSocket
connection. Unbounded transaction accumulation must not be possible.Flag when changes touch:
check_protected_param or PROTECTED_PARAM_NAMESFiles: Live query creation, notification dispatch, KILL handling, cleanup
Flag when changes touch:
Files: Schema statement processing, catalog writes, expression validation
is_allowed(Action::Edit, ResourceKind, Base)
before execution. Schema operations must require explicit namespace/database
context.Flag when changes touch:
Files: ntw/api.rs, API routing, middleware, handler execution
/api/:ns/:db/:endpoint) must be
validated against the session's authenticated context. Mismatches must be rejected..., %2e%2e, encoded slashes) must be rejected or
normalized before routing.Flag when changes touch:
ntw/api.rs routing, path matching, or parameter extractionFiles: Import endpoint, export endpoint, streaming parser, CLI import
Flag when changes touch:
OPTION IMPORT authorization or flag propagationFiles: fnc/, scripting runtime (QuickJS), HTTP client, capabilities,
Surrealism/WASM
$array.map(...)) must be correctly
canonicalized for capability checking.inherit_env) must be reviewed for secret exposure.Flag when changes touch:
fnc/mod.rs)Files: buc/, BucketController, object store backends
check_permission()
with the correct BucketOperation.Flag when changes touch:
buc/ module, BucketController, or bucket permission checkingDEFINE BUCKET processing or backend URL handlingFiles: INFO statements, health/status/version endpoints, metrics
Flag when changes touch:
Files: kvs/, key encoding/decoding, transaction handling, datastore
implementations
Flag when changes touch:
kvs/ key types)Files: syn/, sql/, parser entry points, expression construction
Result types.Flag when changes touch:
Any code path where system-internal execution (perms=false) processes
user-influenced expressions is a potential confused deputy. This includes:
All such paths must have an explicit, documented authority context. The perms=false
scope must be minimized and must not extend to evaluating arbitrary user inputs.
Flag when changes touch new_with_perms(false), authority context propagation in
event/view/cascade processing, or any path that evaluates user-defined expressions
during system-internal operations.
All error responses visible to clients must be sanitized. Review any change to error formatting, error types, or error propagation paths. Watch for:
Flag when changes touch error type definitions, Display/ResponseError impls,
error conversion (From/Into) chains, or any code path that formats errors for
client responses.
Every operation that processes user-controlled input must have bounded resource consumption. Watch for:
Flag when changes add new allocation paths for user-controlled data, remove or increase existing limits, add recursive processing, or introduce new external call sites without timeouts.
The import flag bypasses field validation, event processing, live query
notifications, and view computation. Any change involving:
import flagOPTION IMPORT authorization gate...must be reviewed for authorization correctness and flag lifetime scoping.
Flag when changes touch with_import, OPTION IMPORT handling, or any document
processing pipeline condition that checks the import flag.
Every operation must be verified against the principal's authorized namespace and database. Watch for:
Flag when changes touch key encoding/decoding, cache key construction, USE statement handling, HTTP header-based context resolution, or any data structure that is indexed or partitioned by namespace/database.