Unit 12.02: Permissions enforced in the data, not the prompt
Unit ID: SQL-M12-U03 - Estimated active time: 14-17 minutes Objective: restrict what an assistant can retrieve using data-layer controls.
An instruction is not an access control
"Only answer using documents the user is allowed to see" is a request to a model, not a rule. It fails in the ordinary case - the model misjudges - and in the adversarial case, where retrieved content itself contains instructions.
If a restricted row enters the context window, treat it as disclosed. The only reliable control is to prevent retrieval.
Filter before retrieval, not after generation
-- The assistant's query, scoped to what this user may see
SELECT doc_id, content
FROM order_facts
WHERE country = 'IN' -- the user's permitted scope
AND visibility = 'internal';
-- 679 rows eligible
679 of 1,000 rows are eligible for a user scoped to India. The other 321 were never candidates, so no model decision was involved.
Design the permission column deliberately
SELECT visibility, COUNT(*) FROM order_facts GROUP BY visibility;
-- internal | 1000
A single value here, because our data has no real classification. In a live system this column carries the actual rule - team, region, role, or a join to an entitlements table. Two principles apply:
- Default to the most restrictive value. A row with unknown visibility must not be retrievable.
- Never derive permission from the content. Parsing text to decide who may read it is guesswork;
the answer belongs in a column.
The NULL trap, one last time
SELECT COUNT(*) FROM order_facts WHERE visibility <> 'public';
From Module 3: this drops rows where visibility IS NULL. In a permissions filter that failure is backwards - you want unknown-visibility rows excluded, and a filter written this way silently excludes them from the *restricted* set instead. Write the positive form:
-- fragment: the filter that must be applied before retrieval
WHERE visibility = 'internal'
Allow-lists fail closed. Deny-lists fail open, and NULL is exactly where they fail.
Practice
A colleague proposes adding "do not reveal salary information" to the system prompt. What do you recommend instead?
Check your answer
Do not put salary data in the retrieval table at all - or, if some role legitimately needs it, mark those rows with a restrictive visibility and filter on it at retrieval.
The prompt instruction fails in three ways: the model may misjudge, retrieved content may override it (prompt injection), and there is no audit trail proving the data was never exposed. A row that was never retrieved cannot leak.
Takeaway
Enforce permissions with a column and a filter at retrieval time. Prompt instructions reduce likelihood; only exclusion reduces possibility.
---
