mirror of
https://github.com/yaklang/yaklang-chrome-extension.git
synced 2026-09-22 03:10:43 +08:00
263 lines
8.4 KiB
TypeScript
263 lines
8.4 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import type { BrowserAuthorizationWorkspace } from '../engine';
|
|
import {
|
|
authorizationWorkspaceUIReducer,
|
|
authorizationWorkspaceStage,
|
|
INITIAL_AUTHORIZATION_WORKSPACE_UI,
|
|
normalizePersistedAuthorizationWorkspaceUI,
|
|
persistedAuthorizationWorkspaceUI,
|
|
} from './workspace-reducer';
|
|
|
|
function fixtureWorkspace(): BrowserAuthorizationWorkspace {
|
|
return {
|
|
version: 1,
|
|
id: 'workspace-1',
|
|
engineInstanceId: 'engine-1',
|
|
mode: 'horizontal',
|
|
state: 'ready',
|
|
left: {
|
|
accountLabel: '账号 A',
|
|
origin: 'https://example.test',
|
|
target: { tabId: 11, frameId: 0, documentId: 'document-a' },
|
|
authentication: { status: 'authenticated', cookieCount: 1, storageEntryCount: 0 },
|
|
},
|
|
right: {
|
|
accountLabel: '账号 B',
|
|
origin: 'https://example.test',
|
|
target: { tabId: 22, frameId: 0, documentId: 'document-b' },
|
|
authentication: { status: 'authenticated', cookieCount: 1, storageEntryCount: 0 },
|
|
},
|
|
proof: {
|
|
level: 'strong',
|
|
sameOrigin: true,
|
|
cookieStoreRelation: 'different',
|
|
accountEvidenceRelation: 'different',
|
|
requestCredentialRelation: 'different',
|
|
refreshCheck: 'passed',
|
|
reasons: ['隔离成立'],
|
|
},
|
|
baselines: {},
|
|
baselinePair: {
|
|
state: 'waiting',
|
|
reasons: ['等待正常请求'],
|
|
resourceCandidates: [],
|
|
operationCandidates: [],
|
|
},
|
|
expiresAt: Date.now() + 60_000,
|
|
};
|
|
}
|
|
|
|
describe('authorization workspace UI reducer', () => {
|
|
it('initializes a renewed workspace and clears evidence tied to the old document', () => {
|
|
const workspace = { id: 'renewed' } as BrowserAuthorizationWorkspace;
|
|
const previous = {
|
|
...INITIAL_AUTHORIZATION_WORKSPACE_UI,
|
|
candidates: { left: [{ id: 'old-left' }], right: [{ id: 'old-right' }] } as never,
|
|
selected: { left: 'old-left', right: 'old-right' },
|
|
selectedPlanCandidateId: 'old-plan',
|
|
};
|
|
|
|
const next = authorizationWorkspaceUIReducer(previous, {
|
|
type: 'workspace.initialize',
|
|
workspace,
|
|
});
|
|
|
|
expect(next.workspace).toBe(workspace);
|
|
expect(next.candidates).toEqual({ left: [], right: [] });
|
|
expect(next.selected).toEqual({ left: '', right: '' });
|
|
expect(next.selectedPlanCandidateId).toBe('');
|
|
});
|
|
|
|
it('resets workflow evidence without discarding the selected identities', () => {
|
|
const previous = {
|
|
...INITIAL_AUTHORIZATION_WORKSPACE_UI,
|
|
leftTabId: 11,
|
|
rightTabId: 12,
|
|
workspace: { id: 'old' } as BrowserAuthorizationWorkspace,
|
|
capture: { left: { active: true } } as never,
|
|
};
|
|
const next = authorizationWorkspaceUIReducer(previous, { type: 'workspace.reset' });
|
|
|
|
expect(next.leftTabId).toBe(11);
|
|
expect(next.rightTabId).toBe(12);
|
|
expect(next.workspace).toBeUndefined();
|
|
expect(next.capture).toEqual({});
|
|
});
|
|
|
|
it('persists only durable workflow state', () => {
|
|
const value = persistedAuthorizationWorkspaceUI({
|
|
...INITIAL_AUTHORIZATION_WORKSPACE_UI,
|
|
inspection: { version: 1 } as never,
|
|
capture: { left: { active: true } } as never,
|
|
});
|
|
expect(value).not.toHaveProperty('inspection');
|
|
expect(value).not.toHaveProperty('capture');
|
|
});
|
|
|
|
it('fails closed when a restarted UI session contains a malformed workspace', () => {
|
|
const next = authorizationWorkspaceUIReducer(INITIAL_AUTHORIZATION_WORKSPACE_UI, {
|
|
type: 'hydrate',
|
|
value: {
|
|
mode: 'vertical',
|
|
leftTabId: 11,
|
|
rightTabId: 'not-a-tab',
|
|
leftLabel: '低权限账号',
|
|
workspace: { id: 'truncated-before-storage-write' },
|
|
candidates: { left: [null], right: { invalid: true } },
|
|
selected: null,
|
|
},
|
|
});
|
|
|
|
expect(next).toMatchObject({
|
|
mode: 'vertical',
|
|
leftTabId: 11,
|
|
leftLabel: '低权限账号',
|
|
workspace: undefined,
|
|
candidates: { left: [], right: [] },
|
|
selected: { left: '', right: '' },
|
|
});
|
|
expect(next.rightTabId).toBeUndefined();
|
|
});
|
|
|
|
it('normalizes a valid persisted workflow but drops invalid candidate entries', () => {
|
|
const workspace = {
|
|
...fixtureWorkspace(),
|
|
createdAt: Date.now(),
|
|
};
|
|
const normalized = normalizePersistedAuthorizationWorkspaceUI({
|
|
mode: 'horizontal',
|
|
leftTabId: 11,
|
|
rightTabId: 22,
|
|
leftLabel: '账号 A',
|
|
rightLabel: '账号 B',
|
|
workspace,
|
|
candidates: {
|
|
left: [{
|
|
id: 'left-request',
|
|
method: 'GET',
|
|
url: 'https://example.test/api/profile?id=1',
|
|
path: '/api/profile',
|
|
resourceType: 'xmlhttprequest',
|
|
startedAt: Date.now(),
|
|
eligible: true,
|
|
reasons: [],
|
|
}, { id: 'invalid-url', url: 'javascript:alert(1)' }],
|
|
right: [],
|
|
},
|
|
selected: { left: 'left-request', right: '' },
|
|
selectedPlanCandidateId: '',
|
|
canaryPaths: 'data.owner.id',
|
|
});
|
|
|
|
expect(normalized?.workspace?.id).toBe('workspace-1');
|
|
expect(normalized?.candidates?.left).toEqual([
|
|
expect.objectContaining({ id: 'left-request' }),
|
|
]);
|
|
expect(normalized?.selected?.left).toBe('left-request');
|
|
});
|
|
|
|
it('models the complete identity-to-evidence workflow without losing capture state', () => {
|
|
let current = INITIAL_AUTHORIZATION_WORKSPACE_UI;
|
|
expect(authorizationWorkspaceStage(current)).toBe('identity');
|
|
const initial = fixtureWorkspace();
|
|
current = authorizationWorkspaceUIReducer(current, {
|
|
type: 'workspace.initialize',
|
|
workspace: initial,
|
|
});
|
|
current = authorizationWorkspaceUIReducer(current, {
|
|
type: 'capture.replace',
|
|
capture: {
|
|
left: { active: true, count: 1 } as never,
|
|
right: { active: true, count: 1 } as never,
|
|
},
|
|
});
|
|
expect(authorizationWorkspaceStage(current)).toBe('normal-requests');
|
|
|
|
const baseline = {
|
|
id: 'baseline',
|
|
networkRequestId: 'request',
|
|
request: {
|
|
method: 'GET',
|
|
url: 'https://example.test/api/profile?id=1',
|
|
path: '/api/profile',
|
|
contentType: 'application/json',
|
|
actionFingerprint: 'fingerprint',
|
|
},
|
|
};
|
|
const bound = {
|
|
...initial,
|
|
baselines: { left: { ...baseline, id: 'left' }, right: { ...baseline, id: 'right' } },
|
|
baselinePair: {
|
|
state: 'matched' as const,
|
|
reasons: ['同类请求'],
|
|
resourceCandidates: [{
|
|
id: 'resource-id',
|
|
source: 'wire' as const,
|
|
location: 'query' as const,
|
|
path: 'query.id',
|
|
category: 'identifier',
|
|
confidence: 'high' as const,
|
|
requiresLogicalBinding: false,
|
|
reasons: ['A/B 值不同'],
|
|
}],
|
|
operationCandidates: [],
|
|
},
|
|
};
|
|
current = authorizationWorkspaceUIReducer(current, {
|
|
type: 'baselines.loaded',
|
|
candidates: {
|
|
left: [{ id: 'left-request' }] as never,
|
|
right: [{ id: 'right-request' }] as never,
|
|
},
|
|
selected: { left: 'left-request', right: 'right-request' },
|
|
});
|
|
current = authorizationWorkspaceUIReducer(current, {
|
|
type: 'baselines.bound',
|
|
workspace: bound,
|
|
selectedPlanCandidateId: 'resource-id',
|
|
});
|
|
expect(authorizationWorkspaceStage(current)).toBe('plan');
|
|
|
|
const planned = {
|
|
...bound,
|
|
plan: {
|
|
id: 'plan-1',
|
|
mode: 'horizontal' as const,
|
|
candidateId: 'resource-id',
|
|
state: 'ready' as const,
|
|
selector: { source: 'wire' as const, location: 'query' as const, path: 'query.id' },
|
|
cases: [],
|
|
requestBudget: 4,
|
|
requiresDynamicRebuild: false,
|
|
reasons: ['固定四项矩阵'],
|
|
},
|
|
};
|
|
current = authorizationWorkspaceUIReducer(current, {
|
|
type: 'workspace.updated',
|
|
workspace: planned,
|
|
});
|
|
expect(authorizationWorkspaceStage(current)).toBe('execution');
|
|
|
|
current = authorizationWorkspaceUIReducer(current, {
|
|
type: 'workspace.updated',
|
|
workspace: {
|
|
...planned,
|
|
execution: {
|
|
id: 'execution-1',
|
|
state: 'completed',
|
|
verdict: 'protected',
|
|
confidence: 'high',
|
|
requestCount: 4,
|
|
cases: [],
|
|
evidence: [],
|
|
evidenceAvailable: true,
|
|
reasons: ['交叉访问均被拒绝'],
|
|
},
|
|
},
|
|
});
|
|
expect(authorizationWorkspaceStage(current)).toBe('evidence');
|
|
expect(current.capture.left?.active).toBe(true);
|
|
expect(persistedAuthorizationWorkspaceUI(current)).not.toHaveProperty('capture');
|
|
});
|
|
});
|