Snowflake AI features and the rights they run with
Cortex Agents run generated SQL with the caller's roles, but a Cortex Search service returns what its owner's role could read. Record the rights model of every tool on an agent before the agent reaches governed data.
Row access policies and masking policies are usually written around the person running a query. Each policy checks context such as the querying role or user, then filters the rows the query returns or masks the values it shows. With an AI feature, a request passes through several components before it reaches the table, and each of those components runs under its own rights model. Snowflake's agent documentation states that data access depends on Snowflake privileges and on the execution context of each tool configured on the agent.
The access question therefore has to be answered per tool. A single agent can answer one question with SQL that runs as the user and the next with a search index that runs as its owner. Before an agent is given access to governed data, map the context that every path uses. The same reasoning applies to data reached through a share, which we covered in row access policies and the sharing boundary.
Agents and generated SQL run as the caller
Cortex Agents, generally available since 4 November 2025, authorize each request as the caller. For a REST call the role comes from the X-Snowflake-Role header, or from the user's default role when the header is omitted. When an application calls the agent as one service user for many people, every request carries that service user's roles. Requests made from Snowsight or from Snowflake CoWork (formerly Snowflake Intelligence) use the role selected in the interface. The agent uses the caller's session roles, so those roles also need privileges on the objects the agent's tools use, and USAGE on the agent is not sufficient by itself. What each tool can then read depends on its own rights model.
SQL that a Cortex Analyst tool generates from a semantic view runs with the caller's roles. Semantic views are owner's rights objects, but Snowflake documents that row access and masking policies on the underlying tables propagate to the semantic view and are enforced. Snowflake's examples key those policies on current_role() and on the querying user's email. The semantic view guide adds that, for Cortex Agents, the role that runs the queries needs SELECT on both the semantic view and its underlying tables, which is stricter than a plain query against the view. Users who hold that role can then query the tables directly, so sensitive columns left out of the semantic view need policies on the tables themselves.
Two details are easy to miss. If secondary roles are active in the request or the session, Snowflake uses them for privilege checks and for policy evaluation as well, so tests should run with the secondary-role settings the user will actually have. Sample values defined in Snowsight are not masked, because masking policies are not applied to metadata, and anyone who runs GET_DDL on the view sees them. Snowflake suggests representative, non-sensitive sample values where the real ones are sensitive.
Cortex Search returns what its owner could read
Cortex Search works differently, and Snowflake says so directly. Search services run with owner's rights. A role that holds USAGE on a service can query everything the service has indexed, whatever that role's own privileges on the tables and views in its source query are. In Snowflake's example the source table has row-level policies, and users of the service see results from rows the owner's role can read, even when their own role cannot read those rows in the table. The documentation advises caution when granting USAGE on a search service.
Attribute filters are the documented way to narrow search results, but the filter comes from the query or from the agent's tool configuration. Any role with USAGE can query the service directly, through SEARCH_PREVIEW or the REST API, with a different filter or none, so a filter in an agent's configuration does not limit what a USAGE holder can read. We found no documented option to run a search service with caller's rights or to bind a filter to the current user, and the agent pages do not say that calling a service through an agent changes what it returns. The CoWork pages state that CoWork queries use the user's credentials, and none of the pages we read explains how that fits with the owner's rights behavior of search.
We recommend treating a Cortex Search service as a published extract. Write its source query so that it returns only data that every holder of USAGE may see, and build a separate service for each audience when audiences differ. The same caution applies when Cortex Analyst uses a search service to look up literal values for a dimension. Snowflake's page on that integration still refers to it as a preview and does not say whose rights the lookup uses. Because the service supplies literal values for Cortex Analyst to use in the SQL it writes, values from rows the user cannot read could appear in the generated SQL.
Databricks documents a comparable gap. Its AI Search, formerly Vector Search, does not support row-level or column-level permissions, and an index cannot be created from a table that has row filters or column masks applied. Where attribute-based (ABAC) row filter and column mask policies govern the source table instead, the index syncs all rows and does not enforce those policies when it serves queries.
Custom tools keep their own rights
A stored procedure added to an agent as a custom tool keeps the rights model declared on the procedure. A custom tool can also be a function, and a SQL function reads the objects it references with its owner's privileges. Owner's rights is the default when a procedure is created, and any procedure called from an owner's rights procedure also behaves as an owner's rights procedure. A procedure written in a hurry to look up an account balance will run with its owner's privileges unless it was created with EXECUTE AS CALLER. An owner's rights version can return any balance its owner can read to whichever user asks for it.
Inside owner's rights code, a policy may be evaluated against the owner's roles in place of the asking user's. Snowflake documents that the SNOWFLAKE$CURRENT context reflects the owner's activated roles inside an owner's rights procedure, that the active roles IS_ROLE_IN_SESSION checks can differ from the session roles there, and that INVOKER_ROLE evaluates to the procedure owner's role. The documentation is less consistent about CURRENT_ROLE in that context and does not state what CURRENT_USER returns, so a policy that depends on either should be tested through the tool itself.
Controls Snowflake has added for agents
Snowflake marks sessions as agent-active for Cortex Agents, CoWork and the managed MCP server, among other paths. When an external application acts for a user with only a programmatic access token, Snowflake does not mark the session agent-active unless the application also uses a supported agent identity path, such as an OAuth integration configured with IS_AGENTIC = TRUE. A row access or masking policy can read SYS_CONTEXT('SNOWFLAKE$CURRENT', 'IS_AGENT_ACTIVATED') to withhold sensitive data from an agent even when the user's own role could read it. The agent_type column in query history records the type of agent that invoked a query, such as CORTEX_AGENT, and the agents_info column in access history can name each agent in the call chain. Together they give an audit trail per agent and per user, provided each person authenticates as themselves.
Restricted Session Scope, generally available for agents since 3 September 2026, is a configurable privilege ceiling for agent-active sessions. It can narrow the user's privileges but cannot extend them. The agent identity page, which describes IS_AGENT_ACTIVATED and Restricted Session Scope, does not mention Cortex Search or owner's rights procedures, so neither control should be assumed to cover those paths.
For one agent serving several tenants, the application passes tenant values in the variables block of each agent:run request. Snowflake sets them as session attributes before any generated SQL runs, and row access policies filter on those attributes. The attributes can be marked immutable, so generated SQL, code execution and tool calls cannot change them during the session. Isolation depends on the application that supplies the values, and Snowflake documents the session-attribute mechanism for generated SQL only, so a search service shared by several tenants needs its own separation. Snowflake describes tenant isolation as a shared responsibility, and its guidance for secure agents is to enforce authorization with access control and to keep it out of the agent's instructions.
What to check before an agent goes live
List every tool configured on each agent and record the rights model it runs under. Cortex Analyst tools run as the caller, Cortex Search runs with the service owner's rights, a custom procedure runs with the rights declared on it, and a SQL function used as a tool reads with its owner's privileges.
Test as the role the end user will hold, with the same secondary-role settings. By default a missing privilege on a configured tool does not reject the run. For Cortex Search, Cortex Analyst, MCP and skill tools, Snowflake checks access at the start of the run, and the agent continues with the tools the role can reach and reports the others as warnings. Custom tools, SQL execution and code execution are not checked in advance, so a missing privilege there comes back to the agent as a failed tool result. An agent tested by an administrator can therefore give a business user a different answer without the run failing. We recommend setting orchestration.tool_not_accessible to reject on agents that serve governed data, so that a named, checked tool the caller cannot reach fails the run. The Analyst check covers the semantic view only, so a missing SELECT on its tables still returns as a failed tool result when Analyst runs.
Review who holds USAGE on each search service, and compare the rows each service indexes with what each audience is permitted to read. Finally, decide which data an agent should never return, whoever is asking. Enforce that decision with a policy that reads IS_AGENT_ACTIVATED, test the policy through each owner's rights tool, and keep the same data out of the source query of every search service.