mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-05 06:59:42 +02:00
feat(tunnel,ui): purple tunnel button + opt-in unauthenticated tunnel with warning (v1.1.9)
- Daylight Blue: Cloudflare Tunnel welcome button is now purple (was orange), keeping Claude blue / Tunnel purple / OpenCode green distinct. - Allow enabling the Cloudflare tunnel with no CODEMAN_PASSWORD via the UI: the toggle now pops a security confirm dialog and, on confirm, sends an explicit per-request acknowledgeUnauthTunnel:true (new action field, never persisted). Server logs a loud warning whenever a passwordless public tunnel starts. curl/API/CLI stay refused unless password/env/flag — no accidental exposure. Tests: extend test/routes/system-routes-tunnel-guard.test.ts (ack allows + not persisted; ack:false still refuses). Verified e2e on an isolated instance (purple button, confirm dialog, retry carries the flag, no real tunnel opened). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -117,6 +117,35 @@ describe('COD-55 tunnel password guard (PUT /api/settings tunnelEnabled)', () =>
|
||||
expect(tunnel.start).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('ALLOWS tunnel-enable with per-request acknowledgeUnauthTunnel:true (start called, 200, flag not persisted)', async () => {
|
||||
const res = await harness.app.inject({
|
||||
method: 'PUT',
|
||||
url: '/api/settings',
|
||||
payload: { tunnelEnabled: true, acknowledgeUnauthTunnel: true },
|
||||
});
|
||||
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(tunnel.start).toHaveBeenCalledTimes(1);
|
||||
// The action flag must NOT be persisted to settings.json.
|
||||
expect(mockedWriteFile).toHaveBeenCalled();
|
||||
const persisted = JSON.parse(mockedWriteFile.mock.calls[0][1] as string);
|
||||
expect(persisted.acknowledgeUnauthTunnel).toBeUndefined();
|
||||
expect(persisted.tunnelEnabled).toBe(true);
|
||||
});
|
||||
|
||||
it('still REFUSES when acknowledgeUnauthTunnel is false (4xx, start not called)', async () => {
|
||||
const res = await harness.app.inject({
|
||||
method: 'PUT',
|
||||
url: '/api/settings',
|
||||
payload: { tunnelEnabled: true, acknowledgeUnauthTunnel: false },
|
||||
});
|
||||
|
||||
expect(res.statusCode).toBeGreaterThanOrEqual(400);
|
||||
expect(res.statusCode).toBeLessThan(500);
|
||||
expect(tunnel.start).not.toHaveBeenCalled();
|
||||
expect(mockedWriteFile).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not guard tunnel-disable (tunnelEnabled:false always allowed)', async () => {
|
||||
tunnel = makeTunnelManager(true);
|
||||
(harness.ctx as unknown as { tunnelManager: unknown }).tunnelManager = tunnel;
|
||||
|
||||
Reference in New Issue
Block a user