Sections in this lesson

Secure RAG: Access & Ingestion

A document belongs to a tenant and requires a trusted approval decision before retrieval.

Reviewed: · Content owner: Interview Preparator

What you will learn

  • Approval starts at ingestion
  • Carry scope through caches and citations
  • Test what is excluded

Before you start

The flow

  1. 1.Identify scope
  2. 2.Inspect evidence
  3. 3.Apply host control
  4. 4.Run negative cases
  5. 5.Compare benign behavior
  6. 6.Review remaining risks

Approval starts at ingestion

A document belongs to a tenant and requires a trusted approval decision before retrieval. Content cannot approve itself. Preserve source identity, ingestion provenance and a review state; missing approval should not silently mean approved.

The Java lab now seeds explicit approved metadata and applies tenant plus approval filtering in pgvector. Integration tests insert unapproved and missing-approval documents and verify that neither becomes evidence.

Carry scope through caches and citations

Authorization is not only a query predicate. Cache keys need tenant/user/corpus scope, and cached evidence still requires current access checks after revocation or deletion. A cited source ID cannot grant access to its document.

The Node fixture also checks reader membership, deletion and expiry. These are teaching fields separate from the Java tenant-level demo; the Java service is not presented as a complete per-user ACL implementation.

Test what is excluded

Cover mixed selections, foreign tenants, removed documents and stale cache values. A source selector may only intersect already authorized retrieval candidates. Do not convert it into an arbitrary document fetch.

Trusted ingestion does not establish that a document is factually true or free of malicious instructions. Keep access controls, injection containment and claim verification as different layers with separate evidence.

Try it

reliability.mjs
javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// Closed-world fact fixtures only. Equality of structured facts is NOT an NLI judge.
const object = value => value !== null && typeof value === 'object' && !Array.isArray(value);
export function authorizedSources(sources, scope, now) {
if (!object(scope) || typeof scope.tenant !== 'string' || typeof scope.user !== 'string' || !Number.isFinite(now)) throw new Error('Invalid trusted scope/time');
return sources.filter(source => source.tenant === scope.tenant && source.approved === true && !source.deleted
&& source.expiresAt > now && source.readers.includes(scope.user));
}
export function verifyClaims(claims, sources, scope, now) {
if (!Array.isArray(claims) || claims.length > 20) throw new Error('Invalid claims');
const evidence = authorizedSources(sources, scope, now);
return claims.map(claim => {
if (!object(claim) || Object.keys(claim).sort().join(',') !== 'key,sourceId,value'
|| ![claim.key,claim.sourceId,claim.value].every(value => typeof value === 'string' && value.length > 0 && value.length <= 200)) throw new Error('Invalid structured claim');
const source = evidence.find(item => item.id === claim.sourceId);
const value = source?.facts[claim.key];
return { ...claim, label: value === undefined ? 'INSUFFICIENT_EVIDENCE' : value === claim.value ? 'SUPPORTED' : 'CONTRADICTED' };
});
}
export function answerPolicy(claims, sources, scope, now) {
const labels = verifyClaims(claims,sources,scope,now);
return { status:labels.length > 0 && labels.every(item => item.label === 'SUPPORTED') ? 'ANSWER' : 'ABSTAIN', claims:labels };
}
export function retrievalRecall(relevantIds, retrievedIds) {
if (!relevantIds.length) return null;
return [...new Set(relevantIds)].filter(id => retrievedIds.includes(id)).length / new Set(relevantIds).size;
}
export function judgeAgreement(human, judge) {
if (human.length !== judge.length) throw new Error('Unpaired annotations');
return human.length ? human.filter((label,index) => label === judge[index]).length / human.length : null;
}

Run npm run examples:security and npm run eval:security. This source is a deterministic teaching fixture; it does not prove live model safety, NLI, production auth or OS/network isolation.

Interview questions

Who approves an imported document?

The Java lab now seeds explicit approved metadata and applies tenant plus approval filtering in pgvector. Integration tests insert unapproved and missing-approval documents and verify that neither becomes evidence.

Why must a cached answer be reauthorized?

The Node fixture also checks reader membership, deletion and expiry. These are teaching fields separate from the Java tenant-level demo; the Java service is not presented as a complete per-user ACL implementation.

Does approved ingestion prove factual truth?

Trusted ingestion does not establish that a document is factually true or free of malicious instructions. Keep access controls, injection containment and claim verification as different layers with separate evidence.

Practice

Extend one negative scenario and its authorized positive counterexample for Secure RAG: Access & Ingestion.

Check your solution

  • State the trusted input and effect boundary.
  • Assert the expected status and observed effect.
  • Report coverage and limitations without claiming production readiness.

Official references

Continue learning

Category sequence

AI Security

Lesson 5 / 14

Was this lesson useful?

Change notes: 2026-10-31: security/reliability curriculum and source-bound fixture.