Files
yaklang-chrome-extension/src/features/authorization-testing/isolation.test.ts
T

202 lines
7.3 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import type { ActiveTabInfo, BrowserIsolationContext } from '@/types/models';
import {
activeTabInfo,
applyTabLocalAuthenticationEvidence,
buildIsolationProof,
isolationContextForTab,
type IsolationCookieStore,
type IsolationTabDescriptor,
} from './isolation';
function tab(id: number, incognito: boolean, url = 'https://example.test/account'): IsolationTabDescriptor {
return { id, windowId: incognito ? 2 : 1, title: incognito ? 'B' : 'A', url, incognito };
}
function asActive(
descriptor: IsolationTabDescriptor,
context: BrowserIsolationContext,
): ActiveTabInfo {
return activeTabInfo(descriptor, context);
}
describe('browser identity isolation', () => {
it('proves a Chromium regular/incognito pair with different opaque Cookie Stores', () => {
const stores: IsolationCookieStore[] = [
{ id: 'opaque-regular', tabIds: [1, 3] },
{ id: 'opaque-private', tabIds: [2] },
];
const leftDescriptor = tab(1, false);
const rightDescriptor = tab(2, true);
const leftContext = isolationContextForTab(leftDescriptor, stores, 'chromium');
const rightContext = isolationContextForTab(rightDescriptor, stores, 'chromium');
const proof = buildIsolationProof(
asActive(leftDescriptor, leftContext),
asActive(rightDescriptor, rightContext),
[leftContext, rightContext],
1_000,
'proof-1',
);
expect(leftContext).toEqual(expect.objectContaining({
kind: 'browser-profile',
cookieStoreId: 'opaque-regular',
tabIds: [1, 3],
}));
expect(rightContext).toEqual(expect.objectContaining({
kind: 'chrome-incognito-store',
cookieStoreId: 'opaque-private',
incognito: true,
}));
expect(proof).toEqual(expect.objectContaining({
id: 'proof-1',
level: 'strong',
cookieStoreRelation: 'different',
sameOrigin: true,
refreshCheck: 'not-required',
}));
expect(proof.expiresAt).toBe(1_000 + 30 * 60_000);
});
it('fails closed when two ordinary tabs share one Cookie Store', () => {
const stores: IsolationCookieStore[] = [{ id: 'shared-store', tabIds: [1, 2] }];
const leftDescriptor = tab(1, false);
const rightDescriptor = tab(2, false);
const leftContext = isolationContextForTab(leftDescriptor, stores, 'chromium');
const rightContext = isolationContextForTab(rightDescriptor, stores, 'chromium');
const proof = buildIsolationProof(
asActive(leftDescriptor, leftContext),
asActive(rightDescriptor, rightContext),
[leftContext, rightContext],
1_000,
'proof-shared',
);
expect(leftContext.contextId).toBe(rightContext.contextId);
expect(proof.level).toBe('none');
expect(proof.cookieStoreRelation).toBe('same');
expect(proof.reasons.join(' ')).toContain('不同 tabId 不代表不同登录态');
});
it('upgrades same-store tabs only when authentication is sessionStorage-local and distinct', () => {
const stores: IsolationCookieStore[] = [{ id: 'shared-store', tabIds: [1, 2] }];
const leftDescriptor = tab(1, false);
const rightDescriptor = tab(2, false);
const leftContext = isolationContextForTab(leftDescriptor, stores, 'chromium');
const rightContext = isolationContextForTab(rightDescriptor, stores, 'chromium');
const proof = buildIsolationProof(
asActive(leftDescriptor, leftContext),
asActive(rightDescriptor, rightContext),
[leftContext, rightContext],
1_000,
'proof-tab-local',
);
const upgraded = applyTabLocalAuthenticationEvidence(
proof,
{
origin: 'https://example.test',
status: 'authenticated',
authCookieNames: [],
authLocalStorageKeys: [],
authSessionStorageKeys: ['access_token'],
fingerprint: 'left-fingerprint',
},
{
origin: 'https://example.test',
status: 'authenticated',
authCookieNames: [],
authLocalStorageKeys: [],
authSessionStorageKeys: ['access_token'],
fingerprint: 'right-fingerprint',
},
);
expect(upgraded.level).toBe('conditional');
expect(upgraded.accountEvidenceRelation).toBe('different');
expect(upgraded.requestCredentialRelation).toBe('unknown');
expect(upgraded.refreshCheck).toBe('passed');
});
it('keeps same-store tabs blocked when shared Cookie or localStorage carries authentication', () => {
const stores: IsolationCookieStore[] = [{ id: 'shared-store', tabIds: [1, 2] }];
const leftDescriptor = tab(1, false);
const rightDescriptor = tab(2, false);
const leftContext = isolationContextForTab(leftDescriptor, stores, 'chromium');
const rightContext = isolationContextForTab(rightDescriptor, stores, 'chromium');
const proof = buildIsolationProof(
asActive(leftDescriptor, leftContext),
asActive(rightDescriptor, rightContext),
[leftContext, rightContext],
1_000,
'proof-shared-auth',
);
const shared = {
origin: 'https://example.test',
status: 'authenticated' as const,
authCookieNames: ['session'],
authLocalStorageKeys: ['auth'],
authSessionStorageKeys: ['access_token'],
};
const blocked = applyTabLocalAuthenticationEvidence(
proof,
{ ...shared, fingerprint: 'left' },
{ ...shared, fingerprint: 'right' },
);
expect(blocked.level).toBe('none');
expect(blocked.reasons.join(' ')).toContain('共享 Cookie Store');
});
it('recognizes Firefox Container identities without hard-coding tab IDs', () => {
const stores: IsolationCookieStore[] = [
{ id: 'firefox-container-12', tabIds: [7] },
{ id: 'firefox-container-29', tabIds: [8] },
];
const leftDescriptor = { ...tab(7, false), cookieStoreId: 'firefox-container-12' };
const rightDescriptor = { ...tab(8, false), cookieStoreId: 'firefox-container-29' };
const leftContext = isolationContextForTab(leftDescriptor, stores, 'firefox', [{
cookieStoreId: 'firefox-container-12',
name: 'Yakit 身份 A',
color: 'blue',
icon: 'fingerprint',
managed: true,
}]);
const rightContext = isolationContextForTab(rightDescriptor, stores, 'firefox');
const proof = buildIsolationProof(
asActive(leftDescriptor, leftContext),
asActive(rightDescriptor, rightContext),
[leftContext, rightContext],
1_000,
'proof-container',
);
expect(leftContext.kind).toBe('firefox-container');
expect(leftContext.containerId).toBe('firefox-container-12');
expect(leftContext).toEqual(expect.objectContaining({
containerName: 'Yakit 身份 A',
containerColor: 'blue',
managed: true,
}));
expect(proof.level).toBe('strong');
});
it('does not invent isolation when Cookie Store resolution is unavailable', () => {
const descriptor = tab(9, false);
const context = isolationContextForTab(descriptor, [], 'chromium');
expect(context.level).toBe('none');
expect(context.cookieStoreId).toBeUndefined();
expect(context.guarantees.cookies).toBe('unknown');
});
it('rejects assigning the same page to both identity slots', () => {
const descriptor = tab(1, false);
const context = isolationContextForTab(descriptor, [{ id: 'store', tabIds: [1] }], 'chromium');
const active = asActive(descriptor, context);
expect(() => buildIsolationProof(active, active, [context])).toThrow('不能选择同一个标签页');
});
});