Files
yaak-mountain-loop/apps/yaak-proxy/lib/encryptedCard.test.ts
T
Gregory SchierandClaude Opus 5.5 f2555bdcd5 Yaak Proxy: code-styled names and an info button in the encrypted card's why line
Hosts, apps and entries named in the why and opened-before lines render
as InlineCode, an info button beside the why line explains decryption in
a tooltip, and the "New connections only" line is gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 22:30:40 -07:00

352 lines
12 KiB
TypeScript

import type { DecryptListEntry } from "@yaakapp-internal/proxy-lib";
import { describe, expect, test } from "vite-plus/test";
import {
connectionOf,
type EncryptedCardInput,
encryptedCard,
lineText,
stopDecryptingConnection,
} from "./encryptedCard";
const user = (...entries: string[]): DecryptListEntry[] =>
entries.map((entry) => ({ entry, builtIn: false }));
const BUILT_INS: DecryptListEntry[] = [
{ entry: "!host:*.apple.com", builtIn: true, kind: "apple" },
{ entry: "!host:*.1password.com", builtIn: true, kind: "pinned" },
];
const dia = {
clientName: "Dia",
clientBundleId: "company.thebrowser.dia",
clientPath: "/A/Dia.app",
};
const tunnel: EncryptedCardInput["flow"] = {
url: "www.google.com:443",
...dia,
tunnelReason: "not_listed",
tunnelExcludedBy: null,
tunnelExcludedBuiltIn: false,
tlsRejected: null,
elapsedMs: 188,
error: null,
};
function card(over: Partial<EncryptedCardInput> = {}) {
return encryptedCard({
flow: tunnel,
list: BUILT_INS,
trusted: true,
canTrust: true,
passthrough: [],
...over,
});
}
const summary = (c: ReturnType<typeof card>) =>
c.actions.map((a) => [a.label, a.hint, a.edit ?? null]);
describe("connectionOf", () => {
test("reads a tunnel's authority and a refused handshake's URL alike", () => {
expect(
connectionOf({ url: "www.google.com:443", clientName: null, clientBundleId: null }),
).toEqual({
host: "www.google.com",
port: 443,
label: "www.google.com",
address: "www.google.com:443",
app: null,
});
const c = connectionOf({ url: "https://home.ts.net:8317", ...dia });
expect(c.label).toBe("home.ts.net:8317");
expect(c.app).toEqual({ name: "Dia", bundleId: "company.thebrowser.dia" });
expect(
connectionOf({ url: "[::1]:8443", clientName: null, clientBundleId: null }),
).toMatchObject({ host: "::1", port: 8443, address: "[::1]:8443", label: "[::1]:8443" });
});
});
describe("the card of an encrypted connection", () => {
test("nothing decrypted yet: the host, the app and everything, each with what it writes", () => {
const c = card();
expect(lineText(c.why)).toBe("Nothing is decrypted yet");
expect(summary(c)).toEqual([
[
"Decrypt www.google.com",
"host:www.google.com",
{ add: ["host:www.google.com"], remove: [] },
],
["Decrypt Dia", "app:Dia", { add: ["app:Dia"], remove: [] }],
["Decrypt Everything", "*", { add: ["*"], remove: [] }],
]);
// Nothing under the buttons while the certificate is trusted
expect(c.foot).toEqual([]);
expect(c.fact).toBeNull();
});
test("asks for the password once while the certificate isn't trusted, where it can be", () => {
expect(card({ trusted: false }).foot).toEqual(["Asks for your password once"]);
expect(card({ trusted: false, canTrust: false }).foot).toEqual([]);
});
test("something else listed: not on the list, with the same choices", () => {
const c = card({ list: [...user("host:api.stripe.com"), ...BUILT_INS] });
expect(lineText(c.why)).toBe("Decryption not configured for `www.google.com`");
expect(c.actions.map((a) => a.label)).toEqual([
"Decrypt www.google.com",
"Decrypt Dia",
"Decrypt Everything",
]);
});
test("no app button for a connection no app is named for", () => {
const c = card({
flow: { ...tunnel, clientName: null, clientBundleId: null, clientPath: null },
});
expect(c.actions.map((a) => a.label)).toEqual(["Decrypt www.google.com", "Decrypt Everything"]);
});
test("names the host nothing on the list covers, its port only when not 443", () => {
const list = [...user("host:api.stripe.com"), ...BUILT_INS];
expect(lineText(card({ list, flow: { ...tunnel, url: "t.yaak.app:443" } }).why)).toBe(
"Decryption not configured for `t.yaak.app`",
);
expect(lineText(card({ list, flow: { ...tunnel, url: "home.ts.net:8317" } }).why)).toBe(
"Decryption not configured for `home.ts.net:8317`",
);
});
test("a port off 443 is part of the host it writes", () => {
const c = card({ flow: { ...tunnel, url: "home.ts.net:8317" } });
expect(c.actions[0]?.hint).toBe("host:home.ts.net:8317");
expect(c.actions[0]?.label).toBe("Decrypt home.ts.net:8317");
});
test("Decrypt Everything is offered only where * would decrypt it", () => {
const flow = { ...tunnel, url: "my.1password.com:443", clientName: "1Password" };
expect(card({ flow }).actions.map((a) => a.label)).not.toContain("Decrypt Everything");
});
test("excluded by default: the host alone, dashed, and who may refuse it", () => {
const pinned = card({
flow: {
...tunnel,
url: "my.1password.com:443",
clientName: "1Password",
clientBundleId: "com.1password",
tunnelReason: "excluded",
tunnelExcludedBy: "!host:*.1password.com",
tunnelExcludedBuiltIn: true,
},
});
expect([lineText(pinned.why), pinned.whySub]).toEqual([
"Excluded by default",
"pins its certificate",
]);
expect(summary(pinned)).toEqual([
[
"Decrypt my.1password.com",
"host:my.1password.com",
{ add: ["host:my.1password.com"], remove: [] },
],
]);
expect(pinned.actions[0]?.dashed).toBe(true);
expect(pinned.foot).toEqual(["1Password may refuse it"]);
const apple = card({
flow: {
...tunnel,
url: "gateway.icloud.apple.com:443",
clientName: null,
clientBundleId: null,
tunnelReason: "excluded",
tunnelExcludedBy: "!host:*.apple.com",
tunnelExcludedBuiltIn: true,
},
});
expect(apple.whySub).toBe("Apple service");
expect(apple.foot).toEqual(["macOS may refuse it"]);
});
test("excluded by the person: names it, and Stop Excluding when that alone decrypts it", () => {
const list = [...user("*", "!host:*.google.com"), ...BUILT_INS];
const c = card({
list,
flow: { ...tunnel, tunnelReason: "excluded", tunnelExcludedBy: "!host:*.google.com" },
});
expect(lineText(c.why)).toBe("Excluded by `!host:*.google.com`");
// Nothing added wins over the person's own exclusion, so taking it off is the way in
expect(summary(c)).toEqual([
[
"Stop Excluding *.google.com",
"!host:*.google.com",
{ add: [], remove: ["!host:*.google.com"] },
],
]);
// Their exact exclusion of the host is one Decrypt takes off itself
const exact = card({ list: [...user("*", "!host:www.google.com"), ...BUILT_INS] });
expect(summary(exact)[0]).toEqual([
"Decrypt www.google.com",
"host:www.google.com",
{ add: [], remove: ["!host:www.google.com"] },
]);
// Every way in blocked but taking the exclusions off
const both = [...user("*", "!host:*.google.com", "!app:Dia"), ...BUILT_INS];
const blocked = card({ list: both });
expect(summary(blocked)).toEqual([
[
"Stop Excluding 2 Entries",
"!host:*.google.com, !app:Dia",
{ add: [], remove: ["!host:*.google.com", "!app:Dia"] },
],
]);
expect(blocked.actions[0]?.removes).toBe(true);
});
test("once the list decrypts it: opened before, the opposite, and Reconnect while open", () => {
const list = [...user("host:www.google.com"), ...BUILT_INS];
const closed = card({ list });
expect(lineText(closed.why)).toBe("Not on the decrypt list");
expect(closed.fact && lineText(closed.fact)).toBe(
"Opened before `www.google.com` was decrypted",
);
expect(summary(closed)).toEqual([
[
"Stop Decrypting www.google.com",
"host:www.google.com",
{ add: [], remove: ["host:www.google.com"] },
],
]);
expect(closed.foot).toEqual([]);
const open = card({ list, flow: { ...tunnel, elapsedMs: null } });
expect(open.actions.map((a) => a.kind)).toEqual(["reconnect", "stop"]);
const everything = card({ list: [...user("*"), ...BUILT_INS] });
expect(everything.fact && lineText(everything.fact)).toBe(
"Opened before everything was decrypted",
);
expect(everything.actions[0]?.label).toBe("Stop Decrypting Everything");
});
});
describe("the card of a refused handshake", () => {
const refused: EncryptedCardInput["flow"] = {
...tunnel,
url: "https://sync.acme.dev",
clientName: "Acme",
clientBundleId: "dev.acme",
clientPath: "/A/Acme.app",
tunnelReason: null,
tlsRejected: "bad_certificate",
elapsedMs: 6,
};
const listed = [...user("*"), ...BUILT_INS];
test("an app: likely pinned, Stop Decrypting first, Try Again while passed through", () => {
const c = card({ flow: refused, list: listed, passthrough: ["sync.acme.dev:443"] });
expect(c.rejected).toBe(true);
expect([lineText(c.why), c.whySub]).toEqual(["Acme rejected the certificate", "likely pinned"]);
expect(summary(c)).toEqual([
["Stop Decrypting Acme", "!app:Acme", { add: ["!app:Acme"], remove: [] }],
["Try Again", "next connection", null],
]);
expect(c.actions[0]?.primary).toBe(true);
expect(c.foot).toEqual(["Passed through until relaunch"]);
});
test("once no longer passed through, Try Again goes and the foot says so", () => {
const c = card({ flow: refused, list: listed, passthrough: [] });
expect(c.actions.map((a) => a.kind)).toEqual(["stop"]);
expect(c.foot).toEqual(["No longer passed through"]);
});
test("a program: its own trust store, Copy Environment first", () => {
const python = {
...refused,
url: "https://pypi.org",
clientName: "python3",
clientBundleId: null,
clientPath: "/opt/homebrew/bin/python3",
};
const c = card({ flow: python, list: listed, passthrough: ["pypi.org:443"] });
expect([lineText(c.why), c.whySub]).toEqual([
"python3 rejected the certificate",
"uses its own trust store",
]);
expect(c.actions.map((a) => [a.kind, a.label])).toEqual([
["copy_environment", "Copy Environment"],
["stop", "Stop Decrypting python3"],
["try_again", "Try Again"],
]);
expect(c.foot).toEqual(["Then rerun python3"]);
});
test("an unknown client: the host", () => {
const c = card({
flow: { ...refused, clientName: null, clientBundleId: null, clientPath: null },
list: listed,
});
expect(lineText(c.why)).toBe("A client rejected the certificate");
expect(summary(c)[0]).toEqual([
"Stop Decrypting sync.acme.dev",
"!host:sync.acme.dev",
{ add: ["!host:sync.acme.dev"], remove: [] },
]);
});
test("an untrusted certificate explains it: Trust Certificate first", () => {
const c = card({
flow: refused,
list: listed,
trusted: false,
passthrough: ["sync.acme.dev:443"],
});
expect(lineText(c.why)).toBe("Certificate not trusted");
expect(c.actions.map((a) => a.kind)).toEqual(["trust", "stop"]);
expect(c.foot).toEqual(["Passed through until trusted"]);
});
test("a tunnel after a refusal names the client that refused", () => {
const after = {
...tunnel,
url: "sync.acme.dev:443",
tunnelReason: "rejected" as const,
};
const c = card({
flow: after,
list: listed,
rejectedBy: { clientName: "Acme", clientBundleId: "dev.acme", clientPath: null },
});
expect(lineText(c.why)).toBe("Acme rejected the certificate for this host");
// Stop Decrypting acts on this connection, so on Dia's own
expect(c.actions[0]?.hint).toBe("!app:Dia");
});
});
describe("Stop Decrypting a refused connection", () => {
const c = connectionOf({ url: "sync.acme.dev:443", clientName: "Acme", clientBundleId: null });
test("takes off the entry naming exactly this host when that alone stops it", () => {
const action = stopDecryptingConnection(user("host:sync.acme.dev"), c);
expect(action?.edit).toEqual({ add: [], remove: ["host:sync.acme.dev"] });
expect(action?.label).toBe("Stop Decrypting sync.acme.dev");
});
test("else excludes the app, or the host without one, which always wins", () => {
expect(stopDecryptingConnection(user("*"), c)?.edit).toEqual({
add: ["!app:Acme"],
remove: [],
});
const anonymous = { ...c, app: null };
expect(stopDecryptingConnection(user("*", "host:sync.acme.dev"), anonymous)?.edit).toEqual({
add: ["!host:sync.acme.dev"],
remove: [],
});
});
test("nothing when the list does not decrypt it", () => {
expect(stopDecryptingConnection(BUILT_INS, c)).toBeNull();
});
});