mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-29 14:45:24 +00:00
feat: add plugin packing, signing, pinned trust and tmxplug bundling
This commit is contained in:
1 parent
c6a44b7ff3
commit
2ee9b3592c
25 files changed
+1764
-56
No files matched your search
@@ -0,0 +1,114 @@
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import {
|
||||
keygen,
|
||||
sign,
|
||||
verify,
|
||||
} from "../packages/plugin-sdk/cli/commands/sign.mjs";
|
||||
import {
|
||||
generateKeyPair,
|
||||
keyId,
|
||||
loadPrivateKey,
|
||||
signArtifact,
|
||||
verifyArtifact,
|
||||
} from "../packages/plugin-sdk/cli/lib/signing.mjs";
|
||||
|
||||
const cleanups: Array<() => void> = [];
|
||||
|
||||
function tempDir(): string {
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "termix-sign-"));
|
||||
cleanups.push(() => fs.rmSync(dir, { recursive: true, force: true }));
|
||||
return dir;
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
while (cleanups.length) cleanups.pop()?.();
|
||||
});
|
||||
|
||||
describe("signing helpers", () => {
|
||||
const artifact = Buffer.from("plugin bytes");
|
||||
|
||||
it("verifies a signature from the matching key", () => {
|
||||
const pair = generateKeyPair();
|
||||
const signature = signArtifact(artifact, loadPrivateKey(pair.privateKey));
|
||||
expect(verifyArtifact(artifact, signature, [pair.publicKey])).toBe(
|
||||
pair.keyId,
|
||||
);
|
||||
expect(keyId(pair.publicKey)).toMatch(/^[0-9a-f]{16}$/);
|
||||
});
|
||||
|
||||
it("rejects a tampered artifact", () => {
|
||||
const pair = generateKeyPair();
|
||||
const signature = signArtifact(artifact, loadPrivateKey(pair.privateKey));
|
||||
expect(
|
||||
verifyArtifact(Buffer.from("plugin bytez"), signature, [pair.publicKey]),
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it("rejects the wrong key", () => {
|
||||
const signer = generateKeyPair();
|
||||
const other = generateKeyPair();
|
||||
const signature = signArtifact(artifact, loadPrivateKey(signer.privateKey));
|
||||
expect(verifyArtifact(artifact, signature, [other.publicKey])).toBeNull();
|
||||
expect(
|
||||
verifyArtifact(artifact, signature, [other.publicKey, signer.publicKey]),
|
||||
).toBe(signer.keyId);
|
||||
});
|
||||
|
||||
it("refuses a missing or non-Ed25519 key", () => {
|
||||
expect(() => loadPrivateKey(undefined)).toThrow(/not set/);
|
||||
expect(() => loadPrivateKey("not a key")).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe("keygen, sign and verify commands", () => {
|
||||
it("round trips and never prints the private key", async () => {
|
||||
const log = vi.spyOn(console, "log").mockImplementation(() => {});
|
||||
const dir = tempDir();
|
||||
const file = path.join(dir, "demo-1.0.0.tmxplug");
|
||||
fs.writeFileSync(file, "archive");
|
||||
|
||||
const { keyFile, publicKey } = await keygen({
|
||||
cwd: dir,
|
||||
args: ["--out", dir],
|
||||
});
|
||||
const privateKey = fs.readFileSync(keyFile, "utf8").trim();
|
||||
expect(log.mock.calls.flat().join("\n")).not.toContain(privateKey);
|
||||
|
||||
await sign({
|
||||
cwd: dir,
|
||||
args: [file],
|
||||
env: { TERMIX_PLUGIN_SIGNING_KEY: privateKey },
|
||||
});
|
||||
expect(fs.existsSync(`${file}.sig`)).toBe(true);
|
||||
await expect(
|
||||
verify({ cwd: dir, args: [file, "--key", publicKey] }),
|
||||
).resolves.toMatch(/^[0-9a-f]{16}$/);
|
||||
|
||||
fs.writeFileSync(file, "archivE");
|
||||
await expect(
|
||||
verify({ cwd: dir, args: [file, "--key", publicKey] }),
|
||||
).rejects.toThrow(/not valid/);
|
||||
});
|
||||
|
||||
it("refuses to sign without the key env var", async () => {
|
||||
const dir = tempDir();
|
||||
const file = path.join(dir, "demo-1.0.0.tmxplug");
|
||||
fs.writeFileSync(file, "archive");
|
||||
await expect(sign({ cwd: dir, args: [file], env: {} })).rejects.toThrow(
|
||||
/TERMIX_PLUGIN_SIGNING_KEY is not set/,
|
||||
);
|
||||
});
|
||||
|
||||
it("will not overwrite an existing key file", async () => {
|
||||
vi.spyOn(console, "log").mockImplementation(() => {});
|
||||
const dir = tempDir();
|
||||
await keygen({ cwd: dir, args: ["--out", dir] });
|
||||
await expect(keygen({ cwd: dir, args: ["--out", dir] })).rejects.toThrow(
|
||||
/already exists/,
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user