refactor(authorization): simplify cross-instance testing
CI / Test and build (push) Canceled after 0s

This commit is contained in:
go0p
2026-09-04 16:43:11 +08:00
parent 71a14eb17a
commit dd61541c3e
36 changed files with 475 additions and 9178 deletions
@@ -1,6 +1,5 @@
export type CapabilityDomainId =
| 'navigation-isolation'
| 'authorization'
| 'handoff'
| 'network'
| 'recording-callable-debugger'
@@ -32,11 +31,6 @@ export const NAVIGATION_CAPABILITY_DOMAIN = exactMethods('navigation-isolation',
'browser.isolation.container.remove',
]);
export const AUTHORIZATION_CAPABILITY_DOMAIN: CapabilityDomainDefinition = {
id: 'authorization',
owns: (method) => method.startsWith('browser.authorization.'),
};
export const HANDOFF_CAPABILITY_DOMAIN: CapabilityDomainDefinition = {
id: 'handoff',
owns: (method) => method.startsWith('browser.handoff.'),
@@ -78,7 +72,6 @@ export const PROXY_CAPABILITY_DOMAIN = exactMethods('proxy', [
export const CAPABILITY_DOMAINS: readonly CapabilityDomainDefinition[] = [
NAVIGATION_CAPABILITY_DOMAIN,
AUTHORIZATION_CAPABILITY_DOMAIN,
HANDOFF_CAPABILITY_DOMAIN,
NETWORK_CAPABILITY_DOMAIN,
RECORDING_CAPABILITY_DOMAIN,
@@ -1,153 +0,0 @@
import type { BrowserAuthorizationResourceSelector } from '@/types/models';
import type { CapabilityDomainHandler } from '../capability-context';
import { allowedTarget, requireScope } from '../capability-context';
import {
captureAuthContextHandle,
getAuthContextHandle,
} from '@/features/authorization-testing/auth-context';
import {
captureAuthContextAttestation,
getAuthContextAttestation,
} from '@/features/authorization-testing/auth-attestation';
import {
bindAuthorizationBaselineLogicalRequest,
captureAuthorizationBaseline,
compileAuthorizationBaseline,
compileAuthorizationBaselinePacket,
compileAuthorizationBaselineWithTransform,
getAuthorizationBaseline,
inspectAuthorizationBaselineTransform,
listAuthorizationBaselineCandidates,
readAuthorizationBaselineResource,
} from '@/features/authorization-testing/baseline';
import { AUTHORIZATION_CAPABILITY_DOMAIN } from '../capability-domains';
function requireAuthorizationContextScopes(
grant: Parameters<typeof requireScope>[0],
): void {
requireScope(grant, 'browser.cookies.read');
requireScope(grant, 'browser.storage.read');
}
function requireAuthorizationBaselineScopes(
grant: Parameters<typeof requireScope>[0],
): void {
requireScope(grant, 'browser.isolation.read');
requireAuthorizationContextScopes(grant);
}
export const authorizationCapabilityHandler: CapabilityDomainHandler = {
...AUTHORIZATION_CAPABILITY_DOMAIN,
async handle({ method, input, grant }) {
if (method === 'browser.authorization.context.capture') {
requireAuthorizationContextScopes(grant);
return captureAuthContextHandle({
slotId: input.slotId === 'right' ? 'right' : 'left',
accountLabel: typeof input.accountLabel === 'string' ? input.accountLabel : undefined,
isolationProofId: String(input.isolationProofId || ''),
target: await allowedTarget(grant, input),
grantId: grant.id,
grantExpiresAt: grant.expiresAt,
});
}
if (method === 'browser.authorization.context.get') {
requireAuthorizationContextScopes(grant);
return getAuthContextHandle(String(input.id || ''), grant.id);
}
if (method === 'browser.authorization.context.attest') {
requireAuthorizationContextScopes(grant);
return captureAuthContextAttestation({
target: await allowedTarget(grant, input),
grantId: grant.id,
grantExpiresAt: grant.expiresAt,
});
}
if (method === 'browser.authorization.context.attestation.get') {
requireAuthorizationContextScopes(grant);
return getAuthContextAttestation(String(input.id || ''), grant.id);
}
requireAuthorizationBaselineScopes(grant);
if (method === 'browser.authorization.baseline.capture') {
return captureAuthorizationBaseline({
target: await allowedTarget(grant, input),
grantId: grant.id,
authContextKind: input.authContextKind === 'attestation' ? 'attestation' : 'handle',
authContextId: String(input.authContextId || ''),
networkRequestId: String(input.networkRequestId || ''),
comparisonKey: String(input.comparisonKey || ''),
});
}
if (method === 'browser.authorization.baseline.candidates') {
return listAuthorizationBaselineCandidates({
target: await allowedTarget(grant, input),
grantId: grant.id,
authContextKind: input.authContextKind === 'attestation' ? 'attestation' : 'handle',
authContextId: String(input.authContextId || ''),
limit: typeof input.limit === 'number' ? input.limit : 100,
});
}
if (method === 'browser.authorization.baseline.get') {
return getAuthorizationBaseline(String(input.id || ''), grant.id);
}
if (method === 'browser.authorization.baseline.logical.bind') {
requireScope(grant, 'browser.network.sensitive.read');
requireScope(grant, 'browser.transform.execute');
return bindAuthorizationBaselineLogicalRequest({
id: String(input.id || ''),
grantId: grant.id,
profileId: String(input.profileId || ''),
comparisonKey: String(input.comparisonKey || ''),
});
}
if (method === 'browser.authorization.baseline.resource.get') {
return readAuthorizationBaselineResource({
id: String(input.id || ''),
grantId: grant.id,
selector: input.selector as BrowserAuthorizationResourceSelector,
});
}
if (method === 'browser.authorization.baseline.compile') {
requireScope(grant, 'browser.network.sensitive.read');
return compileAuthorizationBaseline({
id: String(input.id || ''),
grantId: grant.id,
selector: input.selector as BrowserAuthorizationResourceSelector,
replacement: input.replacement as Parameters<typeof compileAuthorizationBaseline>[0]['replacement'],
comparisonKey: String(input.comparisonKey || ''),
});
}
if (method === 'browser.authorization.baseline.packet.compile') {
requireScope(grant, 'browser.network.replay');
requireScope(grant, 'browser.network.sensitive.read');
return compileAuthorizationBaselinePacket({
id: String(input.id || ''),
grantId: grant.id,
});
}
if (method === 'browser.authorization.baseline.transform.inspect') {
requireScope(grant, 'browser.network.sensitive.read');
requireScope(grant, 'browser.transform.read');
return inspectAuthorizationBaselineTransform({
id: String(input.id || ''),
grantId: grant.id,
profileId: String(input.profileId || ''),
});
}
if (method === 'browser.authorization.baseline.transform.compile') {
requireScope(grant, 'browser.network.replay');
requireScope(grant, 'browser.network.sensitive.read');
requireScope(grant, 'browser.transform.execute');
return compileAuthorizationBaselineWithTransform({
id: String(input.id || ''),
grantId: grant.id,
selector: input.selector as BrowserAuthorizationResourceSelector,
replacement: input.replacement as Parameters<typeof compileAuthorizationBaselineWithTransform>[0]['replacement'],
comparisonKey: String(input.comparisonKey || ''),
profileId: String(input.profileId || ''),
bindingFingerprint: String(input.bindingFingerprint || ''),
});
}
throw new Error(`授权能力没有实现: ${method}`);
},
};
-2
View File
@@ -3,7 +3,6 @@ import type {
CapabilityRouteContext,
} from './capability-context';
import { navigationCapabilityHandler } from './capability-handlers/navigation';
import { authorizationCapabilityHandler } from './capability-handlers/authorization';
import { handoffCapabilityHandler } from './capability-handlers/handoff';
import { networkCapabilityHandler } from './capability-handlers/network';
import { recordingCapabilityHandler } from './capability-handlers/recording';
@@ -13,7 +12,6 @@ import { proxyCapabilityHandler } from './capability-handlers/proxy';
export const CAPABILITY_HANDLERS: readonly CapabilityDomainHandler[] = [
navigationCapabilityHandler,
authorizationCapabilityHandler,
handoffCapabilityHandler,
networkCapabilityHandler,
recordingCapabilityHandler,
+1 -31
View File
@@ -1,7 +1,7 @@
import { describe, expect, it } from 'vitest';
import { CONTROL_CAPABILITY_SCOPES } from '@/protocol/capabilities';
import type { ActiveTabInfo, ExtensionState } from '@/types/models';
import { authorizationShareGrantInput, gatewayShareActive, gatewayShareGrantInput } from './gateway-share';
import { gatewayShareActive, gatewayShareGrantInput } from './gateway-share';
const NOW = 1_000_000;
@@ -84,34 +84,4 @@ describe('gateway quick share', () => {
expect(gatewayShareActive(grant, { ...tab, url: 'https://elsewhere.example.test/' }, NOW)).toBe(false);
});
it('creates a focused two-tab authorization grant without retaining unrelated targets', () => {
const current = state();
current.activeGrant = {
id: 'grant',
taskId: 'existing-task',
createdAt: NOW - 1_000,
expiresAt: NOW + 45 * 60_000,
scopes: ['browser.tabs.read'],
targets: [{
tabId: 99,
frameId: 0,
documentId: 'unrelated',
isolationContextId: 'unrelated',
origin: 'https://other.example.test',
grantedUrl: 'https://other.example.test',
title: 'Unrelated',
}],
};
const right = { ...tab, id: 8, incognito: true };
const input = authorizationShareGrantInput(current, [tab, right], NOW);
expect(input.targets).toEqual([
{ tabId: 7, frameId: 0 },
{ tabId: 8, frameId: 0 },
]);
expect(input.scopes).toEqual(expect.arrayContaining(CONTROL_CAPABILITY_SCOPES));
expect(input.durationMinutes).toBe(45);
expect(input.taskId).toBe('existing-task');
});
});
-21
View File
@@ -63,24 +63,3 @@ export function gatewayShareGrantInput(
taskId: active?.taskId,
};
}
export function authorizationShareGrantInput(
state: ExtensionState,
tabs: [ActiveTabInfo, ActiveTabInfo],
now = Date.now(),
): GrantCreateInput {
const active = state.activeGrant && state.activeGrant.expiresAt > now
? state.activeGrant
: undefined;
const scopes = new Set<CapabilityScope>(active?.scopes || []);
CONTROL_CAPABILITY_SCOPES.forEach((scope) => scopes.add(scope));
const remainingMinutes = active
? Math.ceil((active.expiresAt - now) / 60_000)
: 0;
return {
targets: tabs.map((item) => ({ tabId: item.id, frameId: 0 })),
scopes: [...scopes],
durationMinutes: Math.max(DEFAULT_GATEWAY_GRANT_MINUTES, remainingMinutes),
taskId: active?.taskId,
};
}