mirror of
https://github.com/yaklang/yaklang-chrome-extension.git
synced 2026-09-24 04:01:53 +08:00
202 lines
7.3 KiB
TypeScript
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('不能选择同一个标签页');
|
|
});
|
|
});
|