security: a key for the network test probe (08 §7 #9)
Settings → Network makes a key, shown once and stored encrypted; the api sends it to the probe as a Bearer token, and a new probe address drops it. A probe with PROBE_KEY only answers that key. Without one it stays open, and Diagnostics strongly recommends setting it.
This commit is contained in:
1 parent
958405651b
commit
60348cf0c5
14 files changed
+233
-14
No files matched your search
@@ -154,4 +154,55 @@ describe.skipIf(!hasTestDb)('diagnostics', () => {
|
||||
401,
|
||||
);
|
||||
});
|
||||
|
||||
it('the probe key: made here, shown once, sent to the probe, never in settings (08 §7 #9)', async () => {
|
||||
const cookie = await t.login(ADMIN.email, ADMIN.password);
|
||||
const as = (method: string, url: string, payload?: object) =>
|
||||
t.app.inject({ method: method as 'GET', url, payload, headers: { ...H, cookie } });
|
||||
await setSetting(t.db, 'network.probeUrl', 'https://probe.test');
|
||||
// No key yet: the inside checks say to make one.
|
||||
expect(byId(await insideChecks(deps)).probe).toMatchObject({ status: 'warn' });
|
||||
|
||||
const g = await as('POST', '/api/settings/network/probe-key');
|
||||
expect(g.statusCode, g.body).toBe(200);
|
||||
const { key } = g.json() as { key: string };
|
||||
expect(key).toMatch(/^[A-Za-z0-9_-]{43}$/);
|
||||
const s = await as('GET', '/api/settings');
|
||||
expect(s.json()['network.hasProbeKey']).toBe(true);
|
||||
expect(s.body).not.toContain(key);
|
||||
expect(s.json()).not.toHaveProperty('network.probeKeyEnc');
|
||||
expect((await as('PATCH', '/api/settings', { 'network.probeKeyEnc': 'x' })).statusCode).toBe(
|
||||
400,
|
||||
);
|
||||
expect(byId(await insideChecks(deps)).probe).toMatchObject({ status: 'ok' });
|
||||
|
||||
let auth: string | null = null;
|
||||
await networkTest(
|
||||
{
|
||||
...deps,
|
||||
fetchProbe: (async (_u: URL, init: RequestInit) => {
|
||||
auth = new Headers(init.headers).get('authorization');
|
||||
return new Response(JSON.stringify({ ip: '203.0.113.10', results: [] }));
|
||||
}) as unknown as typeof fetch,
|
||||
},
|
||||
'quick',
|
||||
);
|
||||
expect(auth).toBe(`Bearer ${key}`);
|
||||
// The probe refusing it says what to do.
|
||||
const refused = await networkTest(
|
||||
{
|
||||
...deps,
|
||||
fetchProbe: (async () =>
|
||||
new Response(JSON.stringify({ error: 'a key is needed' }), {
|
||||
status: 401,
|
||||
})) as unknown as typeof fetch,
|
||||
},
|
||||
'quick',
|
||||
);
|
||||
expect(byId(refused.checks).probe!.fix).toContain('PROBE_KEY');
|
||||
|
||||
// Another address: the key was for the old probe, so it's gone.
|
||||
await as('PATCH', '/api/settings', { 'network.probeUrl': 'https://other-probe.test' });
|
||||
expect((await as('GET', '/api/settings')).json()['network.hasProbeKey']).toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user