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
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:
+19
-1
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user