// @vitest-environment node import { createHash, generateKeyPairSync, sign } from 'node:crypto'; import { describe, expect, it } from 'vitest'; import { CodingPluginManifestError, parseCodingPluginManifest, } from '../../electron/coding-plugins/manifest'; import { buildPluginReleaseDescriptor, isMakeLoreVersionCompatible, parsePluginReleaseDescriptor, serializePluginReleaseDescriptor, } from '../../electron/coding-plugins/release-descriptor'; import { createPluginSignatureVerifier, verifyPluginReleaseSignature, } from '../../electron/coding-plugins/signature-verifier'; import { CODE_OWNED_PLUGIN_SIGNING_KEYS, createCodeOwnedPluginTrustStore, } from '../../electron/coding-plugins/trusted-keys'; const PACKAGE_ROOT = '/trusted/plugin'; const ROOT = { $schema: 'https://agent-plugins.org/schemas/1.0.0/plugin.schema.json', name: 'makelore.example', version: '1.2.0', description: 'A declarative example Skill.', author: { name: 'MakeLore' }, extensions: { 'com.makelore': { capabilityManifest: './com.makelore/capability.json' } }, }; const SKILL_ONLY_CAPABILITY = { schemaVersion: 2, pluginId: 'makelore.example', contractVersion: 1, scope: 'project', runtime: { kind: 'skill_only' }, skills: [{ id: 'example-skill', entry: '../skills/example-skill/SKILL.md', grants: [] }], tools: [], }; const HOSTED_CAPABILITY = { schemaVersion: 2, pluginId: 'makelore.example', contractVersion: 1, scope: 'project', runtime: { kind: 'platform_hosted', protocol: 'makelore-hosted.v1' }, skills: [{ id: 'example-skill', entry: '../skills/example-skill/SKILL.md', grants: ['example.generate'] }], tools: [{ name: 'example_generate', label: 'Generate example', description: 'Generate one example.', capabilityId: 'example.generate', operation: 'generate', roles: ['parent'], mutation: 'write', projectWriteLease: false, permissions: ['hosted.example.generate'], executionMode: 'synchronous', inputSchema: { type: 'object', additionalProperties: false, required: ['prompt'], properties: { prompt: { type: 'string', minLength: 1, maxLength: 4000 } }, }, outputSchema: { type: 'object', additionalProperties: false, required: ['text'], properties: { text: { type: 'string', maxLength: 20000 } }, }, }], }; function parse(capability: Record) { return parseCodingPluginManifest(ROOT, capability, { packageRoot: PACKAGE_ROOT, rootManifestPath: `${PACKAGE_ROOT}/plugin.json`, capabilityManifestPath: `${PACKAGE_ROOT}/com.makelore/capability.json`, runtimeKind: capability === SKILL_ONLY_CAPABILITY ? 'skill_only' : 'platform_hosted', acquisitionMode: 'user_acquired', releaseId: 'release-1', provenance: { source: 'marketplace', packageRoot: PACKAGE_ROOT }, }); } describe('Marketplace Release A package contract', () => { it('parses immutable skill-only schema 2 with trusted provenance', () => { const definition = parse(SKILL_ONLY_CAPABILITY); expect(definition).toMatchObject({ id: 'makelore.example', version: '1.2.0', runtimeKind: 'skill_only', acquisitionMode: 'user_acquired', releaseId: 'release-1', provenance: { source: 'marketplace', packageRoot: PACKAGE_ROOT }, skills: [{ id: 'example-skill', grants: [] }], tools: [], operations: [], }); expect(Object.isFrozen(definition)).toBe(true); expect(Object.isFrozen(definition.skills)).toBe(true); expect(Object.isFrozen(definition.provenance)).toBe(true); }); it('parses declarative hosted schema 2 and preserves closed schemas', () => { const definition = parse(HOSTED_CAPABILITY); expect(definition).toMatchObject({ runtimeKind: 'platform_hosted', acquisitionMode: 'user_acquired', requiresBackend: true, tools: [{ executionMode: 'synchronous', outputSchema: HOSTED_CAPABILITY.tools[0]?.outputSchema, projectWriteLease: false, }], operations: [{ capabilityId: 'example.generate', operation: 'generate', toolName: 'example_generate' }], }); expect(Object.isFrozen(definition.tools[0]?.outputSchema)).toBe(true); }); it('accepts only the canonical asynchronous job execution mode', () => { const job = structuredClone(HOSTED_CAPABILITY) as Record; (job.tools as Array>)[0]!.executionMode = 'job'; expect(parse(job).tools[0]?.executionMode).toBe('job'); const accepted = structuredClone(HOSTED_CAPABILITY) as Record; (accepted.tools as Array>)[0]!.executionMode = 'accepted'; expect(() => parse(accepted)).toThrow(CodingPluginManifestError); }); it('requires the exact capability manifest path even for direct parsing', () => { const root = structuredClone(ROOT) as Record; const extensions = root.extensions as Record; const makeLore = extensions['com.makelore'] as Record; makeLore.capabilityManifest = './com.makelore/alternate.json'; expect(() => parseCodingPluginManifest(root, SKILL_ONLY_CAPABILITY, { packageRoot: PACKAGE_ROOT, rootManifestPath: `${PACKAGE_ROOT}/plugin.json`, capabilityManifestPath: `${PACKAGE_ROOT}/com.makelore/capability.json`, })).toThrow(CodingPluginManifestError); }); it('allows a hosted write tool to request the Main-owned project write lease', () => { const source = structuredClone(HOSTED_CAPABILITY) as Record; (source.tools as Array>)[0]!.projectWriteLease = true; expect(parse(source).tools[0]?.projectWriteLease).toBe(true); }); it.each([ ['unknown capability field', (value: Record) => { value.unknown = true; }], ['unknown runtime field', (value: Record) => { (value.runtime as Record).unknown = true; }], ['schema-2 adapter privilege', (value: Record) => { value.adapterId = 'data-service'; }], ['schema-2 local surface privilege', (value: Record) => { value.surfaces = {}; }], ['skill-only grants', (value: Record) => { (value.skills as Array>)[0]!.grants = ['example.generate']; }], ['skill-only tools', (value: Record) => { value.tools = [{ ...HOSTED_CAPABILITY.tools[0] }]; }], ['hosted local permission', (value: Record) => { (value.tools as Array>)[0]!.permissions = ['project.data.read']; }], ['hosted foreign permission namespace', (value: Record) => { (value.tools as Array>)[0]!.permissions = ['hosted.makelore.example.generate']; }], ['hosted read-only project lease', (value: Record) => { const tool = (value.tools as Array>)[0]!; tool.mutation = 'read'; tool.projectWriteLease = true; }], ['unbounded schema', (value: Record) => { const tool = (value.tools as Array>)[0]!; (tool.inputSchema as Record).properties = { prompt: { type: 'string' }, }; }], ['implicit required schema', (value: Record) => { const tool = (value.tools as Array>)[0]!; delete (tool.inputSchema as Record).required; }], ])('rejects %s', (_label, mutate) => { const source = structuredClone( _label === 'skill-only grants' || _label === 'skill-only tools' ? SKILL_ONLY_CAPABILITY : HOSTED_CAPABILITY, ) as Record; mutate(source); expect(() => parse(source)).toThrow(CodingPluginManifestError); }); it('keeps the bundled schema-1 definition byte-compatible', async () => { const { readFile } = await import('node:fs/promises'); const path = await import('node:path'); const packageRoot = path.resolve('resources/coding-plugins/data-service'); const root = JSON.parse(await readFile(path.join(packageRoot, 'plugin.json'), 'utf8')) as Record; const capability = JSON.parse(await readFile(path.join(packageRoot, 'com.makelore/capability.json'), 'utf8')) as Record; const definition = parseCodingPluginManifest(root, capability, { packageRoot, capabilityManifestPath: path.join(packageRoot, 'com.makelore/capability.json'), }); expect(definition.id).toBe('makelore.data-service'); expect(definition.runtimeKind).toBe('bundled_typed'); expect(definition.acquisitionMode).toBe('system_included'); expect(definition.tools).toHaveLength(10); expect(definition.operations).toHaveLength(14); }); it('serializes and parses the fixed descriptor bytes in field order', () => { const descriptor = buildPluginReleaseDescriptor({ pluginId: 'makelore.example', version: '1.2.0', packageSchemaVersion: 2, contractVersion: 1, minMakeloreVersion: '1.0.0', maxMakeloreVersion: null, artifact: { sha256: 'a'.repeat(64), sizeBytes: 1234 }, }); const bytes = serializePluginReleaseDescriptor(descriptor); expect(Buffer.from(bytes).toString('utf8')).toBe( '{"schema":"makelore-plugin-release.v1","plugin_id":"makelore.example","version":"1.2.0","package_schema_version":2,"contract_version":1,"min_makelore_version":"1.0.0","max_makelore_version":null,"artifact":{"sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","size_bytes":1234}}', ); expect(parsePluginReleaseDescriptor(bytes)).toEqual(descriptor); expect(() => parsePluginReleaseDescriptor( Buffer.from(Buffer.from(bytes).toString('utf8').replace('"schema":"makelore-plugin-release.v1"', '"schema":"other.v1"')), )).toThrow(); }); it('uses inclusive strict SemVer client bounds', () => { expect(isMakeLoreVersionCompatible('1.0.0', '1.0.0', null)).toBe(true); expect(isMakeLoreVersionCompatible('2.0.0', '1.0.0', '2.0.0')).toBe(true); expect(isMakeLoreVersionCompatible('2.0.1', '1.0.0', '2.0.0')).toBe(false); expect(isMakeLoreVersionCompatible('1.0.0-alpha.1', '1.0.0', null)).toBe(false); }); it('verifies Ed25519 signatures only through the injected test key dependency', () => { const { publicKey, privateKey } = generateKeyPairSync('ed25519'); const artifact = Buffer.from('artifact-bytes'); const descriptor = buildPluginReleaseDescriptor({ pluginId: 'makelore.example', version: '1.2.0', packageSchemaVersion: 2, contractVersion: 1, minMakeloreVersion: '1.0.0', maxMakeloreVersion: null, artifact: { sha256: createHash('sha256').update(artifact).digest('hex'), sizeBytes: artifact.length }, }); const descriptorBytes = serializePluginReleaseDescriptor(descriptor); const signature = sign(null, descriptorBytes, privateKey).toString('base64url'); const verifier = createPluginSignatureVerifier({ keyStore: new Map([['test-key', publicKey.export({ type: 'spki', format: 'der' })]]), clientVersion: '1.0.0', }); expect(verifyPluginReleaseSignature({ verifier, keyId: 'test-key', signature, descriptor, artifact, })).toEqual({ ok: true }); expect(verifyPluginReleaseSignature({ verifier, keyId: 'missing-key', signature, descriptor, artifact, })).toMatchObject({ ok: false, code: 'plugin_signature_invalid' }); expect(createCodeOwnedPluginTrustStore().get('missing-key')).toBeNull(); expect(Object.keys(CODE_OWNED_PLUGIN_SIGNING_KEYS)).toHaveLength(0); }); it('rejects bad signature, digest, descriptor identity, and incompatible client', () => { const { publicKey, privateKey } = generateKeyPairSync('ed25519'); const artifact = Buffer.from('artifact-bytes'); const descriptor = buildPluginReleaseDescriptor({ pluginId: 'makelore.example', version: '1.2.0', packageSchemaVersion: 2, contractVersion: 1, minMakeloreVersion: '2.0.0', maxMakeloreVersion: null, artifact: { sha256: createHash('sha256').update(artifact).digest('hex'), sizeBytes: artifact.length }, }); const signature = sign(null, serializePluginReleaseDescriptor(descriptor), privateKey).toString('base64url'); const verifier = createPluginSignatureVerifier({ keyStore: new Map([['test-key', publicKey.export({ type: 'spki', format: 'der' })]]), clientVersion: '1.0.0', }); expect(verifyPluginReleaseSignature({ verifier, keyId: 'test-key', signature, descriptor, artifact })) .toMatchObject({ ok: false, code: 'plugin_incompatible_client' }); const compatible = createPluginSignatureVerifier({ keyStore: new Map([['test-key', publicKey.export({ type: 'spki', format: 'der' })]]), clientVersion: '2.0.0', }); expect(verifyPluginReleaseSignature({ verifier: compatible, keyId: 'test-key', signature: `${signature.slice(0, -2)}aa`, descriptor, artifact, })).toMatchObject({ ok: false, code: 'plugin_signature_invalid' }); expect(verifyPluginReleaseSignature({ verifier: compatible, keyId: 'test-key', signature, descriptor, artifact: Buffer.from('other'), })).toMatchObject({ ok: false, code: 'plugin_artifact_invalid' }); }); });