🗼 ci: Guard Scoped Model Applies the Isolation Plugin (#13458)

Adds a coverage test that enumerates all registered models and asserts every
schema with a tenantId field has the tenant-isolation plugin applied (detected
via the global Symbol.for('librechat:tenantIsolation') marker the plugin sets),
with a single documented allowlist entry — SystemGrant, which scopes tenancy
manually. A future model that ships a tenantId field without the plugin (the
exact gap that lets data leak across tenants) now fails CI instead of shipping
silently.
This commit is contained in:
Danny Avila 2026-06-01 18:02:02 -04:00 • committed by GitHub
parent 8120fc69cd
commit 547b28455a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -0,0 +1,52 @@
import mongoose from 'mongoose';
import { createModels } from '../index';
/**
* Global symbol set by applyTenantIsolation() on every schema it processes.
* Recreated here via Symbol.for (the global registry) so the guard can detect
* plugin application without the plugin exporting anything.
*/
const TENANT_ISOLATION_APPLIED = Symbol.for('librechat:tenantIsolation');
/**
* Models that carry a `tenantId` field but intentionally do NOT use the
* tenant-isolation plugin. SystemGrant scopes tenancy manually inside its
* methods (see models/systemGrant). Adding an entry here must be a deliberate,
* reviewed decision — that is the whole point of this guard.
*/
const MANUAL_TENANT_SCOPING = new Set<string>(['SystemGrant']);
function isPluginApplied(schema: mongoose.Schema): boolean {
return (schema as unknown as { [key: symbol]: boolean })[TENANT_ISOLATION_APPLIED] === true;
}
describe('tenant-isolation plugin coverage', () => {
beforeAll(() => {
createModels(mongoose);
});
it('applies the tenant-isolation plugin to every model that has a tenantId field', () => {
const missing: string[] = [];
for (const [name, model] of Object.entries(mongoose.models)) {
const hasTenantId = Boolean(model.schema.path('tenantId'));
if (!hasTenantId || MANUAL_TENANT_SCOPING.has(name)) {
continue;
}
if (!isPluginApplied(model.schema)) {
missing.push(name);
}
}
expect(missing).toEqual([]);
});
it('keeps the manual-scoping allowlist accurate (tenantId field, no plugin)', () => {
for (const name of MANUAL_TENANT_SCOPING) {
const model = mongoose.models[name];
expect(model).toBeDefined();
expect(Boolean(model.schema.path('tenantId'))).toBe(true);
expect(isPluginApplied(model.schema)).toBe(false);
}
});
});