mirror of
https://github.com/hanzoai/extension.git
synced 2026-10-31 21:57:06 +00:00
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]>
101 lines
3.6 KiB
TypeScript
101 lines
3.6 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import {
|
|
DEFAULT_SETTINGS,
|
|
parseSettings,
|
|
mergeSettings,
|
|
serializeSettings,
|
|
type Settings,
|
|
} from "../src/store";
|
|
|
|
const KEY = "sk-" + "a".repeat(20);
|
|
const KEY2 = "sk-" + "b".repeat(20);
|
|
|
|
describe("parseSettings", () => {
|
|
it("returns defaults for null / empty", () => {
|
|
expect(parseSettings(null)).toEqual(DEFAULT_SETTINGS);
|
|
expect(parseSettings("")).toEqual(DEFAULT_SETTINGS);
|
|
});
|
|
|
|
it("returns defaults for invalid JSON", () => {
|
|
expect(parseSettings("{not json")).toEqual(DEFAULT_SETTINGS);
|
|
});
|
|
|
|
it("returns defaults for a non-object payload", () => {
|
|
expect(parseSettings("42")).toEqual(DEFAULT_SETTINGS);
|
|
expect(parseSettings("null")).toEqual(DEFAULT_SETTINGS);
|
|
expect(parseSettings('"a string"')).toEqual(DEFAULT_SETTINGS);
|
|
});
|
|
|
|
it("reads a valid stored key + model", () => {
|
|
const raw = JSON.stringify({ apiKey: KEY, model: "zen5-coder" });
|
|
expect(parseSettings(raw)).toEqual({ apiKey: KEY, model: "zen5-coder" });
|
|
});
|
|
|
|
it("drops a stored key that no longer matches the sk- shape", () => {
|
|
const raw = JSON.stringify({ apiKey: "hk-" + "a".repeat(20), model: "zen5" });
|
|
expect(parseSettings(raw)).toEqual({ apiKey: "", model: "zen5" });
|
|
});
|
|
|
|
it("trims the stored key and falls back to the default model when missing/blank", () => {
|
|
const raw = JSON.stringify({ apiKey: ` ${KEY} `, model: " " });
|
|
expect(parseSettings(raw)).toEqual({ apiKey: KEY, model: DEFAULT_SETTINGS.model });
|
|
});
|
|
|
|
it("fills defaults for wrong-typed fields", () => {
|
|
const raw = JSON.stringify({ apiKey: 123, model: [] });
|
|
expect(parseSettings(raw)).toEqual(DEFAULT_SETTINGS);
|
|
});
|
|
});
|
|
|
|
describe("mergeSettings", () => {
|
|
const base: Settings = { apiKey: "", model: "zen5" };
|
|
|
|
it("accepts a valid key patch", () => {
|
|
const { settings, error } = mergeSettings(base, { apiKey: KEY });
|
|
expect(error).toBeUndefined();
|
|
expect(settings.apiKey).toBe(KEY);
|
|
});
|
|
|
|
it("trims the key before validating and storing", () => {
|
|
const { settings } = mergeSettings(base, { apiKey: ` ${KEY2} ` });
|
|
expect(settings.apiKey).toBe(KEY2);
|
|
});
|
|
|
|
it("clears the key on an empty patch (explicit removal)", () => {
|
|
const withKey: Settings = { apiKey: KEY, model: "zen5" };
|
|
const { settings, error } = mergeSettings(withKey, { apiKey: " " });
|
|
expect(error).toBeUndefined();
|
|
expect(settings.apiKey).toBe("");
|
|
});
|
|
|
|
it("rejects a malformed key WITHOUT mutating current settings", () => {
|
|
const { settings, error } = mergeSettings(base, { apiKey: "hk-" + "a".repeat(20) });
|
|
expect(error).toMatch(/Not a Hanzo key/);
|
|
expect(settings).toBe(base); // returns the untouched original
|
|
});
|
|
|
|
it("normalizes a blank model back to the default", () => {
|
|
const { settings } = mergeSettings(base, { model: " " });
|
|
expect(settings.model).toBe(DEFAULT_SETTINGS.model);
|
|
});
|
|
|
|
it("applies a model patch and leaves the key untouched", () => {
|
|
const withKey: Settings = { apiKey: KEY, model: "zen5" };
|
|
const { settings } = mergeSettings(withKey, { model: "zen5-mini" });
|
|
expect(settings).toEqual({ apiKey: KEY, model: "zen5-mini" });
|
|
});
|
|
|
|
it("applies key + model together", () => {
|
|
const { settings, error } = mergeSettings(base, { apiKey: KEY, model: "zen5-coder" });
|
|
expect(error).toBeUndefined();
|
|
expect(settings).toEqual({ apiKey: KEY, model: "zen5-coder" });
|
|
});
|
|
});
|
|
|
|
describe("serializeSettings <-> parseSettings round-trip", () => {
|
|
it("round-trips a valid settings object", () => {
|
|
const s: Settings = { apiKey: KEY, model: "zen5-coder" };
|
|
expect(parseSettings(serializeSettings(s))).toEqual(s);
|
|
});
|
|
});
|