mirror of
https://github.com/yaklang/yaklang-chrome-extension.git
synced 2026-09-22 03:10:43 +08:00
121 lines
6.0 KiB
TypeScript
121 lines
6.0 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import {
|
|
BRIDGE_CAPABILITIES,
|
|
canonicalCapabilityCatalogPayload,
|
|
capabilityBaseScope,
|
|
capabilityVisibleToAgent,
|
|
getBridgeCapabilityCatalog,
|
|
} from './capabilities';
|
|
|
|
describe('versioned Bridge capability catalog', () => {
|
|
it('derives every advertised method from one schema-backed catalog', async () => {
|
|
const catalog = await getBridgeCapabilityCatalog();
|
|
expect(catalog.version).toBe(1);
|
|
expect(catalog.schemaDialect).toBe('http://json-schema.org/draft-07/schema#');
|
|
expect(catalog.hash).toMatch(/^[a-f0-9]{64}$/);
|
|
expect(catalog.capabilities.map((capability) => capability.method)).toEqual(BRIDGE_CAPABILITIES);
|
|
expect(new Set(catalog.capabilities.map((capability) => capability.method)).size).toBe(BRIDGE_CAPABILITIES.length);
|
|
expect(new TextEncoder().encode(JSON.stringify(catalog)).byteLength).toBeLessThan(1024 * 1024);
|
|
});
|
|
|
|
it('publishes the real validation constraints and target semantics', async () => {
|
|
const catalog = await getBridgeCapabilityCatalog();
|
|
const evalCapability = catalog.capabilities.find((capability) => capability.method === 'browser.eval');
|
|
expect(evalCapability).toMatchObject({
|
|
domain: 'page',
|
|
access: 'dangerous',
|
|
scopes: ['browser.page.eval.expression'],
|
|
targetMode: 'document',
|
|
defaultTimeoutMs: 60_000,
|
|
});
|
|
expect(evalCapability?.conditionalScopes).toContainEqual({
|
|
scope: 'browser.page.eval.program',
|
|
when: 'mode=program',
|
|
});
|
|
expect(JSON.stringify(evalCapability?.paramsSchema)).toContain('"mode"');
|
|
expect(JSON.stringify(evalCapability?.paramsSchema)).toContain('"program"');
|
|
expect(capabilityBaseScope('browser.profile.validate')).toBe('browser.transform.execute');
|
|
expect(catalog.capabilities.find((capability) => capability.method === 'browser.thumbnail')).toMatchObject({
|
|
agentVisible: false,
|
|
});
|
|
expect(catalog.capabilities.find((capability) => capability.method === 'browser.handoff.presentation.get')).toMatchObject({
|
|
agentVisible: false,
|
|
});
|
|
expect(catalog.capabilities.find((capability) => capability.method === 'browser.handoff.focus')).toMatchObject({
|
|
agentVisible: false,
|
|
});
|
|
expect(catalog.capabilities.find((capability) => capability.method === 'browser.handoff.resolve')).toMatchObject({
|
|
agentVisible: false,
|
|
});
|
|
expect(capabilityVisibleToAgent('browser.handoff.presentation.get')).toBe(false);
|
|
expect(capabilityVisibleToAgent('browser.handoff.focus')).toBe(false);
|
|
expect(capabilityVisibleToAgent('browser.handoff.resolve')).toBe(false);
|
|
expect(capabilityVisibleToAgent('browser.thumbnail')).toBe(false);
|
|
expect(capabilityVisibleToAgent('browser.context')).toBe(true);
|
|
expect(catalog.capabilities.find((capability) => capability.method === 'browser.transform.profile.save')).toBeUndefined();
|
|
expect(catalog.capabilities.find((capability) => capability.method === 'proxy.switch')?.summary)
|
|
.toContain('不会生成、启用或执行 Transform Profile');
|
|
expect(catalog.capabilities.find((capability) => capability.method === 'browser.profile.validate')?.summary)
|
|
.toContain('用户在插件本地确认保存');
|
|
expect(catalog.capabilities.find((capability) => capability.method === 'browser.transform.recovery.capture')).toMatchObject({
|
|
access: 'dangerous',
|
|
scopes: ['browser.transform.manage', 'browser.debugger.control', 'browser.callable.execute'],
|
|
targetMode: 'profile',
|
|
});
|
|
expect(capabilityBaseScope('browser.transform.recovery.validate')).toBe('browser.transform.execute');
|
|
expect(catalog.capabilities.find((capability) => capability.method === 'browser.isolation.proof')).toMatchObject({
|
|
domain: 'isolation',
|
|
access: 'sensitive-read',
|
|
scopes: ['browser.isolation.read', 'browser.cookies.read', 'browser.storage.read'],
|
|
targetMode: 'none',
|
|
});
|
|
expect(catalog.capabilities.find((capability) => capability.method === 'browser.isolation.incognito.open')).toMatchObject({
|
|
domain: 'isolation',
|
|
access: 'dangerous',
|
|
scopes: ['browser.isolation.manage'],
|
|
});
|
|
expect(catalog.capabilities.find((capability) => capability.method === 'browser.isolation.container.open')).toBeUndefined();
|
|
expect(capabilityBaseScope('missing.capability')).toBeUndefined();
|
|
});
|
|
|
|
it('keeps canonical payloads stable and excludes the transmitted hash', async () => {
|
|
const catalog = await getBridgeCapabilityCatalog();
|
|
const payload = canonicalCapabilityCatalogPayload({
|
|
version: catalog.version,
|
|
schemaDialect: catalog.schemaDialect,
|
|
capabilities: catalog.capabilities,
|
|
});
|
|
expect(payload).not.toContain(catalog.hash);
|
|
expect(payload).toBe(canonicalCapabilityCatalogPayload({
|
|
capabilities: catalog.capabilities,
|
|
schemaDialect: catalog.schemaDialect,
|
|
version: catalog.version,
|
|
}));
|
|
});
|
|
|
|
it('matches the Go canonical JSON and SHA-256 test vector', async () => {
|
|
const payload = canonicalCapabilityCatalogPayload({
|
|
version: 1,
|
|
schemaDialect: 'http://json-schema.org/draft-07/schema#',
|
|
capabilities: [{
|
|
method: 'browser.tabs',
|
|
domain: 'page',
|
|
access: 'read',
|
|
summary: 'List tabs',
|
|
scopes: ['browser.tabs.read'],
|
|
targetMode: 'none',
|
|
defaultTimeoutMs: 20_000,
|
|
paramsSchema: { type: 'object', properties: {}, additionalProperties: false },
|
|
}],
|
|
});
|
|
expect(payload).toBe(
|
|
'{"capabilities":[{"access":"read","defaultTimeoutMs":20000,"domain":"page","method":"browser.tabs","paramsSchema":{"additionalProperties":false,"properties":{},"type":"object"},"scopes":["browser.tabs.read"],"summary":"List tabs","targetMode":"none"}],"schemaDialect":"http://json-schema.org/draft-07/schema#","version":1}',
|
|
);
|
|
const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(payload));
|
|
const hash = [...new Uint8Array(digest)]
|
|
.map((byte) => byte.toString(16).padStart(2, '0'))
|
|
.join('');
|
|
expect(hash).toBe('82bc1210338e773137b95359ca0b39c0443bae3d9fdae33f97942cf82119006f');
|
|
});
|
|
});
|