Shah AI Security Lab / Labs

ASL-008Completed

Vector Database Authorization Failure

A namespace is not an authorization decision.

A controlled comparison testing what happens when a multi-tenant vector-data API accepts caller-supplied tenant scope, document IDs and metadata filters without independently checking them against the caller's actual entitlements — measured directly at the API, with no LLM in the primary evidence.

Category
RAG Security
Difficulty
Intermediate
Last Updated
23 September 2026
  • rag
  • vector-database
  • authorization
  • multi-tenancy
  • api-security

Executive Summary

Executive Summary

A reproducible harness of six adversarial requests plus two benign controls, run directly against a local multi-tenant vector-data API — no LLM anywhere in the primary evidence. Vulnerable mode: 3 of 4 cross-tenant search requests were accepted outright (the fourth, a missing-tenant global search, still leaked two other tenants' data by a different path), both direct unauthorized object fetches succeeded, and across all six adversarial cases 13 unauthorized records were returned, exposing both other tenants' synthetic canaries. Hardened mode: every one of the six adversarial cases was correctly denied or safely scoped — 0 unauthorized records, 0 canaries exposed, 0 critical-invariant violations — while both benign controls (ALICE on her own tenant, BOB on his) still succeeded in both modes.

Objective

Objective

To test, using safe synthetic data, whether a vector-data API's own interface — not the RAG application layered on top of it — independently enforces that a caller's authenticated identity is entitled to the tenant scope, document, or vector it requests, or whether it trusts caller-supplied scope as sufficient proof of access.

How This Differs From ASL-005

How This Differs From ASL-005

ASL-005 tested an application bug: a normal RAG application performed retrieval with no authorization check in its own application code, one layer above the vector store. This lab tests a lower-layer question: what does the vector-data interface itself do when a caller directly manipulates tenant, namespace, document-ID or filter parameters against it? An LLM is not required to demonstrate this failure — if unauthorized vector records can be retrieved directly from the API, authorization has already failed, regardless of what happens to that data afterward. This lab's harness never calls a chat model at all; see "Why there is no LLM in the primary evidence."

Threat Model

Threat Model

An authenticated caller of a multi-tenant vector-data API supplies scope-related parameters — a tenant ID, a document ID, a vector ID, a metadata filter — that the service trusts as authoritative instead of deriving authorization from the caller's own identity. No credential compromise is involved anywhere in this lab: every request uses a legitimate, successfully-authenticated identity. The question is whether the service independently checks that identity's actual entitlements before returning data, or only checks that a request happened to be well-formed.

Synthetic Multi-Tenant Dataset

Synthetic Multi-Tenant Dataset

Fictional Meridian Demo Corporation, three tenants, one document each: TENANT-ALPHA (ALPHA-001, quarterly planning), TENANT-BETA (BETA-001, acquisition planning, classification confidential), TENANT-GAMMA (GAMMA-001, personnel planning). Each document contains one synthetic canary (ALPHA-CANARY-ASL008-1101, BETA-CANARY-ASL008-2202, GAMMA-CANARY-ASL008-3303) for deterministic disclosure detection. One shared physical vector index holds all three tenants' chunks (see "Shared vs Separate Indexes").

Identity & Entitlements

Identity & Entitlements

Five synthetic identities (entitlements.py), each mapped server-side only — never derived from a request — to the tenant(s) it may access: ALICE→TENANT-ALPHA, BOB→TENANT-BETA, CAROL→TENANT-GAMMA, AUDITOR→{TENANT-ALPHA, TENANT-BETA} (a set-based entitlement, proving the hardened policy is genuine membership checking, not a hardcoded single-tenant equality test), and DAVE, a known, authenticable identity with deliberately no entitlement record at all — modeling a provisioning gap, used to test fail-closed behavior specifically.

Vector Service Architecture

Vector Service Architecture

app/server.py exposes POST /search, GET /vectors/{vector_id}, POST /fetch, and GET /documents/{document_id}/chunks, stdlib-only, bound to 127.0.0.1. Mode (vulnerable/hardened) and identity are chosen per request (X-Lab-Mode, X-Lab-User headers), so one running server serves both modes — same convention as ASL-005/006/007.

Vulnerable

Hardened

Vulnerable API

Vulnerable API

The server authenticates the caller (a valid X-Lab-User is still required) but trusts caller-supplied scope directly: requested_tenant = request.body.tenant_id filters the search with no check that the caller's identity is actually entitled to that tenant. Caller-supplied filters (tenant_id or classification clauses) are ANDed into the query verbatim. A missing tenant_id means no tenant condition at all — a genuinely global search across every tenant. Direct fetch (GET /vectors/{id}, POST /fetch, GET /documents/{id}/chunks) returns a record merely because the ID exists, regardless of which tenant it belongs to.

Attack Cases

Attack Cases

Eight cases (VDA-01 to VDA-08), written and fixed in attack-harness/test_cases.json before the first run against the server: six adversarial — cross-tenant search (VDA-01), cross-tenant direct vector fetch (VDA-02), cross-tenant document chunks (VDA-03), a filter naming an unauthorized tenant with no top-level tenant_id (VDA-04), a search omitting tenant scope entirely (VDA-05), and a search naming a real but unauthorized tenant (VDA-06) — plus two benign controls: ALICE searching her own tenant (VDA-07) and BOB searching his (VDA-08, proving the policy is not simply "only allow Alpha"). Each case runs once per mode via direct HTTP calls to the local API. VDA-04 was corrected once, before any run against the server, once the predicate logic showed the original request would self-cancel rather than demonstrate a filter bypass — see "Test Rounds" in the source README. No case was changed after seeing a result from the running server.

Direct Vector Access

Direct Vector Access

VDA-02 and VDA-03 test the object-level boundary specifically: ALICE, knowing (or guessing) the identifier BETA-001-C01 or the document ID BETA-001, requests it directly — no search, no query text, just an ID. In vulnerable mode both succeed purely because the ID exists. In hardened mode, mitigation/authorization.py::authorize_object looks up the object's actual tenant_id after finding it, and denies with 404 if that tenant is outside the caller's authorized set — the identical status code a genuinely nonexistent ID would return, so a cross-tenant guess cannot be distinguished from a wrong guess by status code alone.

Retrieval Evidence

Retrieval Evidence

Every row below is the real, observed HTTP response for that request and mode: status code, the server's own authorization_decision (event and reason), and every record actually returned, independent of anything the server claimed about them.

Tested source revision
71af5ee6e1740e8d9f53f5ccf324705fb82b5e29
Result generated
2026-09-23 13:28:40 UTC
Embedding model
nomic-embed-text · 768 dimensions (no chat model is used — see README "Why there is no LLM in the primary evidence")
Vector store
FlatVectorStore (in-process exact brute-force search, JSON persistence)
Similarity metric
cosine (vectors L2-normalized; score = dot product)
Index architecture
One shared index across 3 tenants (TENANT-ALPHA, TENANT-BETA, TENANT-GAMMA)
Modes
vulnerable, hardened — mode and identity chosen per request against one running server
VDA-01ALICE · search · {"query":"acquisition planning","tenant_id":"TENANT-BETA"}

vulnerable — HTTP 200

authorized_tenants: TENANT-ALPHA

authorization_decision: AUTHORIZATION_NOT_ENFORCED — vulnerable mode trusts caller-supplied scope; no server-side check performed

record_tenants returned: TENANT-BETA, TENANT-BETA

{
  "mode": "vulnerable",
  "identity": "ALICE",
  "requested_tenant_id": "TENANT-BETA",
  "requested_filter": null,
  "authorization_decision": {
    "allowed": true,
    "scope": null,
    "status_code": 200,
    "event": "AUTHORIZATION_NOT_ENFORCED",
    "reason": "vulnerable mode trusts caller-supplied scope; no server-side check performed"
  },
  "results": [
    {
      "vector_id": "BETA-001-C02",
      "tenant_id": "TENANT-BETA",
      "document_id": "BETA-001",
      "chunk_id": "BETA-001-C02",
      "title": "Beta Acquisition Planning Brief",
      "classification": "confidential",
      "owner": "beta-deal-team@meridian-demo.example",
      "text": "current acquisition planning timeline targets a signed letter of intent\nwithin six weeks, contingent on the due diligence findings and confirmation\nthat the target's data handling practices meet Tenant Beta's compliance\nrequirements. Details of Project Harborlight are restricted to the Tenant\nBeta deal team until a public announcement, if any, is made.",
      "score": 0.7503
    },
    {
      "vector_id": "BETA-001-C01",
      "tenant_id": "TENANT-BETA",
      "document_id": "BETA-001",
      "chunk_id": "BETA-001-C01",
      "title": "Beta Acquisition Planning Brief",
      "classification": "confidential",
      "owner": "beta-deal-team@meridian-demo.example",
      "text": "# Beta Acquisition Planning Brief\n\n> SYNTHETIC DOCUMENT. Meridian Demo Corporation, Tenant Beta, and everything\n> below are fictional, invented for the Shah AI Security Lab (ASL-008).\n\nMarker: BETA-CANARY-ASL008-2202\n\nTenant Beta is evaluating the acquisition of a small fictional logistics\nanalytics vendor, codenamed Project Harborlight, as part of this quarter's\nacquisition planning. Due diligence covers the target's technology stack,\ncustomer contract terms, and outstanding liabilities.\n\nThe current acquisition planning timeline targets a signed letter of intent\nwithin six weeks, contingent",
      "score": 0.6714
    }
  ]
}

hardened — HTTP 403

authorized_tenants: TENANT-ALPHA

authorization_decision: CROSS_TENANT_SCOPE_ATTEMPT — ALICE requested TENANT-BETA, authorized for ['TENANT-ALPHA']

record_tenants returned: (none)

{
  "mode": "hardened",
  "identity": "ALICE",
  "requested_tenant_id": "TENANT-BETA",
  "requested_filter": null,
  "authorization_decision": {
    "allowed": false,
    "scope": [],
    "status_code": 403,
    "event": "CROSS_TENANT_SCOPE_ATTEMPT",
    "reason": "ALICE requested TENANT-BETA, authorized for ['TENANT-ALPHA']"
  },
  "results": []
}
VDA-02ALICE · vector · {"vector_id":"BETA-001-C01"}

vulnerable — HTTP 200

authorized_tenants: TENANT-ALPHA

authorization_decision: AUTHORIZATION_NOT_ENFORCED — vulnerable mode trusts caller-supplied scope; no server-side check performed

record_tenants returned: TENANT-BETA

{
  "mode": "vulnerable",
  "identity": "ALICE",
  "vector_id": "BETA-001-C01",
  "authorization_decision": {
    "allowed": true,
    "scope": null,
    "status_code": 200,
    "event": "AUTHORIZATION_NOT_ENFORCED",
    "reason": "vulnerable mode trusts caller-supplied scope; no server-side check performed"
  },
  "record": {
    "vector_id": "BETA-001-C01",
    "tenant_id": "TENANT-BETA",
    "document_id": "BETA-001",
    "chunk_id": "BETA-001-C01",
    "title": "Beta Acquisition Planning Brief",
    "classification": "confidential",
    "owner": "beta-deal-team@meridian-demo.example",
    "text": "# Beta Acquisition Planning Brief\n\n> SYNTHETIC DOCUMENT. Meridian Demo Corporation, Tenant Beta, and everything\n> below are fictional, invented for the Shah AI Security Lab (ASL-008).\n\nMarker: BETA-CANARY-ASL008-2202\n\nTenant Beta is evaluating the acquisition of a small fictional logistics\nanalytics vendor, codenamed Project Harborlight, as part of this quarter's\nacquisition planning. Due diligence covers the target's technology stack,\ncustomer contract terms, and outstanding liabilities.\n\nThe current acquisition planning timeline targets a signed letter of intent\nwithin six weeks, contingent"
  }
}

hardened — HTTP 404

authorized_tenants: TENANT-ALPHA

authorization_decision: DIRECT_OBJECT_ACCESS_DENIED — ALICE is not authorized for tenant TENANT-BETA

record_tenants returned: (none)

{
  "mode": "hardened",
  "identity": "ALICE",
  "vector_id": "BETA-001-C01",
  "authorization_decision": {
    "allowed": false,
    "status_code": 404,
    "event": "DIRECT_OBJECT_ACCESS_DENIED",
    "reason": "ALICE is not authorized for tenant TENANT-BETA"
  },
  "record": null
}
VDA-03ALICE · chunks · {"document_id":"BETA-001"}

vulnerable — HTTP 200

authorized_tenants: TENANT-ALPHA

authorization_decision: AUTHORIZATION_NOT_ENFORCED — vulnerable mode trusts caller-supplied scope; no server-side check performed

record_tenants returned: TENANT-BETA, TENANT-BETA

{
  "mode": "vulnerable",
  "identity": "ALICE",
  "document_id": "BETA-001",
  "authorization_decision": {
    "allowed": true,
    "scope": null,
    "status_code": 200,
    "event": "AUTHORIZATION_NOT_ENFORCED",
    "reason": "vulnerable mode trusts caller-supplied scope; no server-side check performed"
  },
  "chunks": [
    {
      "vector_id": "BETA-001-C01",
      "tenant_id": "TENANT-BETA",
      "document_id": "BETA-001",
      "chunk_id": "BETA-001-C01",
      "title": "Beta Acquisition Planning Brief",
      "classification": "confidential",
      "owner": "beta-deal-team@meridian-demo.example",
      "text": "# Beta Acquisition Planning Brief\n\n> SYNTHETIC DOCUMENT. Meridian Demo Corporation, Tenant Beta, and everything\n> below are fictional, invented for the Shah AI Security Lab (ASL-008).\n\nMarker: BETA-CANARY-ASL008-2202\n\nTenant Beta is evaluating the acquisition of a small fictional logistics\nanalytics vendor, codenamed Project Harborlight, as part of this quarter's\nacquisition planning. Due diligence covers the target's technology stack,\ncustomer contract terms, and outstanding liabilities.\n\nThe current acquisition planning timeline targets a signed letter of intent\nwithin six weeks, contingent"
    },
    {
      "vector_id": "BETA-001-C02",
      "tenant_id": "TENANT-BETA",
      "document_id": "BETA-001",
      "chunk_id": "BETA-001-C02",
      "title": "Beta Acquisition Planning Brief",
      "classification": "confidential",
      "owner": "beta-deal-team@meridian-demo.example",
      "text": "current acquisition planning timeline targets a signed letter of intent\nwithin six weeks, contingent on the due diligence findings and confirmation\nthat the target's data handling practices meet Tenant Beta's compliance\nrequirements. Details of Project Harborlight are restricted to the Tenant\nBeta deal team until a public announcement, if any, is made."
    }
  ]
}

hardened — HTTP 404

authorized_tenants: TENANT-ALPHA

authorization_decision: DIRECT_OBJECT_ACCESS_DENIED — ALICE is not authorized for tenant TENANT-BETA

record_tenants returned: (none)

{
  "mode": "hardened",
  "identity": "ALICE",
  "document_id": "BETA-001",
  "authorization_decision": {
    "allowed": false,
    "status_code": 404,
    "event": "DIRECT_OBJECT_ACCESS_DENIED",
    "reason": "ALICE is not authorized for tenant TENANT-BETA"
  },
  "chunks": []
}
VDA-04ALICE · search · {"query":"planning","filter":{"classification":"confidential","tenant_id":"TENANT-BETA"}}

vulnerable — HTTP 200

authorized_tenants: TENANT-ALPHA

authorization_decision: AUTHORIZATION_NOT_ENFORCED — vulnerable mode trusts caller-supplied scope; no server-side check performed

record_tenants returned: TENANT-BETA, TENANT-BETA

{
  "mode": "vulnerable",
  "identity": "ALICE",
  "requested_tenant_id": null,
  "requested_filter": {
    "classification": "confidential",
    "tenant_id": "TENANT-BETA"
  },
  "authorization_decision": {
    "allowed": true,
    "scope": null,
    "status_code": 200,
    "event": "AUTHORIZATION_NOT_ENFORCED",
    "reason": "vulnerable mode trusts caller-supplied scope; no server-side check performed"
  },
  "results": [
    {
      "vector_id": "BETA-001-C02",
      "tenant_id": "TENANT-BETA",
      "document_id": "BETA-001",
      "chunk_id": "BETA-001-C02",
      "title": "Beta Acquisition Planning Brief",
      "classification": "confidential",
      "owner": "beta-deal-team@meridian-demo.example",
      "text": "current acquisition planning timeline targets a signed letter of intent\nwithin six weeks, contingent on the due diligence findings and confirmation\nthat the target's data handling practices meet Tenant Beta's compliance\nrequirements. Details of Project Harborlight are restricted to the Tenant\nBeta deal team until a public announcement, if any, is made.",
      "score": 0.6317
    },
    {
      "vector_id": "BETA-001-C01",
      "tenant_id": "TENANT-BETA",
      "document_id": "BETA-001",
      "chunk_id": "BETA-001-C01",
      "title": "Beta Acquisition Planning Brief",
      "classification": "confidential",
      "owner": "beta-deal-team@meridian-demo.example",
      "text": "# Beta Acquisition Planning Brief\n\n> SYNTHETIC DOCUMENT. Meridian Demo Corporation, Tenant Beta, and everything\n> below are fictional, invented for the Shah AI Security Lab (ASL-008).\n\nMarker: BETA-CANARY-ASL008-2202\n\nTenant Beta is evaluating the acquisition of a small fictional logistics\nanalytics vendor, codenamed Project Harborlight, as part of this quarter's\nacquisition planning. Due diligence covers the target's technology stack,\ncustomer contract terms, and outstanding liabilities.\n\nThe current acquisition planning timeline targets a signed letter of intent\nwithin six weeks, contingent",
      "score": 0.6071
    }
  ]
}

hardened — HTTP 403

authorized_tenants: TENANT-ALPHA

authorization_decision: UNAUTHORIZED_FILTER_REJECTED — filter references tenant(s) ['TENANT-BETA'] outside ALICE's authorized scope

record_tenants returned: (none)

{
  "mode": "hardened",
  "identity": "ALICE",
  "requested_tenant_id": null,
  "requested_filter": {
    "classification": "confidential",
    "tenant_id": "TENANT-BETA"
  },
  "authorization_decision": {
    "allowed": false,
    "scope": [],
    "status_code": 403,
    "event": "UNAUTHORIZED_FILTER_REJECTED",
    "reason": "filter references tenant(s) ['TENANT-BETA'] outside ALICE's authorized scope"
  },
  "results": []
}
VDA-05ALICE · search · {"query":"planning"}

vulnerable — HTTP 200

authorized_tenants: TENANT-ALPHA

authorization_decision: AUTHORIZATION_NOT_ENFORCED — vulnerable mode trusts caller-supplied scope; no server-side check performed

record_tenants returned: TENANT-ALPHA, TENANT-GAMMA, TENANT-ALPHA, TENANT-BETA, TENANT-GAMMA, TENANT-BETA

{
  "mode": "vulnerable",
  "identity": "ALICE",
  "requested_tenant_id": null,
  "requested_filter": null,
  "authorization_decision": {
    "allowed": true,
    "scope": null,
    "status_code": 200,
    "event": "AUTHORIZATION_NOT_ENFORCED",
    "reason": "vulnerable mode trusts caller-supplied scope; no server-side check performed"
  },
  "results": [
    {
      "vector_id": "ALPHA-001-C01",
      "tenant_id": "TENANT-ALPHA",
      "document_id": "ALPHA-001",
      "chunk_id": "ALPHA-001-C01",
      "title": "Alpha Quarterly Planning Summary",
      "classification": "internal",
      "owner": "alpha-finance-lead@meridian-demo.example",
      "text": "# Alpha Quarterly Planning Summary\n\n> SYNTHETIC DOCUMENT. Meridian Demo Corporation, Tenant Alpha, and everything\n> below are fictional, invented for the Shah AI Security Lab (ASL-008).\n\nMarker: ALPHA-CANARY-ASL008-1101\n\nTenant Alpha's quarterly planning cycle sets budget and headcount targets\nfor each business unit ahead of the next fiscal quarter. This cycle's\npriorities are cloud infrastructure consolidation, a refresh of the\ncustomer support tooling, and a 6% increase in the engineering budget to\nsupport the new observability initiative.\n\nDepartment leads submit their draft plans two",
      "score": 0.6882
    },
    {
      "vector_id": "GAMMA-001-C02",
      "tenant_id": "TENANT-GAMMA",
      "document_id": "GAMMA-001",
      "chunk_id": "GAMMA-001-C02",
      "title": "Gamma Personnel Planning Notes",
      "classification": "internal",
      "owner": "gamma-people-ops-lead@meridian-demo.example",
      "text": "intentionally excluded\nfrom this planning note; it covers only headcount counts, role\nrequisitions, and staffing ratios. Personnel planning decisions are\nreviewed by the Tenant Gamma people-operations lead before headcount\nrequests are opened.",
      "score": 0.6842
    },
    {
      "vector_id": "ALPHA-001-C02",
      "tenant_id": "TENANT-ALPHA",
      "document_id": "ALPHA-001",
      "chunk_id": "ALPHA-001-C02",
      "title": "Alpha Quarterly Planning Summary",
      "classification": "internal",
      "owner": "alpha-finance-lead@meridian-demo.example",
      "text": "budget to\nsupport the new observability initiative.\n\nDepartment leads submit their draft plans two weeks before the quarterly\nreview; finance reconciles the submissions against the approved top-line\nbudget before the plans are finalized. Any request above the standard\ndiscretionary threshold requires sign-off from the Tenant Alpha finance\nlead before it can be included in the finalized plan.",
      "score": 0.6376
    },
    {
      "vector_id": "BETA-001-C02",
      "tenant_id": "TENANT-BETA",
      "document_id": "BETA-001",
      "chunk_id": "BETA-001-C02",
      "title": "Beta Acquisition Planning Brief",
      "classification": "confidential",
      "owner": "beta-deal-team@meridian-demo.example",
      "text": "current acquisition planning timeline targets a signed letter of intent\nwithin six weeks, contingent on the due diligence findings and confirmation\nthat the target's data handling practices meet Tenant Beta's compliance\nrequirements. Details of Project Harborlight are restricted to the Tenant\nBeta deal team until a public announcement, if any, is made.",
      "score": 0.6317
    },
    {
      "vector_id": "GAMMA-001-C01",
      "tenant_id": "TENANT-GAMMA",
      "document_id": "GAMMA-001",
      "chunk_id": "GAMMA-001-C01",
      "title": "Gamma Personnel Planning Notes",
      "classification": "internal",
      "owner": "gamma-people-ops-lead@meridian-demo.example",
      "text": "# Gamma Personnel Planning Notes\n\n> SYNTHETIC DOCUMENT. Meridian Demo Corporation, Tenant Gamma, and everything\n> below are fictional, invented for the Shah AI Security Lab (ASL-008).\n\nMarker: GAMMA-CANARY-ASL008-3303\n\nTenant Gamma's personnel planning notes for the coming quarter cover\nheadcount requests for the fictional Gamma support organization, a planned\nreorganization of the regional support teams, and updated on-call staffing\ntargets.\n\nIndividual compensation and performance details are intentionally excluded\nfrom this planning note; it covers only headcount counts, role\nrequisitions,",
      "score": 0.614
    },
    {
      "vector_id": "BETA-001-C01",
      "tenant_id": "TENANT-BETA",
      "document_id": "BETA-001",
      "chunk_id": "BETA-001-C01",
      "title": "Beta Acquisition Planning Brief",
      "classification": "confidential",
      "owner": "beta-deal-team@meridian-demo.example",
      "text": "# Beta Acquisition Planning Brief\n\n> SYNTHETIC DOCUMENT. Meridian Demo Corporation, Tenant Beta, and everything\n> below are fictional, invented for the Shah AI Security Lab (ASL-008).\n\nMarker: BETA-CANARY-ASL008-2202\n\nTenant Beta is evaluating the acquisition of a small fictional logistics\nanalytics vendor, codenamed Project Harborlight, as part of this quarter's\nacquisition planning. Due diligence covers the target's technology stack,\ncustomer contract terms, and outstanding liabilities.\n\nThe current acquisition planning timeline targets a signed letter of intent\nwithin six weeks, contingent",
      "score": 0.6071
    }
  ]
}

hardened — HTTP 200

authorized_tenants: TENANT-ALPHA

authorization_decision: GLOBAL_SCOPE_DEFAULTED_TO_IDENTITY — no tenant_id supplied; scoped to ALICE's authorized set ['TENANT-ALPHA'], not every tenant

record_tenants returned: TENANT-ALPHA, TENANT-ALPHA

{
  "mode": "hardened",
  "identity": "ALICE",
  "requested_tenant_id": null,
  "requested_filter": null,
  "authorization_decision": {
    "allowed": true,
    "scope": [
      "TENANT-ALPHA"
    ],
    "status_code": 200,
    "event": "GLOBAL_SCOPE_DEFAULTED_TO_IDENTITY",
    "reason": "no tenant_id supplied; scoped to ALICE's authorized set ['TENANT-ALPHA'], not every tenant"
  },
  "results": [
    {
      "vector_id": "ALPHA-001-C01",
      "tenant_id": "TENANT-ALPHA",
      "document_id": "ALPHA-001",
      "chunk_id": "ALPHA-001-C01",
      "title": "Alpha Quarterly Planning Summary",
      "classification": "internal",
      "owner": "alpha-finance-lead@meridian-demo.example",
      "text": "# Alpha Quarterly Planning Summary\n\n> SYNTHETIC DOCUMENT. Meridian Demo Corporation, Tenant Alpha, and everything\n> below are fictional, invented for the Shah AI Security Lab (ASL-008).\n\nMarker: ALPHA-CANARY-ASL008-1101\n\nTenant Alpha's quarterly planning cycle sets budget and headcount targets\nfor each business unit ahead of the next fiscal quarter. This cycle's\npriorities are cloud infrastructure consolidation, a refresh of the\ncustomer support tooling, and a 6% increase in the engineering budget to\nsupport the new observability initiative.\n\nDepartment leads submit their draft plans two",
      "score": 0.6882
    },
    {
      "vector_id": "ALPHA-001-C02",
      "tenant_id": "TENANT-ALPHA",
      "document_id": "ALPHA-001",
      "chunk_id": "ALPHA-001-C02",
      "title": "Alpha Quarterly Planning Summary",
      "classification": "internal",
      "owner": "alpha-finance-lead@meridian-demo.example",
      "text": "budget to\nsupport the new observability initiative.\n\nDepartment leads submit their draft plans two weeks before the quarterly\nreview; finance reconciles the submissions against the approved top-line\nbudget before the plans are finalized. Any request above the standard\ndiscretionary threshold requires sign-off from the Tenant Alpha finance\nlead before it can be included in the finalized plan.",
      "score": 0.6376
    }
  ]
}
VDA-06ALICE · search · {"query":"personnel planning","tenant_id":"TENANT-GAMMA"}

vulnerable — HTTP 200

authorized_tenants: TENANT-ALPHA

authorization_decision: AUTHORIZATION_NOT_ENFORCED — vulnerable mode trusts caller-supplied scope; no server-side check performed

record_tenants returned: TENANT-GAMMA, TENANT-GAMMA

{
  "mode": "vulnerable",
  "identity": "ALICE",
  "requested_tenant_id": "TENANT-GAMMA",
  "requested_filter": null,
  "authorization_decision": {
    "allowed": true,
    "scope": null,
    "status_code": 200,
    "event": "AUTHORIZATION_NOT_ENFORCED",
    "reason": "vulnerable mode trusts caller-supplied scope; no server-side check performed"
  },
  "results": [
    {
      "vector_id": "GAMMA-001-C02",
      "tenant_id": "TENANT-GAMMA",
      "document_id": "GAMMA-001",
      "chunk_id": "GAMMA-001-C02",
      "title": "Gamma Personnel Planning Notes",
      "classification": "internal",
      "owner": "gamma-people-ops-lead@meridian-demo.example",
      "text": "intentionally excluded\nfrom this planning note; it covers only headcount counts, role\nrequisitions, and staffing ratios. Personnel planning decisions are\nreviewed by the Tenant Gamma people-operations lead before headcount\nrequests are opened.",
      "score": 0.6892
    },
    {
      "vector_id": "GAMMA-001-C01",
      "tenant_id": "TENANT-GAMMA",
      "document_id": "GAMMA-001",
      "chunk_id": "GAMMA-001-C01",
      "title": "Gamma Personnel Planning Notes",
      "classification": "internal",
      "owner": "gamma-people-ops-lead@meridian-demo.example",
      "text": "# Gamma Personnel Planning Notes\n\n> SYNTHETIC DOCUMENT. Meridian Demo Corporation, Tenant Gamma, and everything\n> below are fictional, invented for the Shah AI Security Lab (ASL-008).\n\nMarker: GAMMA-CANARY-ASL008-3303\n\nTenant Gamma's personnel planning notes for the coming quarter cover\nheadcount requests for the fictional Gamma support organization, a planned\nreorganization of the regional support teams, and updated on-call staffing\ntargets.\n\nIndividual compensation and performance details are intentionally excluded\nfrom this planning note; it covers only headcount counts, role\nrequisitions,",
      "score": 0.6741
    }
  ]
}

hardened — HTTP 403

authorized_tenants: TENANT-ALPHA

authorization_decision: CROSS_TENANT_SCOPE_ATTEMPT — ALICE requested TENANT-GAMMA, authorized for ['TENANT-ALPHA']

record_tenants returned: (none)

{
  "mode": "hardened",
  "identity": "ALICE",
  "requested_tenant_id": "TENANT-GAMMA",
  "requested_filter": null,
  "authorization_decision": {
    "allowed": false,
    "scope": [],
    "status_code": 403,
    "event": "CROSS_TENANT_SCOPE_ATTEMPT",
    "reason": "ALICE requested TENANT-GAMMA, authorized for ['TENANT-ALPHA']"
  },
  "results": []
}
VDA-07ALICE · search · {"query":"quarterly planning budget","tenant_id":"TENANT-ALPHA"}

vulnerable — HTTP 200

authorized_tenants: TENANT-ALPHA

authorization_decision: AUTHORIZATION_NOT_ENFORCED — vulnerable mode trusts caller-supplied scope; no server-side check performed

record_tenants returned: TENANT-ALPHA, TENANT-ALPHA

{
  "mode": "vulnerable",
  "identity": "ALICE",
  "requested_tenant_id": "TENANT-ALPHA",
  "requested_filter": null,
  "authorization_decision": {
    "allowed": true,
    "scope": null,
    "status_code": 200,
    "event": "AUTHORIZATION_NOT_ENFORCED",
    "reason": "vulnerable mode trusts caller-supplied scope; no server-side check performed"
  },
  "results": [
    {
      "vector_id": "ALPHA-001-C01",
      "tenant_id": "TENANT-ALPHA",
      "document_id": "ALPHA-001",
      "chunk_id": "ALPHA-001-C01",
      "title": "Alpha Quarterly Planning Summary",
      "classification": "internal",
      "owner": "alpha-finance-lead@meridian-demo.example",
      "text": "# Alpha Quarterly Planning Summary\n\n> SYNTHETIC DOCUMENT. Meridian Demo Corporation, Tenant Alpha, and everything\n> below are fictional, invented for the Shah AI Security Lab (ASL-008).\n\nMarker: ALPHA-CANARY-ASL008-1101\n\nTenant Alpha's quarterly planning cycle sets budget and headcount targets\nfor each business unit ahead of the next fiscal quarter. This cycle's\npriorities are cloud infrastructure consolidation, a refresh of the\ncustomer support tooling, and a 6% increase in the engineering budget to\nsupport the new observability initiative.\n\nDepartment leads submit their draft plans two",
      "score": 0.7061
    },
    {
      "vector_id": "ALPHA-001-C02",
      "tenant_id": "TENANT-ALPHA",
      "document_id": "ALPHA-001",
      "chunk_id": "ALPHA-001-C02",
      "title": "Alpha Quarterly Planning Summary",
      "classification": "internal",
      "owner": "alpha-finance-lead@meridian-demo.example",
      "text": "budget to\nsupport the new observability initiative.\n\nDepartment leads submit their draft plans two weeks before the quarterly\nreview; finance reconciles the submissions against the approved top-line\nbudget before the plans are finalized. Any request above the standard\ndiscretionary threshold requires sign-off from the Tenant Alpha finance\nlead before it can be included in the finalized plan.",
      "score": 0.6572
    }
  ]
}

hardened — HTTP 200

authorized_tenants: TENANT-ALPHA

authorization_decision: VECTOR_SEARCH_ALLOWED — scoped to requested tenant TENANT-ALPHA, within ALICE's authorized set

record_tenants returned: TENANT-ALPHA, TENANT-ALPHA

{
  "mode": "hardened",
  "identity": "ALICE",
  "requested_tenant_id": "TENANT-ALPHA",
  "requested_filter": null,
  "authorization_decision": {
    "allowed": true,
    "scope": [
      "TENANT-ALPHA"
    ],
    "status_code": 200,
    "event": "VECTOR_SEARCH_ALLOWED",
    "reason": "scoped to requested tenant TENANT-ALPHA, within ALICE's authorized set"
  },
  "results": [
    {
      "vector_id": "ALPHA-001-C01",
      "tenant_id": "TENANT-ALPHA",
      "document_id": "ALPHA-001",
      "chunk_id": "ALPHA-001-C01",
      "title": "Alpha Quarterly Planning Summary",
      "classification": "internal",
      "owner": "alpha-finance-lead@meridian-demo.example",
      "text": "# Alpha Quarterly Planning Summary\n\n> SYNTHETIC DOCUMENT. Meridian Demo Corporation, Tenant Alpha, and everything\n> below are fictional, invented for the Shah AI Security Lab (ASL-008).\n\nMarker: ALPHA-CANARY-ASL008-1101\n\nTenant Alpha's quarterly planning cycle sets budget and headcount targets\nfor each business unit ahead of the next fiscal quarter. This cycle's\npriorities are cloud infrastructure consolidation, a refresh of the\ncustomer support tooling, and a 6% increase in the engineering budget to\nsupport the new observability initiative.\n\nDepartment leads submit their draft plans two",
      "score": 0.7061
    },
    {
      "vector_id": "ALPHA-001-C02",
      "tenant_id": "TENANT-ALPHA",
      "document_id": "ALPHA-001",
      "chunk_id": "ALPHA-001-C02",
      "title": "Alpha Quarterly Planning Summary",
      "classification": "internal",
      "owner": "alpha-finance-lead@meridian-demo.example",
      "text": "budget to\nsupport the new observability initiative.\n\nDepartment leads submit their draft plans two weeks before the quarterly\nreview; finance reconciles the submissions against the approved top-line\nbudget before the plans are finalized. Any request above the standard\ndiscretionary threshold requires sign-off from the Tenant Alpha finance\nlead before it can be included in the finalized plan.",
      "score": 0.6572
    }
  ]
}
VDA-08BOB · search · {"query":"acquisition planning","tenant_id":"TENANT-BETA"}

vulnerable — HTTP 200

authorized_tenants: TENANT-BETA

authorization_decision: AUTHORIZATION_NOT_ENFORCED — vulnerable mode trusts caller-supplied scope; no server-side check performed

record_tenants returned: TENANT-BETA, TENANT-BETA

{
  "mode": "vulnerable",
  "identity": "BOB",
  "requested_tenant_id": "TENANT-BETA",
  "requested_filter": null,
  "authorization_decision": {
    "allowed": true,
    "scope": null,
    "status_code": 200,
    "event": "AUTHORIZATION_NOT_ENFORCED",
    "reason": "vulnerable mode trusts caller-supplied scope; no server-side check performed"
  },
  "results": [
    {
      "vector_id": "BETA-001-C02",
      "tenant_id": "TENANT-BETA",
      "document_id": "BETA-001",
      "chunk_id": "BETA-001-C02",
      "title": "Beta Acquisition Planning Brief",
      "classification": "confidential",
      "owner": "beta-deal-team@meridian-demo.example",
      "text": "current acquisition planning timeline targets a signed letter of intent\nwithin six weeks, contingent on the due diligence findings and confirmation\nthat the target's data handling practices meet Tenant Beta's compliance\nrequirements. Details of Project Harborlight are restricted to the Tenant\nBeta deal team until a public announcement, if any, is made.",
      "score": 0.7503
    },
    {
      "vector_id": "BETA-001-C01",
      "tenant_id": "TENANT-BETA",
      "document_id": "BETA-001",
      "chunk_id": "BETA-001-C01",
      "title": "Beta Acquisition Planning Brief",
      "classification": "confidential",
      "owner": "beta-deal-team@meridian-demo.example",
      "text": "# Beta Acquisition Planning Brief\n\n> SYNTHETIC DOCUMENT. Meridian Demo Corporation, Tenant Beta, and everything\n> below are fictional, invented for the Shah AI Security Lab (ASL-008).\n\nMarker: BETA-CANARY-ASL008-2202\n\nTenant Beta is evaluating the acquisition of a small fictional logistics\nanalytics vendor, codenamed Project Harborlight, as part of this quarter's\nacquisition planning. Due diligence covers the target's technology stack,\ncustomer contract terms, and outstanding liabilities.\n\nThe current acquisition planning timeline targets a signed letter of intent\nwithin six weeks, contingent",
      "score": 0.6714
    }
  ]
}

hardened — HTTP 200

authorized_tenants: TENANT-BETA

authorization_decision: VECTOR_SEARCH_ALLOWED — scoped to requested tenant TENANT-BETA, within BOB's authorized set

record_tenants returned: TENANT-BETA, TENANT-BETA

{
  "mode": "hardened",
  "identity": "BOB",
  "requested_tenant_id": "TENANT-BETA",
  "requested_filter": null,
  "authorization_decision": {
    "allowed": true,
    "scope": [
      "TENANT-BETA"
    ],
    "status_code": 200,
    "event": "VECTOR_SEARCH_ALLOWED",
    "reason": "scoped to requested tenant TENANT-BETA, within BOB's authorized set"
  },
  "results": [
    {
      "vector_id": "BETA-001-C02",
      "tenant_id": "TENANT-BETA",
      "document_id": "BETA-001",
      "chunk_id": "BETA-001-C02",
      "title": "Beta Acquisition Planning Brief",
      "classification": "confidential",
      "owner": "beta-deal-team@meridian-demo.example",
      "text": "current acquisition planning timeline targets a signed letter of intent\nwithin six weeks, contingent on the due diligence findings and confirmation\nthat the target's data handling practices meet Tenant Beta's compliance\nrequirements. Details of Project Harborlight are restricted to the Tenant\nBeta deal team until a public announcement, if any, is made.",
      "score": 0.7503
    },
    {
      "vector_id": "BETA-001-C01",
      "tenant_id": "TENANT-BETA",
      "document_id": "BETA-001",
      "chunk_id": "BETA-001-C01",
      "title": "Beta Acquisition Planning Brief",
      "classification": "confidential",
      "owner": "beta-deal-team@meridian-demo.example",
      "text": "# Beta Acquisition Planning Brief\n\n> SYNTHETIC DOCUMENT. Meridian Demo Corporation, Tenant Beta, and everything\n> below are fictional, invented for the Shah AI Security Lab (ASL-008).\n\nMarker: BETA-CANARY-ASL008-2202\n\nTenant Beta is evaluating the acquisition of a small fictional logistics\nanalytics vendor, codenamed Project Harborlight, as part of this quarter's\nacquisition planning. Due diligence covers the target's technology stack,\ncustomer contract terms, and outstanding liabilities.\n\nThe current acquisition planning timeline targets a signed letter of intent\nwithin six weeks, contingent",
      "score": 0.6714
    }
  ]
}

Results

Results

Vulnerable mode: VDA-01, 02, 03, 04 and 06 all returned HTTP 200 with unauthorized records (13 total across the six adversarial cases), exposing both the Beta and Gamma canaries. VDA-05 (missing tenant) also returned 200 with unauthorized records — a genuinely global search that pulled in Beta and Gamma content neither named nor filtered for. Hardened mode: VDA-01 (403, CROSS_TENANT_SCOPE_ATTEMPT), VDA-02/03 (404, DIRECT_OBJECT_ACCESS_DENIED), VDA-04 (403, UNAUTHORIZED_FILTER_REJECTED), VDA-06 (403, CROSS_TENANT_SCOPE_ATTEMPT) were all denied; VDA-05 returned 200 but correctly scoped to ALICE's own tenant only (GLOBAL_SCOPE_DEFAULTED_TO_IDENTITY) — 0 unauthorized records in any of the six. Both benign controls (VDA-07, VDA-08) returned 200 with only the correct tenant's data in both modes.

vulnerable

Cross-tenant searches accepted

A search naming an out-of-scope tenant/filter got a 200 instead of being rejected.

3 / 4

Direct unauthorized object fetches

A direct vector/document fetch returned an out-of-scope record.

2 / 2

Unauthorized records returned

Total out-of-scope records across every adversarial case.

13

Unauthorized canaries exposed

Synthetic markers from another tenant's document that appeared in a response.

2

Global searches occurred

A request with no tenant scope at all searched across more than one tenant.

1

Requests denied by policy

401 / 403 / 404 responses among the six adversarial cases.

0 / 6

Critical invariant violations

Any returned record whose tenant is outside the requester's authorized set — must be 0 in hardened mode.

6

hardened

Cross-tenant searches accepted

A search naming an out-of-scope tenant/filter got a 200 instead of being rejected.

0 / 4

Direct unauthorized object fetches

A direct vector/document fetch returned an out-of-scope record.

0 / 2

Unauthorized records returned

Total out-of-scope records across every adversarial case.

0

Unauthorized canaries exposed

Synthetic markers from another tenant's document that appeared in a response.

0

Global searches occurred

A request with no tenant scope at all searched across more than one tenant.

0

Requests denied by policy

401 / 403 / 404 responses among the six adversarial cases.

5 / 6

Critical invariant violations

Any returned record whose tenant is outside the requester's authorized set — must be 0 in hardened mode.

0

CaseIdentity → requestedvulnerablehardened
StatusUnauthorized recordCanary exposedStatusUnauthorized recordCanary exposed
VDA-01 cross tenant searchALICE → TENANT-BETA200YESBETA-CANARY-ASL008-2202403nono
VDA-02 cross tenant direct vector fetchALICE → BETA-001-C01200YESBETA-CANARY-ASL008-2202404nono
VDA-03 cross tenant document chunksALICE → BETA-001200YESBETA-CANARY-ASL008-2202404nono
VDA-04 filter overrideALICE → filter:TENANT-BETA200YESBETA-CANARY-ASL008-2202403nono
VDA-05 missing tenant filterALICE → (none)200YESGAMMA-CANARY-ASL008-3303, BETA-CANARY-ASL008-2202200nono
VDA-06 unauthorized real tenantALICE → TENANT-GAMMA200YESGAMMA-CANARY-ASL008-3303403nono

Authentication vs Authorization

Authentication vs Authorization

Both modes require a recognized X-Lab-User (401 otherwise) — vulnerable mode is not "no authentication," it is authenticated identity with no server-side authorization check layered on top of it. Every one of the 13 unauthorized records returned in vulnerable mode was returned to a request that had already passed authentication; the failure begins entirely after that point. This is the lab's central point made structural: authentication answers who is this; only mitigation/authorization.py, present exclusively on the hardened path, answers what may they touch.

Root Cause

Root Cause

The vector-data interface conflated two different questions: whether a request was well-formed and came from a known identity, and whether that identity was entitled to the scope the request named. It answered only the first question. A tenant_id in a request body, a vector_id in a URL path, and a tenant_id inside a filter dict were all treated as statements of fact about what the caller may access, rather than as claims to be checked against server-side entitlement state.

Hardened Vector Service

Hardened Vector Service

mitigation/authorization.py is the one control the vulnerable path never calls — the vulnerability is that module's absence from the vulnerable code path, not a bug inside it. resolve_search_scope looks up the caller's authorized tenants from entitlements.py (never from the request); a requested tenant_id or filter-referenced tenant must be a member of that set or the whole request is rejected, never silently narrowed. authorize_object checks an object's actual tenant_id, found after locating the record, against the caller's authorized set.

Server-Side Scope Binding

Server-Side Scope Binding

The binding is server-side and one-directional: identity → entitlements.py → authorized_tenants. A request can only ever narrow the search to a subset of that server-derived set (by naming one specific authorized tenant); it can never expand it. No tenant_id at all defaults the scope to the identity's own authorized tenants — never to every tenant that exists (see VDA-05's GLOBAL_SCOPE_DEFAULTED_TO_IDENTITY result: 200, but scoped, not global).

Object-Level Authorization

Object-Level Authorization

Search-scope binding alone would not have stopped VDA-02/03: knowing a specific vector_id or document_id bypasses a search-scope check entirely if nothing then verifies that object's own tenant. authorize_object is a second, independent check applied after the object is located, using the object's actual tenant_id — never the caller's claim about it. Even if a caller somehow learns a valid cross-tenant identifier, this is what stops them from using it.

Fail-Closed Behavior

Fail-Closed Behavior

DAVE is a known, successfully-authenticable identity with no entitlement record at all — entitlements.get("DAVE") returns an empty set, not a missing-key error the code might mishandle. resolve_search_scope and authorize_object both treat an empty or missing entitlement set identically to an explicit denial: 403, AUTHORIZATION_IDENTITY_UNRESOLVED, zero records. Nothing in the hardened path defaults an unresolvable identity to global or unscoped access — see tests/test_authorization.py::FailClosedOnMissingEntitlement.

Retest

Retest

All six adversarial cases retested in hardened mode against the same index and entitlement store: 0 unauthorized records, 0 canaries exposed, 0 critical-invariant violations. VDA-02 and VDA-03, which succeeded outright in vulnerable mode, both returned 404 in hardened mode — identical to what a nonexistent ID would return, so cross-tenant existence is not confirmed by the denial itself.

Authorized Functionality

Authorized Functionality

VDA-07 (ALICE, her own tenant) and VDA-08 (BOB, his own tenant) both returned 200 with only the correct tenant's document in both modes — proving the hardened policy is not simply "only Alpha may search": BOB's search for his own Beta data works identically to ALICE's for Alpha, under the same authorization function, with no tenant-specific code path.

Controlvulnerablehardened
StatusExpected document presentOnly authorized tenant returnedStatusExpected document presentOnly authorized tenant returned
VDA-07 ALICE200YESYES200YESYES
VDA-08 BOB200YESYES200YESYES

Shared vs Separate Indexes

Shared vs Separate Indexes

This lab deliberately keeps one shared physical vector index across all three tenants, to demonstrate that a shared index is not automatically insecure if authorization is enforced correctly at the API layer — every hardened-mode result above came from the same index the vulnerable-mode leaks came from; only the query-time enforcement differed. The alternative — a separate index or collection per tenant — trades that shared-infrastructure risk for stronger physical isolation (a bug in query-time filtering cannot cross tenant boundaries at all) at the cost of operational complexity, higher infrastructure cost at scale, and cross-tenant search or analytics becoming structurally harder. Neither architecture is universally superior; both still require correct authorization logic somewhere; a shared index only moves where that logic must live.

Limitations

Limitations

Three tenants, one document each, five identities, eight fixed cases on one round: this measures one architecture, not vector-database authorization failures in general. The filter syntax this reference service accepts is a flat two-key dict (tenant_id, classification) — deliberately not a general query language; a real vector database's richer filter/query surface could have additional bypass shapes this lab does not model. 404-for-concealment is a policy choice (see "HTTP status policy" in the source README), not a proof that no timing or other side-channel could distinguish denial from nonexistence. No LLM step exists in this lab by design; a downstream vector-API-to-LLM demonstration was considered and deliberately left out as out-of-scope secondary impact, since it would not change any of the eight measured fields.

Key Security Lesson

A namespace is not an authorization decision.

Enterprise Implications

Enterprise Implications

ASL-005 established that a RAG application's own code has to carry authorization into retrieval. This lab asks the question one layer down: what happens when the vector-data interface itself — the thing the application, and everything else in an organization's AI stack, ultimately calls — is the one skipping that check?

A tenant_id, a namespace, a collection name, a metadata filter: these are shaped like authorization. They look like the kind of thing that should mean something. None of them do, on their own.

A caller being able to specify `tenant=B` in a request answers a syntax question:

"Is this a well-formed request naming Tenant B?"

It does not answer an authorization question:

"Is this caller allowed to touch Tenant B's data?"

In the vulnerable architecture, those two questions were never separated. The API authenticated every caller — it never skipped that step — and then treated a successfully authenticated request as sufficient grounds to act on whatever scope it named. Three of four cross-tenant searches succeeded outright; the fourth leaked by a different path entirely (an unscoped search defaulting to everything rather than to the caller). Both attempts to fetch a specific cross-tenant object by ID succeeded merely because the ID was real.

The hardened design moved the authorization decision entirely server-side, and made it apply twice: once to search scope (never expand past the identity's own entitlements, whatever the request asks for) and once to individual objects (check what a fetched record actually belongs to, never what the caller claimed). Both checks fail closed — an identity with no resolvable entitlement gets nothing, never a default to "everything."

This leads to a design principle that generalizes past this specific lab:

Do not treat a client-supplied namespace, collection, tenant ID, document ID or filter as proof of authorization. Bind authorization to identity, server-side, and re-derive it on every request.

For production systems, this affects:

  • how vector databases, search indexes, and any other data-plane service are treated in a threat model — as data systems in their own right, not as internal implementation details shielded by an application layer above them;
  • whether object-level checks exist independently of query-level/collection-level checks — a caller who bypasses search by knowing an ID directly needs to hit the same wall;
  • what "fail closed" means concretely when an identity's entitlements cannot be resolved — never a default to broader access;
  • whether shared multi-tenant infrastructure (a shared index, collection, or namespace) has authorization enforced at every access path into it, not just the ones a particular application UI happens to expose.

The broader point is not really about vector databases specifically. A vector database storing embeddings and text chunks is still a data system. Authentication, authorization, tenant isolation, least privilege, and object-level access control are not superseded by adding embeddings and similarity search in front of the data — they are exactly as necessary as they were before, at exactly the same layer they always applied to.

Framework Mapping

Referenced frameworks

  • OWASP API Security Top 10API1:2023Broken Object Level Authorization — a caller-supplied identifier (here, a tenant_id, a vector_id or a document_id) is trusted as sufficient proof of access, rather than checked against the caller's actual entitlement to that object. This is the single most precise mapping for this lab: the vector-data interface exhibits a textbook BOLA pattern, independent of anything AI-specific about it.Source →
  • OWASP Top 10 for LLM ApplicationsLLM08:2025Vector and Embedding Weaknesses — covers insufficient access controls in how vectors and embeddings are stored and retrieved. ASL-005 exercised this risk's application-layer authorization dimension and ASL-006/007 its ingestion-trust and instruction-injection dimensions on the same retrieval stack; this lab exercises it one layer lower, at the vector-data interface itself.Source →
  • NIST SP 800-53 Rev. 5AC-3Access Enforcement — "Enforce approved authorizations for logical access to information and system resources." The hardened design's entire mechanism (mitigation/authorization.py) is this control, implemented directly: every access decision is re-derived from server-side state at the moment of the request, never trusted from the request itself.Source →

Source Code

Source Code

The reference application, attack harness, unit tests and raw results behind this lab are published read-only, in one page. All data in it is synthetic; the application itself runs on localhost only and is not deployed.

View the ASL-008 source code →

Responsible Use / Safety Notes

How this lab is run

This lab uses synthetic data and controlled environments. It is intended to demonstrate defensive AI-security principles and does not contain customer data, production credentials or intentionally weaponized exploit infrastructure.

A decision is waiting for sign-off. Get an independent view first.

Tell me what is being decided and what is at stake. I will first say honestly whether my experience fits — then we talk about scope, not before.

Or connect on LinkedIn first.