mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 18:34:02 +00:00
feat(street-level): one imagery source at a time; IC8 review fixes
- One provider is lit at a time: lighting a chip darkens the others, and a chip click records every registered provider's switch in one update so the share link keeps the swap (also off Google 3D, where Street View is not listed). The layer starts with the first default-on provider (Mapillary, free); params that light several keep the one already on, so reapplying them never flips (litByParams). The now-unreachable "several lit" chip paths are gone. - Leaving Google 3D during a Street View lookup ends it without an error or a stuck loading state (openImage's early return resets it; the lookup loop rechecks availability). - A source swap during a lookup no longer reports "no imagery". - Street View lit off Google 3D says the panel needs the Google 3D map. - Comments trimmed to the why; keySetupCore.test.mjs back to upstream's formatting plus the one new test (was a 551-line reformat). Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
64d939f3d2
commit
b1efd22f02
24 files changed
+388
-625
No files matched your search
+118
-419
@@ -29,10 +29,7 @@ test('the boot provenance snapshot survives in-process Vite config re-evaluation
|
||||
// panel save has already set its values live on process.env. A recomputed
|
||||
// snapshot would classify the panel's own keys as external (read-only) until
|
||||
// a full process relaunch, so the first evaluation's snapshot must win.
|
||||
const source = readFileSync(
|
||||
new URL('../server/standalone/key-setup.js', import.meta.url),
|
||||
'utf8',
|
||||
);
|
||||
const source = readFileSync(new URL('../server/standalone/key-setup.js', import.meta.url), 'utf8');
|
||||
assert.match(
|
||||
source,
|
||||
/const PROVIDER_ENV_AT_BOOT\s*=\s*\(?globalThis\.__GEV_PROVIDER_ENV_AT_BOOT\s*\?\?=\s*Object\.freeze\(/,
|
||||
@@ -40,40 +37,24 @@ test('the boot provenance snapshot survives in-process Vite config re-evaluation
|
||||
});
|
||||
|
||||
test('external ownership uses boot provenance even when store and shell values match', () => {
|
||||
assert.equal(
|
||||
isKeySetupExternallyManaged({
|
||||
effectiveValue: 'same-value',
|
||||
storedValue: 'same-value',
|
||||
wasExternalAtBoot: true,
|
||||
}),
|
||||
true,
|
||||
'equal bytes cannot turn a shell/Keychain value into a file-owned value',
|
||||
);
|
||||
assert.equal(
|
||||
isKeySetupExternallyManaged({
|
||||
effectiveValue: 'file-value',
|
||||
storedValue: 'file-value',
|
||||
}),
|
||||
false,
|
||||
'a value loaded only from the owned store remains editable',
|
||||
);
|
||||
assert.equal(
|
||||
isKeySetupExternallyManaged({
|
||||
effectiveValue: 'shell-value',
|
||||
storedValue: 'stale-file-value',
|
||||
}),
|
||||
true,
|
||||
'a differing live value remains external',
|
||||
);
|
||||
assert.equal(
|
||||
isKeySetupExternallyManaged({
|
||||
effectiveValue: '',
|
||||
storedValue: 'stale-file-value',
|
||||
wasExternalAtBoot: true,
|
||||
}),
|
||||
false,
|
||||
'an absent live credential has no external owner',
|
||||
);
|
||||
assert.equal(isKeySetupExternallyManaged({
|
||||
effectiveValue: 'same-value',
|
||||
storedValue: 'same-value',
|
||||
wasExternalAtBoot: true,
|
||||
}), true, 'equal bytes cannot turn a shell/Keychain value into a file-owned value');
|
||||
assert.equal(isKeySetupExternallyManaged({
|
||||
effectiveValue: 'file-value',
|
||||
storedValue: 'file-value',
|
||||
}), false, 'a value loaded only from the owned store remains editable');
|
||||
assert.equal(isKeySetupExternallyManaged({
|
||||
effectiveValue: 'shell-value',
|
||||
storedValue: 'stale-file-value',
|
||||
}), true, 'a differing live value remains external');
|
||||
assert.equal(isKeySetupExternallyManaged({
|
||||
effectiveValue: '',
|
||||
storedValue: 'stale-file-value',
|
||||
wasExternalAtBoot: true,
|
||||
}), false, 'an absent live credential has no external owner');
|
||||
});
|
||||
|
||||
test('the status payload reports presence without any credential material', () => {
|
||||
@@ -83,35 +64,17 @@ test('the status payload reports presence without any credential material', () =
|
||||
// Secret missing: the OpenSky pair must read as NOT set.
|
||||
};
|
||||
const status = keySetupStatus(env);
|
||||
assert.equal(
|
||||
status.total,
|
||||
KEY_SETUP_KEYS.filter((key) => !key.hidden).length,
|
||||
);
|
||||
assert.equal(status.total, KEY_SETUP_KEYS.filter((key) => !key.hidden).length);
|
||||
const google = status.keys.find((key) => key.id === 'google-maps');
|
||||
assert.equal(google.set, true);
|
||||
const opensky = status.keys.find((key) => key.id === 'opensky');
|
||||
assert.equal(opensky.set, false, 'half a credential pair is not configured');
|
||||
const serialized = JSON.stringify(status);
|
||||
assert.ok(
|
||||
!serialized.includes('AIzaSyFakeFakeFakeFake1234'),
|
||||
'a value leaked into status',
|
||||
);
|
||||
assert.ok(
|
||||
!serialized.includes('client-id-abcdef'),
|
||||
'a value leaked into status',
|
||||
);
|
||||
assert.ok(
|
||||
!serialized.includes('1234'),
|
||||
'a credential suffix leaked into status',
|
||||
);
|
||||
assert.ok(
|
||||
!serialized.includes('abcdef'),
|
||||
'a credential suffix leaked into status',
|
||||
);
|
||||
assert.ok(
|
||||
!serialized.includes('tails'),
|
||||
'status must not expose a credential-tail field',
|
||||
);
|
||||
assert.ok(!serialized.includes('AIzaSyFakeFakeFakeFake1234'), 'a value leaked into status');
|
||||
assert.ok(!serialized.includes('client-id-abcdef'), 'a value leaked into status');
|
||||
assert.ok(!serialized.includes('1234'), 'a credential suffix leaked into status');
|
||||
assert.ok(!serialized.includes('abcdef'), 'a credential suffix leaked into status');
|
||||
assert.ok(!serialized.includes('tails'), 'status must not expose a credential-tail field');
|
||||
assert.equal(status.setCount, 1);
|
||||
});
|
||||
|
||||
@@ -122,57 +85,25 @@ test('whitespace-only env values do not count as configured', () => {
|
||||
|
||||
test('subprocess success requires a clean zero exit', () => {
|
||||
assert.equal(commandCompletedSuccessfully({ status: 0, signal: null }), true);
|
||||
assert.equal(
|
||||
commandCompletedSuccessfully({ status: 1, signal: null }),
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
commandCompletedSuccessfully({ status: 0, signal: 'SIGTERM' }),
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
commandCompletedSuccessfully({
|
||||
status: 0,
|
||||
signal: null,
|
||||
error: new Error('spawn failed'),
|
||||
}),
|
||||
false,
|
||||
);
|
||||
assert.equal(commandCompletedSuccessfully({ status: 1, signal: null }), false);
|
||||
assert.equal(commandCompletedSuccessfully({ status: 0, signal: 'SIGTERM' }), false);
|
||||
assert.equal(commandCompletedSuccessfully({ status: 0, signal: null, error: new Error('spawn failed') }), false);
|
||||
assert.equal(commandCompletedSuccessfully(null), false);
|
||||
});
|
||||
|
||||
test('Windows owner SID parsing reads only the structured user-SID CSV field', () => {
|
||||
const localUser = 'S-1-5-21-1111111111-2222222222-3333333333-1001';
|
||||
const entraUser = 'S-1-12-1-1111111111-2222222222-3333333333-4444444444';
|
||||
assert.equal(
|
||||
parseWindowsUserSid(`"WORKSTATION\\alice","${localUser}"\r\n`),
|
||||
localUser,
|
||||
);
|
||||
assert.equal(
|
||||
parseWindowsUserSid(`"AzureAD\\alice","${entraUser}"`),
|
||||
entraUser,
|
||||
);
|
||||
assert.equal(parseWindowsUserSid(`"WORKSTATION\\alice","${localUser}"\r\n`), localUser);
|
||||
assert.equal(parseWindowsUserSid(`"AzureAD\\alice","${entraUser}"`), entraUser);
|
||||
assert.equal(
|
||||
parseWindowsUserSid(`"${localUser}","S-1-5-32-545"`),
|
||||
null,
|
||||
'an SID-looking account name must never be mistaken for the token SID',
|
||||
);
|
||||
assert.equal(
|
||||
parseWindowsUserSid('"WORKSTATION\\alice","S-1-5-32-545"'),
|
||||
null,
|
||||
'broad group SID refused',
|
||||
);
|
||||
assert.equal(
|
||||
parseWindowsUserSid(
|
||||
`"WORKSTATION\\alice","${localUser}"\n"extra","${localUser}"`,
|
||||
),
|
||||
null,
|
||||
);
|
||||
assert.equal(
|
||||
parseWindowsUserSid(`"WORKSTATION\\alice","${localUser}`),
|
||||
null,
|
||||
'unterminated CSV refused',
|
||||
);
|
||||
assert.equal(parseWindowsUserSid('"WORKSTATION\\alice","S-1-5-32-545"'), null, 'broad group SID refused');
|
||||
assert.equal(parseWindowsUserSid(`"WORKSTATION\\alice","${localUser}"\n"extra","${localUser}"`), null);
|
||||
assert.equal(parseWindowsUserSid(`"WORKSTATION\\alice","${localUser}`), null, 'unterminated CSV refused');
|
||||
});
|
||||
|
||||
test('validation accepts every registry env var and only those', () => {
|
||||
@@ -182,16 +113,8 @@ test('validation accepts every registry env var and only those', () => {
|
||||
assert.equal(verdict.ok, true, `${name} should validate`);
|
||||
assert.equal(verdict.updates[name], 'valid-value-123');
|
||||
}
|
||||
assert.equal(
|
||||
validateKeySetupUpdates({ PATH: '/usr/bin' }).ok,
|
||||
false,
|
||||
'PATH must be refused',
|
||||
);
|
||||
assert.equal(
|
||||
validateKeySetupUpdates({ NODE_OPTIONS: '--x' }).ok,
|
||||
false,
|
||||
'NODE_OPTIONS must be refused',
|
||||
);
|
||||
assert.equal(validateKeySetupUpdates({ PATH: '/usr/bin' }).ok, false, 'PATH must be refused');
|
||||
assert.equal(validateKeySetupUpdates({ NODE_OPTIONS: '--x' }).ok, false, 'NODE_OPTIONS must be refused');
|
||||
});
|
||||
|
||||
test('validation trims, and refuses empties, newlines, spaces, and oversize values', () => {
|
||||
@@ -200,25 +123,11 @@ test('validation trims, and refuses empties, newlines, spaces, and oversize valu
|
||||
assert.equal(trimmed.updates.OPENAI_API_KEY, 'sk-abc123');
|
||||
assert.equal(validateKeySetupUpdates({ OPENAI_API_KEY: '' }).ok, false);
|
||||
assert.equal(validateKeySetupUpdates({ OPENAI_API_KEY: ' ' }).ok, false);
|
||||
assert.equal(validateKeySetupUpdates({ OPENAI_API_KEY: 'a\nb' }).ok, false, 'newline injection');
|
||||
assert.equal(validateKeySetupUpdates({ OPENAI_API_KEY: 'a b' }).ok, false, 'inner space');
|
||||
assert.equal(validateKeySetupUpdates({ OPENAI_API_KEY: 'kéy' }).ok, false, 'non-ASCII');
|
||||
assert.equal(
|
||||
validateKeySetupUpdates({ OPENAI_API_KEY: 'a\nb' }).ok,
|
||||
false,
|
||||
'newline injection',
|
||||
);
|
||||
assert.equal(
|
||||
validateKeySetupUpdates({ OPENAI_API_KEY: 'a b' }).ok,
|
||||
false,
|
||||
'inner space',
|
||||
);
|
||||
assert.equal(
|
||||
validateKeySetupUpdates({ OPENAI_API_KEY: 'kéy' }).ok,
|
||||
false,
|
||||
'non-ASCII',
|
||||
);
|
||||
assert.equal(
|
||||
validateKeySetupUpdates({
|
||||
OPENAI_API_KEY: 'x'.repeat(KEY_SETUP_VALUE_LIMIT + 1),
|
||||
}).ok,
|
||||
validateKeySetupUpdates({ OPENAI_API_KEY: 'x'.repeat(KEY_SETUP_VALUE_LIMIT + 1) }).ok,
|
||||
false,
|
||||
);
|
||||
assert.equal(validateKeySetupUpdates(null).ok, false);
|
||||
@@ -236,16 +145,13 @@ test('upsert replaces the last active assignment in place', () => {
|
||||
'',
|
||||
].join('\n');
|
||||
const next = upsertDotenvValues(text, { OPENAI_API_KEY: 'new-key' });
|
||||
assert.equal(
|
||||
next,
|
||||
[
|
||||
'# comment stays',
|
||||
'OPENAI_API_KEY=old-one',
|
||||
'PORT=4173',
|
||||
'OPENAI_API_KEY=new-key',
|
||||
'',
|
||||
].join('\n'),
|
||||
);
|
||||
assert.equal(next, [
|
||||
'# comment stays',
|
||||
'OPENAI_API_KEY=old-one',
|
||||
'PORT=4173',
|
||||
'OPENAI_API_KEY=new-key',
|
||||
'',
|
||||
].join('\n'));
|
||||
});
|
||||
|
||||
test('upsert uncomments a commented assignment in place, keeping file shape', () => {
|
||||
@@ -256,49 +162,36 @@ test('upsert uncomments a commented assignment in place, keeping file shape', ()
|
||||
'PORT=4173',
|
||||
].join('\n');
|
||||
const next = upsertDotenvValues(text, { FIRMS_MAP_KEY: 'firms-123' });
|
||||
assert.equal(
|
||||
next,
|
||||
[
|
||||
'# Optional: NASA FIRMS live active fires.',
|
||||
'FIRMS_MAP_KEY=firms-123',
|
||||
'',
|
||||
'PORT=4173',
|
||||
].join('\n') + '\n',
|
||||
);
|
||||
assert.equal(next, [
|
||||
'# Optional: NASA FIRMS live active fires.',
|
||||
'FIRMS_MAP_KEY=firms-123',
|
||||
'',
|
||||
'PORT=4173',
|
||||
].join('\n') + '\n');
|
||||
});
|
||||
|
||||
test('upsert appends unknown keys under one shared header, once', () => {
|
||||
const first = upsertDotenvValues('PORT=4173\n', { OPENAI_API_KEY: 'sk-1' });
|
||||
assert.equal(
|
||||
first,
|
||||
['PORT=4173', '', KEY_SETUP_APPEND_HEADER, 'OPENAI_API_KEY=sk-1'].join(
|
||||
'\n',
|
||||
) + '\n',
|
||||
);
|
||||
assert.equal(first, [
|
||||
'PORT=4173',
|
||||
'',
|
||||
KEY_SETUP_APPEND_HEADER,
|
||||
'OPENAI_API_KEY=sk-1',
|
||||
].join('\n') + '\n');
|
||||
const second = upsertDotenvValues(first, { FIRMS_MAP_KEY: 'f-2' });
|
||||
assert.equal(
|
||||
second,
|
||||
[
|
||||
'PORT=4173',
|
||||
'',
|
||||
KEY_SETUP_APPEND_HEADER,
|
||||
'OPENAI_API_KEY=sk-1',
|
||||
'FIRMS_MAP_KEY=f-2',
|
||||
].join('\n') + '\n',
|
||||
);
|
||||
assert.equal(
|
||||
second.split(KEY_SETUP_APPEND_HEADER).length,
|
||||
2,
|
||||
'header written once',
|
||||
);
|
||||
assert.equal(second, [
|
||||
'PORT=4173',
|
||||
'',
|
||||
KEY_SETUP_APPEND_HEADER,
|
||||
'OPENAI_API_KEY=sk-1',
|
||||
'FIRMS_MAP_KEY=f-2',
|
||||
].join('\n') + '\n');
|
||||
assert.equal(second.split(KEY_SETUP_APPEND_HEADER).length, 2, 'header written once');
|
||||
});
|
||||
|
||||
test('upsert births a well-formed file from nothing', () => {
|
||||
const next = upsertDotenvValues('', { GOOGLE_MAPS_API_KEY: 'AIza-x' });
|
||||
assert.equal(
|
||||
next,
|
||||
`${KEY_SETUP_APPEND_HEADER}\nGOOGLE_MAPS_API_KEY=AIza-x\n`,
|
||||
);
|
||||
assert.equal(next, `${KEY_SETUP_APPEND_HEADER}\nGOOGLE_MAPS_API_KEY=AIza-x\n`);
|
||||
});
|
||||
|
||||
test('upsert handles export-prefixed lines and never touches lookalike keys', () => {
|
||||
@@ -315,21 +208,15 @@ test('upsert handles export-prefixed lines and never touches lookalike keys', ()
|
||||
});
|
||||
|
||||
test('upsert is idempotent for a repeated save', () => {
|
||||
const once = upsertDotenvValues('', {
|
||||
OPENAI_API_KEY: 'sk-1',
|
||||
FIRMS_MAP_KEY: 'f-1',
|
||||
});
|
||||
const twice = upsertDotenvValues(once, {
|
||||
OPENAI_API_KEY: 'sk-1',
|
||||
FIRMS_MAP_KEY: 'f-1',
|
||||
});
|
||||
const once = upsertDotenvValues('', { OPENAI_API_KEY: 'sk-1', FIRMS_MAP_KEY: 'f-1' });
|
||||
const twice = upsertDotenvValues(once, { OPENAI_API_KEY: 'sk-1', FIRMS_MAP_KEY: 'f-1' });
|
||||
assert.equal(once, twice);
|
||||
});
|
||||
|
||||
test('a real .env.example round-trip: the curated file keeps its shape', () => {
|
||||
// A representative slice of the shipped .env.example.
|
||||
const example = [
|
||||
"# God's Eye View — environment variables",
|
||||
'# God\'s Eye View — environment variables',
|
||||
'GOOGLE_MAPS_API_KEY=your_google_maps_api_key_here',
|
||||
'',
|
||||
'# Optional: OpenAI Realtime voice control. Do not prefix with VITE_.',
|
||||
@@ -346,16 +233,8 @@ test('a real .env.example round-trip: the curated file keeps its shape', () => {
|
||||
const lines = next.split('\n');
|
||||
assert.equal(lines[1], 'GOOGLE_MAPS_API_KEY=AIza-real');
|
||||
assert.equal(lines[4], 'OPENAI_API_KEY=sk-real');
|
||||
assert.equal(
|
||||
lines[5],
|
||||
'OPENAI_REALTIME_MODEL=gpt-realtime-2',
|
||||
'sibling key untouched',
|
||||
);
|
||||
assert.equal(
|
||||
lines[7],
|
||||
'TOMTOM_API_KEY=tt-real',
|
||||
'commented key uncommented in place',
|
||||
);
|
||||
assert.equal(lines[5], 'OPENAI_REALTIME_MODEL=gpt-realtime-2', 'sibling key untouched');
|
||||
assert.equal(lines[7], 'TOMTOM_API_KEY=tt-real', 'commented key uncommented in place');
|
||||
});
|
||||
|
||||
test('the admission gate refuses every non-local shape, one assertion per refusal', async () => {
|
||||
@@ -368,111 +247,29 @@ test('the admission gate refuses every non-local shape, one assertion per refusa
|
||||
contentType: 'application/json',
|
||||
env: {},
|
||||
};
|
||||
assert.equal(
|
||||
admitKeySetupRequest(local).ok,
|
||||
true,
|
||||
'the honest local request is admitted',
|
||||
);
|
||||
assert.equal(
|
||||
admitKeySetupRequest({ ...local, method: 'GET', contentType: undefined })
|
||||
.ok,
|
||||
true,
|
||||
'local GET needs no content type',
|
||||
);
|
||||
assert.equal(
|
||||
admitKeySetupRequest({ ...local, origin: undefined }).ok,
|
||||
false,
|
||||
'POST without Origin is refused',
|
||||
);
|
||||
assert.equal(
|
||||
admitKeySetupRequest({
|
||||
...local,
|
||||
method: 'GET',
|
||||
origin: undefined,
|
||||
contentType: undefined,
|
||||
}).ok,
|
||||
true,
|
||||
'local GET may omit Origin',
|
||||
);
|
||||
assert.equal(
|
||||
admitKeySetupRequest({
|
||||
...local,
|
||||
remoteAddress: '::ffff:127.0.0.1',
|
||||
hostHeader: '[::1]:4173',
|
||||
origin: 'http://[::1]:4173',
|
||||
}).ok,
|
||||
true,
|
||||
'IPv6 loopback forms are local',
|
||||
);
|
||||
assert.equal(admitKeySetupRequest(local).ok, true, 'the honest local request is admitted');
|
||||
assert.equal(admitKeySetupRequest({ ...local, method: 'GET', contentType: undefined }).ok, true, 'local GET needs no content type');
|
||||
assert.equal(admitKeySetupRequest({ ...local, origin: undefined }).ok, false, 'POST without Origin is refused');
|
||||
assert.equal(admitKeySetupRequest({ ...local, method: 'GET', origin: undefined, contentType: undefined }).ok, true, 'local GET may omit Origin');
|
||||
assert.equal(admitKeySetupRequest({ ...local, remoteAddress: '::ffff:127.0.0.1', hostHeader: '[::1]:4173', origin: 'http://[::1]:4173' }).ok, true, 'IPv6 loopback forms are local');
|
||||
|
||||
// Tunnel/LAN sharing of any kind removes the surface outright — tunnel
|
||||
// traffic arrives FROM loopback, so no socket check can carry this boundary.
|
||||
assert.equal(
|
||||
admitKeySetupRequest({
|
||||
...local,
|
||||
env: { PINOKIO_SHARE_CLOUDFLARE: 'true' },
|
||||
}).ok,
|
||||
false,
|
||||
'sharing disables the surface',
|
||||
);
|
||||
assert.equal(
|
||||
admitKeySetupRequest({ ...local, env: { PINOKIO_SHARE_LOCAL: '1' } }).ok,
|
||||
false,
|
||||
'LAN sharing disables the surface',
|
||||
);
|
||||
assert.equal(admitKeySetupRequest({ ...local, env: { PINOKIO_SHARE_CLOUDFLARE: 'true' } }).ok, false, 'sharing disables the surface');
|
||||
assert.equal(admitKeySetupRequest({ ...local, env: { PINOKIO_SHARE_LOCAL: '1' } }).ok, false, 'LAN sharing disables the surface');
|
||||
// A LAN peer reaching a wide-bound server.
|
||||
assert.equal(
|
||||
admitKeySetupRequest({ ...local, remoteAddress: '192.168.1.20' }).ok,
|
||||
false,
|
||||
'non-loopback socket refused',
|
||||
);
|
||||
assert.equal(admitKeySetupRequest({ ...local, remoteAddress: '192.168.1.20' }).ok, false, 'non-loopback socket refused');
|
||||
// Tunnel and DNS-rebinding traffic carries a foreign Host over a loopback socket.
|
||||
assert.equal(
|
||||
admitKeySetupRequest({ ...local, hostHeader: 'abc.trycloudflare.com' }).ok,
|
||||
false,
|
||||
'foreign Host refused',
|
||||
);
|
||||
assert.equal(
|
||||
admitKeySetupRequest({ ...local, hostHeader: 'workstation.local:4173' }).ok,
|
||||
false,
|
||||
'non-localhost hostnames refused',
|
||||
);
|
||||
assert.equal(
|
||||
admitKeySetupRequest({ ...local, hostHeader: '' }).ok,
|
||||
false,
|
||||
'missing Host refused',
|
||||
);
|
||||
assert.equal(
|
||||
admitKeySetupRequest({ ...local, hostHeader: '[::1].evil:4173' }).ok,
|
||||
false,
|
||||
'malformed bracketed Host refused',
|
||||
);
|
||||
assert.equal(admitKeySetupRequest({ ...local, hostHeader: 'abc.trycloudflare.com' }).ok, false, 'foreign Host refused');
|
||||
assert.equal(admitKeySetupRequest({ ...local, hostHeader: 'workstation.local:4173' }).ok, false, 'non-localhost hostnames refused');
|
||||
assert.equal(admitKeySetupRequest({ ...local, hostHeader: '' }).ok, false, 'missing Host refused');
|
||||
assert.equal(admitKeySetupRequest({ ...local, hostHeader: '[::1].evil:4173' }).ok, false, 'malformed bracketed Host refused');
|
||||
// A hostile web page POSTing at localhost carries its own Origin.
|
||||
assert.equal(
|
||||
admitKeySetupRequest({ ...local, origin: 'https://evil.example' }).ok,
|
||||
false,
|
||||
'cross-origin refused',
|
||||
);
|
||||
assert.equal(
|
||||
admitKeySetupRequest({ ...local, origin: 'not a url' }).ok,
|
||||
false,
|
||||
'unparseable Origin refused',
|
||||
);
|
||||
assert.equal(
|
||||
admitKeySetupRequest({ ...local, origin: 'http://localhost:4174' }).ok,
|
||||
false,
|
||||
'cross-port Origin refused',
|
||||
);
|
||||
assert.equal(
|
||||
admitKeySetupRequest({ ...local, origin: 'https://localhost:4173' }).ok,
|
||||
false,
|
||||
'cross-scheme Origin refused',
|
||||
);
|
||||
assert.equal(
|
||||
admitKeySetupRequest({ ...local, origin: 'http://127.0.0.1:4173' }).ok,
|
||||
false,
|
||||
'different loopback host Origin refused',
|
||||
);
|
||||
assert.equal(admitKeySetupRequest({ ...local, origin: 'https://evil.example' }).ok, false, 'cross-origin refused');
|
||||
assert.equal(admitKeySetupRequest({ ...local, origin: 'not a url' }).ok, false, 'unparseable Origin refused');
|
||||
assert.equal(admitKeySetupRequest({ ...local, origin: 'http://localhost:4174' }).ok, false, 'cross-port Origin refused');
|
||||
assert.equal(admitKeySetupRequest({ ...local, origin: 'https://localhost:4173' }).ok, false, 'cross-scheme Origin refused');
|
||||
assert.equal(admitKeySetupRequest({ ...local, origin: 'http://127.0.0.1:4173' }).ok, false, 'different loopback host Origin refused');
|
||||
// A simple-request POST (no JSON content type) is the CSRF write shape.
|
||||
const noJson = admitKeySetupRequest({ ...local, contentType: 'text/plain' });
|
||||
assert.equal(noJson.ok, false, 'non-JSON POST refused');
|
||||
@@ -483,16 +280,8 @@ test('a null value validates as a removal; an empty string still does not', () =
|
||||
const removal = validateKeySetupUpdates({ OPENAI_API_KEY: null });
|
||||
assert.equal(removal.ok, true);
|
||||
assert.equal(removal.updates.OPENAI_API_KEY, null);
|
||||
assert.equal(
|
||||
validateKeySetupUpdates({ OPENAI_API_KEY: '' }).ok,
|
||||
false,
|
||||
'empty is a mistake, not a removal',
|
||||
);
|
||||
assert.equal(
|
||||
validateKeySetupUpdates({ PATH: null }).ok,
|
||||
false,
|
||||
'removal is registry-bound too',
|
||||
);
|
||||
assert.equal(validateKeySetupUpdates({ OPENAI_API_KEY: '' }).ok, false, 'empty is a mistake, not a removal');
|
||||
assert.equal(validateKeySetupUpdates({ PATH: null }).ok, false, 'removal is registry-bound too');
|
||||
});
|
||||
|
||||
test('removal comments the assignment back out, returning the file to template shape', () => {
|
||||
@@ -503,11 +292,7 @@ test('removal comments the assignment back out, returning the file to template s
|
||||
].join('\n');
|
||||
const next = upsertDotenvValues(text, { OPENAI_API_KEY: null });
|
||||
const lines = next.split('\n');
|
||||
assert.equal(
|
||||
lines[1],
|
||||
'# OPENAI_API_KEY=',
|
||||
'active line commented out, not deleted',
|
||||
);
|
||||
assert.equal(lines[1], '# OPENAI_API_KEY=', 'active line commented out, not deleted');
|
||||
assert.equal(lines[2], 'PORT=4173', 'neighbors untouched');
|
||||
// Removing a key with no active assignment changes nothing.
|
||||
assert.equal(upsertDotenvValues(next, { FIRMS_MAP_KEY: null }), next);
|
||||
@@ -519,148 +304,62 @@ test('removal comments the assignment back out, returning the file to template s
|
||||
test('the sharing gate treats a real PINOKIO_SHARE_VAR as sharing, but not the empty/sentinel normal state', async () => {
|
||||
const { admitKeySetupRequest } = await import('./keySetupCore.mjs');
|
||||
const base = {
|
||||
method: 'POST',
|
||||
remoteAddress: '127.0.0.1',
|
||||
hostHeader: 'localhost:4173',
|
||||
origin: 'http://localhost:4173',
|
||||
contentType: 'application/json',
|
||||
method: 'POST', remoteAddress: '127.0.0.1', hostHeader: 'localhost:4173',
|
||||
origin: 'http://localhost:4173', contentType: 'application/json',
|
||||
};
|
||||
// The ordinary launch states: unset, empty, or the explicit disabled sentinel.
|
||||
assert.equal(
|
||||
admitKeySetupRequest({ ...base, env: {} }).ok,
|
||||
true,
|
||||
'unset SHARE_VAR is normal',
|
||||
);
|
||||
assert.equal(
|
||||
admitKeySetupRequest({ ...base, env: { PINOKIO_SHARE_VAR: '' } }).ok,
|
||||
true,
|
||||
'empty SHARE_VAR is normal',
|
||||
);
|
||||
assert.equal(
|
||||
admitKeySetupRequest({
|
||||
...base,
|
||||
env: { PINOKIO_SHARE_VAR: '__gev_sharing_disabled__' },
|
||||
}).ok,
|
||||
true,
|
||||
'the disabled sentinel is normal',
|
||||
);
|
||||
assert.equal(admitKeySetupRequest({ ...base, env: {} }).ok, true, 'unset SHARE_VAR is normal');
|
||||
assert.equal(admitKeySetupRequest({ ...base, env: { PINOKIO_SHARE_VAR: '' } }).ok, true, 'empty SHARE_VAR is normal');
|
||||
assert.equal(admitKeySetupRequest({ ...base, env: { PINOKIO_SHARE_VAR: '__gev_sharing_disabled__' } }).ok, true, 'the disabled sentinel is normal');
|
||||
// A real tunnel var disables the surface.
|
||||
assert.equal(
|
||||
admitKeySetupRequest({
|
||||
...base,
|
||||
env: { PINOKIO_SHARE_VAR: 'MY_TUNNEL_TOKEN' },
|
||||
}).ok,
|
||||
false,
|
||||
'a real share var is sharing',
|
||||
);
|
||||
assert.equal(admitKeySetupRequest({ ...base, env: { PINOKIO_SHARE_VAR: 'MY_TUNNEL_TOKEN' } }).ok, false, 'a real share var is sharing');
|
||||
});
|
||||
|
||||
test('the gate refuses proxied requests even from a loopback socket with local headers', async () => {
|
||||
const { admitKeySetupRequest } = await import('./keySetupCore.mjs');
|
||||
const base = {
|
||||
method: 'POST',
|
||||
remoteAddress: '127.0.0.1',
|
||||
hostHeader: 'localhost:4173',
|
||||
origin: 'http://localhost:4173',
|
||||
contentType: 'application/json',
|
||||
env: {},
|
||||
method: 'POST', remoteAddress: '127.0.0.1', hostHeader: 'localhost:4173',
|
||||
origin: 'http://localhost:4173', contentType: 'application/json', env: {},
|
||||
};
|
||||
assert.equal(
|
||||
admitKeySetupRequest(base).ok,
|
||||
true,
|
||||
'no proxy headers → admitted',
|
||||
);
|
||||
for (const header of [
|
||||
'x-forwarded-for',
|
||||
'forwarded',
|
||||
'via',
|
||||
'cf-connecting-ip',
|
||||
'cf-ray',
|
||||
'x-real-ip',
|
||||
'x-forwarded-host',
|
||||
'x-forwarded-port',
|
||||
'x-forwarded-proto',
|
||||
]) {
|
||||
assert.equal(admitKeySetupRequest(base).ok, true, 'no proxy headers → admitted');
|
||||
for (const header of ['x-forwarded-for', 'forwarded', 'via', 'cf-connecting-ip', 'cf-ray', 'x-real-ip', 'x-forwarded-host', 'x-forwarded-port', 'x-forwarded-proto']) {
|
||||
assert.equal(
|
||||
admitKeySetupRequest({ ...base, proxyHeaders: { [header]: 'anything' } })
|
||||
.ok,
|
||||
admitKeySetupRequest({ ...base, proxyHeaders: { [header]: 'anything' } }).ok,
|
||||
false,
|
||||
`${header} present → refused`,
|
||||
);
|
||||
}
|
||||
// An empty forwarding header is not a proxy signal.
|
||||
assert.equal(
|
||||
admitKeySetupRequest({ ...base, proxyHeaders: { 'x-forwarded-for': '' } })
|
||||
.ok,
|
||||
true,
|
||||
);
|
||||
assert.equal(admitKeySetupRequest({ ...base, proxyHeaders: { 'x-forwarded-for': '' } }).ok, true);
|
||||
});
|
||||
|
||||
test('validation rejects dotenv metacharacters that would round-trip wrong', () => {
|
||||
for (const bad of ['abc#def', 'ab"cd', "ab'cd", 'ab$cd', 'ab\\cd', 'ab`cd']) {
|
||||
assert.equal(
|
||||
validateKeySetupUpdates({ OPENAI_API_KEY: bad }).ok,
|
||||
false,
|
||||
`${JSON.stringify(bad)} refused`,
|
||||
);
|
||||
assert.equal(validateKeySetupUpdates({ OPENAI_API_KEY: bad }).ok, false, `${JSON.stringify(bad)} refused`);
|
||||
}
|
||||
// Real key alphabets still pass: base64url, JWT dots, hex, plus/slash.
|
||||
for (const good of [
|
||||
'sk-AbC0-9_x',
|
||||
'eyJhbGc.eyJzdWI.QWxpY2U',
|
||||
'a1b2c3d4e5f6',
|
||||
'AB+cd/ef=',
|
||||
]) {
|
||||
assert.equal(
|
||||
validateKeySetupUpdates({ OPENAI_API_KEY: good }).ok,
|
||||
true,
|
||||
`${good} accepted`,
|
||||
);
|
||||
for (const good of ['sk-AbC0-9_x', 'eyJhbGc.eyJzdWI.QWxpY2U', 'a1b2c3d4e5f6', 'AB+cd/ef=']) {
|
||||
assert.equal(validateKeySetupUpdates({ OPENAI_API_KEY: good }).ok, true, `${good} accepted`);
|
||||
}
|
||||
});
|
||||
|
||||
test('server Google key remains supported without appearing in setup or its missing count', () => {
|
||||
const secret = 'server-key-fixture';
|
||||
assert.deepEqual(
|
||||
validateKeySetupUpdates({ GOOGLE_MAPS_SERVER_API_KEY: secret }),
|
||||
{
|
||||
ok: true,
|
||||
updates: { GOOGLE_MAPS_SERVER_API_KEY: secret },
|
||||
},
|
||||
);
|
||||
assert.equal(
|
||||
validateKeySetupUpdates({ GOOGLE_MAPS_SERVER_API_KEY: null }).ok,
|
||||
true,
|
||||
);
|
||||
assert.deepEqual(validateKeySetupUpdates({ GOOGLE_MAPS_SERVER_API_KEY: secret }), {
|
||||
ok: true, updates: { GOOGLE_MAPS_SERVER_API_KEY: secret },
|
||||
});
|
||||
assert.equal(validateKeySetupUpdates({ GOOGLE_MAPS_SERVER_API_KEY: null }).ok, true);
|
||||
const status = keySetupStatus({ GOOGLE_MAPS_SERVER_API_KEY: secret });
|
||||
assert.deepEqual(status, keySetupStatus({}));
|
||||
assert.equal(
|
||||
status.keys.some((key) => key.id === 'google-maps-server'),
|
||||
false,
|
||||
);
|
||||
assert.equal(status.keys.some((key) => key.id === 'google-maps-server'), false);
|
||||
assert.equal(keySetupRequirement('google-maps-server'), '');
|
||||
assert.equal(
|
||||
status.keys.find((key) => key.id === 'google-maps').title,
|
||||
'GOOGLE MAPS',
|
||||
);
|
||||
const allVisibleConfigured = Object.fromEntries(
|
||||
status.keys.flatMap((key) =>
|
||||
key.envVars.map((name) => [name, 'configured-fixture']),
|
||||
),
|
||||
);
|
||||
assert.equal(status.keys.find((key) => key.id === 'google-maps').title, 'GOOGLE MAPS');
|
||||
const allVisibleConfigured = Object.fromEntries(status.keys.flatMap((key) =>
|
||||
key.envVars.map((name) => [name, 'configured-fixture']),
|
||||
));
|
||||
const complete = keySetupStatus(allVisibleConfigured);
|
||||
assert.equal(
|
||||
complete.setCount,
|
||||
complete.total,
|
||||
'an absent server key must not leave setup incomplete',
|
||||
);
|
||||
assert.deepEqual(
|
||||
complete,
|
||||
keySetupStatus({
|
||||
...allVisibleConfigured,
|
||||
GOOGLE_MAPS_SERVER_API_KEY: secret,
|
||||
}),
|
||||
);
|
||||
assert.equal(complete.setCount, complete.total, 'an absent server key must not leave setup incomplete');
|
||||
assert.deepEqual(complete, keySetupStatus({ ...allVisibleConfigured, GOOGLE_MAPS_SERVER_API_KEY: secret }));
|
||||
assert.ok(!JSON.stringify(status).includes('GOOGLE_MAPS_SERVER_API_KEY'));
|
||||
assert.ok(!JSON.stringify(status).includes(secret));
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user