fix(sidecar): block cloud fallback in Docker mode (#1726)

Self-hosted Docker instances must not proxy unhandled routes to
api.worldmonitor.app. When LOCAL_API_MODE=docker, cloudFallback is
forced to false regardless of LOCAL_API_CLOUD_FALLBACK env var.
Logs a warning if the user explicitly requested fallback.

Prevents self-hosted users from unknowingly sending traffic to the
production Vercel deployment.
This commit is contained in:
Elie Habib
2026-03-17 02:10:12 +04:00
committed by GitHub
parent d92b6ffc02
commit 2e36e57a75
2 changed files with 44 additions and 1 deletions

View File

@@ -530,7 +530,11 @@ function resolveConfig(options = {}) {
].find((candidate) => existsSync(candidate)) ?? path.join(resourceDir, 'api');
const dataDir = String(options.dataDir ?? process.env.LOCAL_API_DATA_DIR ?? resourceDir);
const mode = String(options.mode ?? process.env.LOCAL_API_MODE ?? 'desktop-sidecar');
const cloudFallback = String(options.cloudFallback ?? process.env.LOCAL_API_CLOUD_FALLBACK ?? '') === 'true';
const requestedFallback = String(options.cloudFallback ?? process.env.LOCAL_API_CLOUD_FALLBACK ?? '') === 'true';
const cloudFallback = mode === 'docker' ? false : requestedFallback;
if (mode === 'docker' && requestedFallback) {
(options.logger ?? console).warn('[local-api] Cloud fallback disabled in Docker mode (self-hosted instances must not proxy to api.worldmonitor.app)');
}
const logger = options.logger ?? console;
return {

View File

@@ -528,6 +528,45 @@ test('strips browser origin headers when proxying to cloud fallback (cloudFallba
}
});
test('blocks cloud fallback in Docker mode even when explicitly requested', async () => {
const remote = await setupRemoteServer();
const localApi = await setupApiDir({
'docker-test.js': `
export default async function handler() {
return new Response(JSON.stringify({ source: 'local-error' }), {
status: 500,
headers: { 'content-type': 'application/json' }
});
}
`,
});
const warnings = [];
const app = await createLocalApiServer({
port: 0,
apiDir: localApi.apiDir,
remoteBase: remote.remoteBase,
cloudFallback: 'true',
mode: 'docker',
logger: { log() {}, warn(...args) { warnings.push(args.join(' ')); }, error() {} },
});
const { port } = await app.start();
try {
const response = await fetch(`http://127.0.0.1:${port}/api/docker-test`);
// Should NOT fall back to cloud; should return the local 500 directly
assert.equal(response.status, 500);
const body = await response.json();
assert.equal(body.source, 'local-error');
// Should have logged a warning about Docker mode blocking fallback
assert.ok(warnings.some(w => w.includes('Docker mode')), 'Should warn about Docker mode blocking fallback');
} finally {
await app.close();
await localApi.cleanup();
await remote.close();
}
});
test('responds to OPTIONS preflight with CORS headers', async () => {
const localApi = await setupApiDir({
'data.js': `