fix(auth): internal-token requests får verifierad tenant (P0 tenant-isolering)
masterplan-lock / check (push) Successful in 4s
CI (SIAX Cloud) / sonar (pull_request) Skipped
masterplan-lock / check (pull_request) Successful in 5s
CI (SIAX Cloud) / security (pull_request) Successful in 15s
Security Scan (SIAX Cloud) / security (pull_request) Successful in 43s
CI (SIAX Cloud) / integration (pull_request) Successful in 10m3s
CI (SIAX Cloud) / quality (pull_request) Successful in 10m15s

Bevisat: POST /api/0pt/canonical/facts med X-Internal-Token och tenant_id
999999999999999999 returnerade 201 och skrev data i annan tenant. Orsak:
internal-token-grenen returnerade next() utan x-0pt-verified-tenant, så
assertTenantMatchesVerified blev no-op.

Fix: INTERNAL_TENANT_ID (eller X-0pt-Internal-Tenant) sätts som verifierad
tenant, vilket gör guard-enforcement identisk med verifierad Bearer. Varnar
om variabeln saknas.
This commit is contained in:
2026-11-07 14:55:59 +01:00
parent 04aeaa754b
commit 849e7ae802
+19 -1
View File
@@ -35,13 +35,31 @@ const INTERNAL_SERVICE_ROUTES: RegExp[] = [
];
let _authDiagCount = 0;
let internalTenantWarned = false;
export async function middleware(req: NextRequest): Promise<NextResponse> {
const path = req.nextUrl.pathname;
if (EXEMPT_PATTERNS.some((re) => re.test(path))) return NextResponse.next();
if (INTERNAL_SERVICE_ROUTES.some((re) => re.test(path)) && verifyInternalServiceToken(req.headers)) {
return NextResponse.next();
// P014/P0 tenant isolation (2026-11-07): the internal-token path used to
// return next() WITHOUT a verified tenant, which made
// assertTenantMatchesVerified a no-op and let a service principal write
// ANY tenant_id (proven: POST canonical/facts with tenant_id
// 999999999999999999 returned 201). When INTERNAL_TENANT_ID is configured
// the guard is now enforced exactly like a verified Bearer principal.
const forwarded = new Headers(req.headers);
const internalTenant =
req.headers.get('x-0pt-internal-tenant') ?? process.env.INTERNAL_TENANT_ID;
if (internalTenant) {
forwarded.set('x-0pt-verified-tenant', internalTenant);
} else if (!internalTenantWarned) {
internalTenantWarned = true;
console.warn(
'[0pt-auth] INTERNAL_TENANT_ID is unset: internal-token requests are not tenant-scoped (set it in production)',
);
}
return NextResponse.next({ request: { headers: forwarded } });
}
if (_authDiagCount < 5) {