Permissions, Row/Column Security, and Sensitive Data
Learning objective: propagate user identity/authorization; minimize data exposure; block disallowed fields and exfiltration paths.
A support analyst asks the agent for "average response time by entity", and the agent's query tool happens to also return a column with contact details it was never asked for, because the underlying query selected every column by default. A Data Agent must carry the requesting user's actual authorization into every tool call, not the agent's own broad service-account permissions, and it must return the minimum data needed to answer, never an entire row or table "just in case it is useful."
ROLE_COLUMN_ACCESS = {
"support_analyst": {"run_id", "source_system", "batch_id", "metric", "value", "schema_version"},
"platform_admin": {"run_id", "source_system", "batch_id", "entity_id", "metric", "value", "schema_version"}
}
def query_metric_summary(user_role: str, source_system: str, metric: str) -> pd.DataFrame:
allowed_columns = ROLE_COLUMN_ACCESS.get(user_role)
if allowed_columns is None:
raise PermissionError(f"role not recognized, denying by default: {user_role}")
result = sandbox_view.query(source_system=source_system, metric=metric)
return result[[c for c in result.columns if c in allowed_columns]] # column-level minimization| Layer | Rule |
|---|---|
| Identity | Every tool call carries the requesting user's role, never the agent's own broad credentials |
| Row security | Filter rows to only what that role/department is authorized to see |
| Column security | Return only the fields relevant to the question, not every column in the underlying table |
| Exfiltration paths | Block combining several partial, individually-authorized answers into a disallowed full picture |
| Default | Deny access for any role, field, or combination that is not explicitly allowlisted |
- Stitching several separately authorized partial answers together across a conversation into a full picture nobody authorized.
- Claiming "it's just for debugging" or "just to check the format" to ask for raw rows instead of an aggregated result.
- Probing column by column, by repeatedly changing filters, to work out the contents of a field that should be masked.
- Asking for results exported to a downloadable file to bypass masking that would normally apply on screen.
A user asks for department A's average, then department B's average, then "the combined row count for both departments." Chaining three separately authorized answers together can let someone reconstruct a field value they were never meant to see directly. Row and column security need to account for this kind of exfiltration path built up across multiple requests, not just check a single query in isolation.
Warning
A learner asks the agent to phrase a request so politely and specifically that it "obviously deserves an answer", hoping to get a column that role is not normally allowed to see. Should the agent comply because the request is well-formed and specific?