mirror of
https://github.com/yaklang/yaklang-chrome-extension.git
synced 2026-09-26 21:21:53 +08:00
refactor(authorization): simplify cross-instance testing
CI / Test and build (push) Canceled after 0s
CI / Test and build (push) Canceled after 0s
This commit is contained in:
@@ -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}`);
|
||||
},
|
||||
};
|
||||
@@ -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,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');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user