Files
webmail/lib/__tests__/plugin-host-api-third-party.test.ts

61 lines
2.7 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import type { InstalledPlugin } from '../plugin-types';
import { setNoThirdPartyTraffic } from '../third-party-mode';
// #903: the host API's own locks, behind the loader that already refuses
// plugins declaring network access: api.http.fetch and the WKD key lookup
// reach nothing outside while third-party traffic is off.
vi.mock('@/stores/toast-store', () => ({ toast: { success: vi.fn(), error: vi.fn(), info: vi.fn(), warning: vi.fn() } }));
import { dispatchApiCall } from '../plugin-sandbox/host-api';
function plugin(overrides: Partial<InstalledPlugin> = {}): InstalledPlugin {
return {
id: 'net', name: 'Net', version: '1.0.0', author: '', description: '', type: 'hook',
permissions: [], entrypoint: 'index.js', enabled: true, status: 'running', settings: {},
...overrides,
};
}
const fetchSpy = vi.fn(async () => new Response('{}', { headers: { 'content-type': 'application/json' } }));
beforeEach(() => {
fetchSpy.mockClear();
vi.stubGlobal('fetch', fetchSpy);
});
afterEach(() => {
vi.unstubAllGlobals();
setNoThirdPartyTraffic(false);
});
describe('api.http.fetch', () => {
const fetcher = plugin({ permissions: ['http:fetch'], grantedPermissions: ['http:fetch'], httpOrigins: ['https://api.vendor.example'] });
it('is refused with no third-party traffic, even for a plugin that got started', async () => {
setNoThirdPartyTraffic(true);
await expect(dispatchApiCall(fetcher, 'http.fetch', ['https://api.vendor.example/x'])).rejects.toThrow(/Outside requests are off/);
expect(fetchSpy).not.toHaveBeenCalled();
});
it('reaches the declared origin with the mode off', async () => {
await dispatchApiCall(fetcher, 'http.fetch', ['https://api.vendor.example/x']).catch(() => undefined);
expect(String((fetchSpy.mock.calls[0] as unknown[] | undefined)?.[0])).toMatch(/^https:\/\/api\.vendor\.example\/x/);
});
});
describe('api.crypto.getPublicKeyFromWKD', () => {
const pgp = plugin({ permissions: ['crypto:full'], grantedPermissions: ['crypto:full'] });
it('asks nobody with no third-party traffic', async () => {
setNoThirdPartyTraffic(true);
expect(await dispatchApiCall(pgp, 'crypto.getPublicKeyFromWKD', ['joe@vendor.example'], { privileged: true })).toMatchObject({ status: 'ERROR' });
expect(fetchSpy).not.toHaveBeenCalled();
});
it('asks the recipient\'s domain with the mode off', async () => {
fetchSpy.mockImplementationOnce(async () => new Response('', { status: 404 }));
await dispatchApiCall(pgp, 'crypto.getPublicKeyFromWKD', ['joe@vendor.example'], { privileged: true });
expect(String((fetchSpy.mock.calls[0] as unknown[])[0])).toMatch(/^https:\/\/openpgpkey\.vendor\.example\//);
});
});