Files
zooqueenandHanzo Dev f717dd89ad keys: the key a user pastes is sk-, not the retired hk-
28 apps told the user to paste an `hk-` key — input placeholders, settings
help text, READMEs, the error thrown when the field is empty — and five of
them validated that paste against /^hk-/ before sending it as a bearer.
Nothing mints that prefix any more: cloud accepts pk- and sk-, IAM mints
sk-. So the guard admitted a credential that authenticates nothing, and the
panel failed later at the gateway with nothing on screen to explain it.

The three leak detectors keep hk- and gain sk-. Salesforce's source scan
and the two Zendesk tests exist to catch a key committed to source or
echoed back through a request; retired keys are still in circulation and
leak exactly as badly, while a detector that only knew hk- would have gone
blind to every key minted from here on.

`sk-` used to be the example of the foreign key the guard screened out — an
OpenAI key. It cannot mean that now, and shape alone cannot tell one
vendor's sk- from another's, so those comments say what the check really
proves and point at the /v1/models call that proves the rest. The three
tests that pinned "sk- is rejected" now pin "hk- is rejected", which is the
invariant that survived.

Verified: 2029 tests pass across the 28 packages; typecheck clean on 25 of
them (hubspot, jupyter and teams fail identically at HEAD on JSX and ESM
type conflicts in files this does not touch); salesforce validate-source
green.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-13 18:07:16 -07:00

78 lines
2.1 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import {
resolveSettings,
DEFAULT_SETTINGS,
DEFAULT_MODEL,
HANZO_API_BASE_URL,
CommandIds,
PLUGIN_ID,
} from '../src/config';
describe('resolveSettings', () => {
it('fills defaults when everything is missing', () => {
expect(resolveSettings({})).toEqual(DEFAULT_SETTINGS);
});
it('uses provided values', () => {
const s = resolveSettings({
apiKey: 'sk-abc',
model: 'zen5-coder',
baseUrl: 'https://gw.example.com',
});
expect(s).toEqual({
apiKey: 'sk-abc',
model: 'zen5-coder',
baseUrl: 'https://gw.example.com',
});
});
it('falls back to defaults on blank / whitespace strings', () => {
const s = resolveSettings({ apiKey: ' ', model: '', baseUrl: ' ' });
expect(s.model).toBe(DEFAULT_MODEL);
expect(s.baseUrl).toBe(HANZO_API_BASE_URL);
expect(s.apiKey).toBe('');
});
it('trims values and strips trailing slashes from baseUrl', () => {
const s = resolveSettings({
apiKey: ' sk-x ',
model: ' zen5 ',
baseUrl: 'https://api.hanzo.ai///',
});
expect(s.apiKey).toBe('sk-x');
expect(s.model).toBe('zen5');
expect(s.baseUrl).toBe('https://api.hanzo.ai');
});
it('ignores non-string values', () => {
const s = resolveSettings({
apiKey: 123 as unknown,
model: null as unknown,
baseUrl: {} as unknown,
});
expect(s).toEqual(DEFAULT_SETTINGS);
});
it('default model is a Zen model and default base is api.hanzo.ai', () => {
expect(DEFAULT_MODEL).toBe('zen5');
expect(HANZO_API_BASE_URL).toBe('https://api.hanzo.ai');
});
});
describe('command + plugin identifiers', () => {
it('every command id is namespaced under hanzo:', () => {
for (const id of Object.values(CommandIds)) {
expect(id.startsWith('hanzo:')).toBe(true);
}
});
it('command ids are unique', () => {
const ids = Object.values(CommandIds);
expect(new Set(ids).size).toBe(ids.length);
});
it('plugin id matches the schema basename convention', () => {
expect(PLUGIN_ID).toBe('@hanzo/jupyter:plugin');
});
});