Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions docs/architecture/application-read-leak-evidence.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Application read isolation evidence

Issue #157 tracks tenant-owned read isolation across the backend, HTTP API, and MCP. `ApplicationReadLeakMatrixE2ETests` supplies a two-populated-tenant broker case in standalone and split API/worker hosts. One signed-in member owns both tenants, and an unrelated signed-in member exercises denial.

The test creates a distinct application and two system instances per tenant, plus two current draft boundary references for the application and one instance in each tenant. It verifies positive exact application-revision and system-instance reads over HTTP and MCP. It follows every page of each tenant's application-revision, system-instance, application-boundary-reference, and system-instance-boundary-reference lists with `limit=1` over both transports, checking tenant IDs, record IDs, and tenant-specific names or reference text. Cursors issued in tenant A are rejected in tenant B over HTTP and MCP.

For each list and exact read, a foreign application or instance ID under the other tenant returns `NotFound`; the unrelated member is denied. Application-change previews for both tenants return only their own current draft application boundary references, while a foreign application ID and the unrelated member are denied over HTTP and MCP.

This evidence covers current draft boundary references and the populated preview `boundary_references` field. It does not seed approved boundary versions, control draft references, or other preview contexts. It does not exercise a stopped worker or replay, or prove the top-level application list. The broader #157 inventory is in [tenant-read-leak-matrix.md](tenant-read-leak-matrix.md).
18 changes: 9 additions & 9 deletions docs/architecture/tenant-read-leak-matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,18 @@ The route inventory below follows `src/Compliance.App/Program.cs`.
| --- | --- | --- |
| RBAC `/teams`, `/roles`, team members, role permissions, role teams and the matching MCP lists | `TenantReadLeakMatrixE2ETests.ShouldScopeRbacListsSearchAndCursorsGivenTwoTenants`: both host modes; two-tenant team/role search and HTTP/MCP cursors through exhaustion, role-permission search cursors through exhaustion, nested search, foreign-parent empty pages, and outsider denial. | Add multi-row cursor cases for team members and role teams when those relationships gain a suitable seed fixture. |
| Application import batch GET, `/rows`, `/preview` and matching MCP reads | `TenantReadLeakMatrixE2ETests.ShouldScopeImportCountsRowsAndPreviewGivenTwoTenants`: both modes; distinct nonzero row and invalid counters, row and preview content, HTTP row cursor and foreign-cursor rejection, cross-tenant IDs, outsider denial. | Add preview and MCP row cursor variants. The progress fields are currently initialized to zero and have no update path; prove their isolation with processing in #213. No import list or separate count route exists. |
| Application list and boundary references | `ProjectionReadConsistencyE2ETests.ShouldNeverReturnStaleProjectionOrCrossTenantRowsGivenStandaloneOrSplitHost` covers application-list isolation and boundary-reference exact/non-disclosure in both modes. | Application revision, system-instance and reference list pagination, change preview, and remaining HTTP/MCP permutations. |
| Snapshot read, verification and manifest regeneration | `SnapshotE2ETests.ShouldFreezeExactScopeGivenStandaloneOrSplitWorker` covers cross-tenant verification/manifest and outsider denial in both modes. | Two-tenant snapshot list and cursor, plus full MCP list proof. |
| Application list, revisions, system instances, references and change preview | `ProjectionReadConsistencyE2ETests.ShouldNeverReturnStaleProjectionOrCrossTenantRowsGivenStandaloneOrSplitHost` covers application-list isolation in both modes. `ApplicationReadLeakMatrixE2ETests.ShouldKeepApplicationReadPagesAndPreviewsWithinTenantGivenTwoPopulatedTenants` adds HTTP/MCP exact revision and instance reads, `limit=1` revision, instance and application/instance reference lists through cursor exhaustion, strict foreign-cursor rejection, populated change previews, foreign IDs and outsider denial in both modes. [Detailed evidence](application-read-leak-evidence.md). | The top-level application list still lacks this two-populated-tenant cursor proof. The new fixture covers current draft boundary references, not approved versions, control references or stopped-worker replay. |
| Boundary program lists, versions, decisions and impact preview | `BoundarySnapshotReadLeakMatrixE2ETests.ShouldScopeBoundaryAndSnapshotReadsGivenTwoTenants` covers HTTP/MCP paged boundary, approved-version and decision lists through exhaustion, exact boundary/draft/version/effective/decision reads, and populated impact changes in both modes. Foreign IDs, outsider denial and strict foreign-cursor rejection are asserted. | One approved version per tenant is seeded; nested impact contributions can be empty. No separate count route exists. |
| Snapshot list, detail, verification and manifest regeneration | The same `BoundarySnapshotReadLeakMatrixE2ETests` case covers HTTP/MCP paged snapshot lists through exhaustion, exact snapshot, verification and regenerated manifest content in both modes, with foreign IDs, outsider denial and strict foreign-cursor rejection. `SnapshotE2ETests.ShouldFreezeExactScopeGivenStandaloneOrSplitWorker` supplies additional freeze/scope evidence. | Two snapshots per tenant are seeded; stopped-worker replay has separate capability evidence. No separate count route exists. |
| Program and client-service current, revision, list, history and setup-work reads | `ProgramReadLeakMatrixE2ETests.ShouldScopeProgramAndServicePagesGivenTwoTenants` covers HTTP/MCP exact current and revision reads, six paged list/history/setup-work families, seeded content and versions, strict foreign-cursor rejection, foreign IDs and outsider denial in both modes. [Detailed evidence](program-service-read-leak-evidence.md). | The fixture does not cover stopped-worker replay, Program stage transitions or future setup-work inputs beyond boundaries. These routes expose no separate count or search operation. |
| Platform metadata exception | `OperatorPortfolioE2ETests.ShouldListPlatformTenantMetadataGivenConfiguredOperatorAndHostMode` covers operator-only tenant metadata pagination in both modes. | Keep this separate from client business-record access. |
| Tenant detail, self-list, operator member list, invitation list, effective member access and matching MCP reads | `TenantIdentityReadLeakMatrixE2ETests.ShouldScopeTenantMemberInvitationAndSelfReadsGivenTwoTenants`: both modes; separate tenant owners and an outsider, operator metadata and member-list authority, HTTP/MCP self-list and invitation cursors through exhaustion, strict foreign invitation cursor rejection, foreign paths and email filters, and foreign member-access denial. [Detailed evidence](tenant-identity-read-leak-matrix.md). | Member-list pagination after a second membership activates and actor replacement/deprovisioning lifecycle proof. These reads have no total-count field. |

Still open for the same two-tenant, two-host HTTP/MCP matrix: program list,
revision and setup-work reads; control, commitment and risk draft/history pages;
client-service tenant and program lists/history; boundary program lists,
versions, decisions and impact preview; and the remaining application and
snapshot reads above. Existing capability tests often prove standalone
cross-tenant denial or split-host recovery, but that does not prove every list
and cursor in both modes.
Still open for the same two-tenant, two-host HTTP/MCP matrix: control,
commitment and risk draft/current/history reads, the top-level application
list cursor and the remaining application contexts above. Existing capability
tests often prove standalone cross-tenant denial or split-host recovery, but that
does not prove every list and cursor in both modes.

The current API has no separate tenant-owned count, export, job, notification
or artifact-content route. Import batch status carries row and invalid counts
Expand Down
Loading