[Init] Initial commit - NetMesh terminal manager
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled

This commit is contained in:
2026-09-13 18:24:01 +08:00
commit 3c72efcb7f
3255 changed files with 907009 additions and 0 deletions

View File

@@ -0,0 +1,263 @@
import test from "node:test";
import assert from "node:assert/strict";
import type { SSHKey } from "../../domain/models";
import { ENCRYPTED_CREDENTIAL_PLACEHOLDER } from "../../domain/credentialsTestFixtures";
import { STORAGE_KEY_KEYS } from "../config/storageKeys";
import { isEncryptedCredentialPlaceholder, sanitizeCredentialValue } from "../../domain/credentials";
import { localStorageAdapter } from "./localStorageAdapter.ts";
import {
decryptField,
decryptFieldResult,
decryptKeySecrets,
decryptKeys,
encryptKeys,
hydrateStoredKeySecrets,
notifyKeysEncryptedWritePending,
} from "./secureFieldAdapter.ts";
const PRIVATE_KEY = "-----BEGIN OPENSSH PRIVATE KEY-----\nsecret\n-----END OPENSSH PRIVATE KEY-----";
const storedKey = (overrides: Partial<SSHKey> = {}): SSHKey => ({
id: "key-1",
label: "Imported",
type: "ED25519",
privateKey: ENCRYPTED_CREDENTIAL_PLACEHOLDER,
source: "imported",
category: "key",
created: 1,
...overrides,
});
function installLocalStorage(t: test.TestContext): Map<string, string> {
const store = new Map<string, string>();
const storage: Storage = {
get length() {
return store.size;
},
clear() {
store.clear();
},
getItem(key: string) {
return store.get(key) ?? null;
},
key(index: number) {
return Array.from(store.keys())[index] ?? null;
},
removeItem(key: string) {
store.delete(key);
},
setItem(key: string, value: string) {
store.set(key, value);
},
};
const previousLocalStorage = Object.getOwnPropertyDescriptor(globalThis, "localStorage");
Object.defineProperty(globalThis, "localStorage", {
configurable: true,
value: storage,
});
t.after(() => {
if (previousLocalStorage) Object.defineProperty(globalThis, "localStorage", previousLocalStorage);
else delete (globalThis as { localStorage?: Storage }).localStorage;
});
return store;
}
function installBridge(
t: test.TestContext,
netcatty: { credentialsDecrypt?: (value: string) => Promise<string> } | undefined,
): void {
const previousWindow = Object.getOwnPropertyDescriptor(globalThis, "window");
Object.defineProperty(globalThis, "window", {
configurable: true,
value: { netcatty },
});
t.after(() => {
if (previousWindow) Object.defineProperty(globalThis, "window", previousWindow);
else delete (globalThis as { window?: unknown }).window;
});
}
test("decryptField marks enc:v1 unread without treating it as plaintext when the bridge is missing", async (t) => {
installBridge(t, undefined);
const result = await decryptFieldResult(ENCRYPTED_CREDENTIAL_PLACEHOLDER);
assert.equal(result.unread, true);
assert.equal(result.value, ENCRYPTED_CREDENTIAL_PLACEHOLDER);
assert.equal(await decryptField(ENCRYPTED_CREDENTIAL_PLACEHOLDER), ENCRYPTED_CREDENTIAL_PLACEHOLDER);
assert.equal(sanitizeCredentialValue(result.value), undefined);
assert.equal(await decryptField("plain-secret"), "plain-secret");
assert.equal((await decryptFieldResult("plain-secret")).unread, false);
});
test("decryptField marks enc:v1 unread when decrypt returns the same value", async (t) => {
installBridge(t, {
credentialsDecrypt: async (value: string) => value,
});
const result = await decryptFieldResult(ENCRYPTED_CREDENTIAL_PLACEHOLDER);
assert.equal(result.unread, true);
assert.equal(result.value, ENCRYPTED_CREDENTIAL_PLACEHOLDER);
assert.equal(sanitizeCredentialValue(result.value), undefined);
});
test("decryptField keeps enc:v1 ciphertext when decrypt throws", async (t) => {
installBridge(t, {
credentialsDecrypt: async () => {
throw new Error("safeStorage unavailable");
},
});
const result = await decryptFieldResult(ENCRYPTED_CREDENTIAL_PLACEHOLDER);
assert.equal(result.unread, true);
assert.equal(result.value, ENCRYPTED_CREDENTIAL_PLACEHOLDER);
});
test("decryptField returns plaintext once the credential bridge decrypts", async (t) => {
installBridge(t, {
credentialsDecrypt: async (value: string) => {
assert.equal(value, ENCRYPTED_CREDENTIAL_PLACEHOLDER);
return PRIVATE_KEY;
},
});
const result = await decryptFieldResult(ENCRYPTED_CREDENTIAL_PLACEHOLDER);
assert.equal(result.unread, false);
assert.equal(result.value, PRIVATE_KEY);
assert.equal(await decryptField(ENCRYPTED_CREDENTIAL_PLACEHOLDER), PRIVATE_KEY);
});
test("decryptKeySecrets keeps enc:v1 privateKey ciphertext until decrypt succeeds", async (t) => {
installBridge(t, undefined);
const decrypted = await decryptKeySecrets(storedKey());
assert.equal(decrypted.privateKey, ENCRYPTED_CREDENTIAL_PLACEHOLDER);
assert.equal(isEncryptedCredentialPlaceholder(decrypted.privateKey), true);
});
test("failed decrypt does not persist wiping enc:v1 key material", async (t) => {
installLocalStorage(t);
installBridge(t, undefined);
const decrypted = await decryptKeys([storedKey()]);
assert.equal(decrypted[0]?.privateKey, ENCRYPTED_CREDENTIAL_PLACEHOLDER);
const encrypted = await encryptKeys(decrypted);
assert.equal(encrypted[0]?.privateKey, ENCRYPTED_CREDENTIAL_PLACEHOLDER);
localStorageAdapter.write(STORAGE_KEY_KEYS, encrypted);
const stored = localStorageAdapter.read<SSHKey[]>(STORAGE_KEY_KEYS);
assert.equal(stored?.[0]?.privateKey, ENCRYPTED_CREDENTIAL_PLACEHOLDER);
});
test("hydrateStoredKeySecrets waits until ciphertext decrypts", async (t) => {
installLocalStorage(t);
let attempts = 0;
installBridge(t, {
credentialsDecrypt: async (value: string) => {
attempts += 1;
if (attempts < 3) return value;
return PRIVATE_KEY;
},
});
const hydrated = await hydrateStoredKeySecrets(storedKey(), {
timeoutMs: 500,
retryDelayMs: 10,
});
assert.equal(hydrated.unreadable, false);
assert.equal(hydrated.key.privateKey, PRIVATE_KEY);
assert.ok(attempts >= 3);
});
test("hydrateStoredKeySecrets re-reads storage when in-memory privateKey was stripped", async (t) => {
const store = installLocalStorage(t);
store.set(STORAGE_KEY_KEYS, JSON.stringify([storedKey()]));
installBridge(t, {
credentialsDecrypt: async (value: string) => {
assert.equal(value, ENCRYPTED_CREDENTIAL_PLACEHOLDER);
return PRIVATE_KEY;
},
});
const hydrated = await hydrateStoredKeySecrets(storedKey({ privateKey: "" }), {
timeoutMs: 100,
retryDelayMs: 10,
});
assert.equal(hydrated.unreadable, false);
assert.equal(hydrated.key.privateKey, PRIVATE_KEY);
});
test("hydrateStoredKeySecrets does not revive a stale persisted key while the vault write is pending", async (t) => {
const store = installLocalStorage(t);
store.set(
STORAGE_KEY_KEYS,
JSON.stringify([storedKey({ privateKey: ENCRYPTED_CREDENTIAL_PLACEHOLDER })]),
);
installBridge(t, {
credentialsDecrypt: async (value: string) => {
assert.equal(value, ENCRYPTED_CREDENTIAL_PLACEHOLDER);
return PRIVATE_KEY;
},
});
const keyAfterRecovery = storedKey({ privateKey: "" });
let landWrite: () => void = () => {};
const pendingWrite = new Promise<void>((resolve) => {
landWrite = () => {
// A sync/import recovery replaced the key with an empty private key; the
// async encrypted write publishes that state when it lands.
store.set(STORAGE_KEY_KEYS, JSON.stringify([keyAfterRecovery]));
resolve();
};
});
notifyKeysEncryptedWritePending(pendingWrite);
t.after(() => notifyKeysEncryptedWritePending(null));
let hydrationSettled = false;
const hydration = hydrateStoredKeySecrets(keyAfterRecovery, {
timeoutMs: 2000,
retryDelayMs: 10,
}).then((result) => {
hydrationSettled = true;
return result;
});
await new Promise((resolve) => setTimeout(resolve, 50));
// Without the gate the stale persisted ciphertext would hydrate immediately.
assert.equal(hydrationSettled, false);
landWrite();
const hydrated = await hydration;
assert.equal(hydrated.unreadable, false);
assert.equal(hydrated.key.privateKey, "");
assert.equal(hydrated.key.passphrase, undefined);
});
test("hydrateStoredKeySecrets hydrates from settled storage after the vault write lands", async (t) => {
const store = installLocalStorage(t);
installBridge(t, {
credentialsDecrypt: async () => PRIVATE_KEY,
});
let landWrite: () => void = () => {};
const pendingWrite = new Promise<void>((resolve) => {
landWrite = () => {
store.set(STORAGE_KEY_KEYS, JSON.stringify([storedKey()]));
resolve();
};
});
notifyKeysEncryptedWritePending(pendingWrite);
t.after(() => notifyKeysEncryptedWritePending(null));
const hydration = hydrateStoredKeySecrets(storedKey({ privateKey: "" }), {
timeoutMs: 2000,
retryDelayMs: 10,
});
landWrite();
const hydrated = await hydration;
assert.equal(hydrated.unreadable, false);
assert.equal(hydrated.key.privateKey, PRIVATE_KEY);
});
test("hydrateStoredKeySecrets does not wait when there is no decrypt bridge", async (t) => {
installBridge(t, undefined);
const started = Date.now();
const hydrated = await hydrateStoredKeySecrets(storedKey(), {
timeoutMs: 2000,
retryDelayMs: 50,
});
assert.equal(hydrated.unreadable, true);
assert.equal(hydrated.key.privateKey, "");
assert.ok(Date.now() - started < 200);
});