101 lines
3.6 KiB
TypeScript
101 lines
3.6 KiB
TypeScript
import { describe, expect, it, vi } from 'vitest';
|
|
import { SafeUpstreamGateway } from './safe-upstream-gateway.js';
|
|
|
|
describe('SafeUpstreamGateway', () => {
|
|
it('dispatches only the audited zero-body network registration operation through pinned POST', async () => {
|
|
const calls: unknown[] = [];
|
|
const gateway = new SafeUpstreamGateway({
|
|
transport: {
|
|
get: async () => ({ status: 200, headers: {}, body: '' }),
|
|
post: async (url, headers, body) => {
|
|
calls.push({ url, headers, body });
|
|
return { status: 204, headers: {}, body: '' };
|
|
},
|
|
},
|
|
});
|
|
const response = await gateway.postNetworkRegisterAuto('http://192.168.1.20:8080');
|
|
expect(response.status).toBe(204);
|
|
expect(calls).toEqual([
|
|
{ url: 'http://192.168.1.20:8080/api/network/register-auto', headers: {}, body: '' },
|
|
]);
|
|
});
|
|
|
|
it('rejects malformed operation origins before calling transport', async () => {
|
|
const post = vi.fn(async () => ({ status: 204, headers: {}, body: '' }));
|
|
const gateway = new SafeUpstreamGateway({
|
|
transport: { get: async () => ({ status: 200, headers: {}, body: '' }), post },
|
|
});
|
|
await expect(
|
|
gateway.postNetworkRegisterAuto('http://192.168.1.20/base?next=x'),
|
|
).rejects.toMatchObject({ code: 'UPSTREAM_REQUEST_INVALID', dispatched: false });
|
|
expect(post).not.toHaveBeenCalled();
|
|
});
|
|
it('sends login password only as JSON through the pinned POST transport', async () => {
|
|
const calls: unknown[] = [];
|
|
const gateway = new SafeUpstreamGateway({
|
|
transport: {
|
|
get: async () => ({ status: 200, headers: {}, body: '' }),
|
|
post: async (url, headers, body) => {
|
|
calls.push({ url, headers, body });
|
|
return { status: 200, headers: { 'set-cookie': 'simadmin_session=opaque' }, body: '' };
|
|
},
|
|
},
|
|
});
|
|
await gateway.request({
|
|
url: 'https://192.168.1.20:8080/api/auth/login',
|
|
method: 'POST',
|
|
headers: { 'content-type': 'application/json' },
|
|
secret: '[REDACTED]',
|
|
body: '[REDACTED]',
|
|
});
|
|
expect(calls).toEqual([
|
|
{
|
|
url: 'https://192.168.1.20:8080/api/auth/login',
|
|
headers: { 'content-type': 'application/json' },
|
|
body: '{"password":"[REDACTED]"}',
|
|
},
|
|
]);
|
|
});
|
|
it('rejects HTTP login and logout so credentials and cookies are never sent in cleartext', async () => {
|
|
const gateway = new SafeUpstreamGateway({
|
|
transport: {
|
|
get: async () => ({ status: 200, headers: {}, body: '' }),
|
|
post: async () => ({ status: 200, headers: {}, body: '' }),
|
|
},
|
|
});
|
|
await expect(
|
|
gateway.request({
|
|
url: 'http://192.168.1.20:8080/api/auth/login',
|
|
method: 'POST',
|
|
headers: { 'content-type': 'application/json' },
|
|
secret: '[REDACTED]',
|
|
body: '[REDACTED]',
|
|
}),
|
|
).rejects.toThrow('UPSTREAM_INSECURE_AUTH');
|
|
await expect(
|
|
gateway.request({
|
|
url: 'http://192.168.1.20:8080/api/auth/logout',
|
|
method: 'POST',
|
|
headers: { cookie: 'simadmin_session=opaque' },
|
|
}),
|
|
).rejects.toThrow('UPSTREAM_INSECURE_AUTH');
|
|
});
|
|
|
|
it('does not allow a supplied redacted body marker to become a network request body', async () => {
|
|
const gateway = new SafeUpstreamGateway({
|
|
transport: {
|
|
get: async () => ({ status: 200, headers: {}, body: '' }),
|
|
post: async () => ({ status: 200, headers: {}, body: '' }),
|
|
},
|
|
});
|
|
await expect(
|
|
gateway.request({
|
|
url: 'https://192.168.1.20:8080/api/auth/login',
|
|
method: 'POST',
|
|
headers: {},
|
|
body: '[REDACTED]',
|
|
}),
|
|
).rejects.toThrow('UPSTREAM_REQUEST_INVALID');
|
|
});
|
|
});
|