68 lines
3.5 KiB
TypeScript
68 lines
3.5 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { spawnSync } from "node:child_process";
|
|
import { closeSync, chmodSync, mkdtempSync, openSync, realpathSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join, resolve } from "node:path";
|
|
import { test } from "node:test";
|
|
import { planSecretInput } from "./secret-input.js";
|
|
|
|
const fixture = "synthetic-provisioning-value-not-a-real-secret";
|
|
function protectedInput(body: (dir: string, file: string) => void) {
|
|
const dir = realpathSync(mkdtempSync(join(tmpdir(), "potato-secret-contract-")));
|
|
const file = join(dir, "release-token");
|
|
try {
|
|
chmodSync(dir, 0o700);
|
|
writeFileSync(file, fixture, { mode: 0o600 });
|
|
body(dir, file);
|
|
} finally { rmSync(dir, { recursive: true, force: true }); }
|
|
}
|
|
|
|
for (const invalid of ["public-file", "public-directory", "symlink", "relative", "repository", "missing", "directory", "empty", "login"] as const) {
|
|
test(`protected provisioning plan refuses ${invalid} input without reading or echoing it`, () => protectedInput((dir, file) => {
|
|
let path = file;
|
|
let root = resolve(".");
|
|
let login = "fixture-login";
|
|
if (invalid === "public-file") chmodSync(file, 0o644);
|
|
if (invalid === "public-directory") chmodSync(dir, 0o755);
|
|
if (invalid === "symlink") { path = join(dir, "link"); symlinkSync(file, path); }
|
|
if (invalid === "relative") path = "relative-token";
|
|
if (invalid === "repository") root = dir;
|
|
if (invalid === "missing") path = join(dir, "missing");
|
|
if (invalid === "directory") path = dir;
|
|
if (invalid === "empty") writeFileSync(file, "");
|
|
if (invalid === "login") login = "--debug";
|
|
assert.throws(() => planSecretInput(login, path, "file", root), {
|
|
message: "Invalid protected secret input; no provisioning was performed.",
|
|
});
|
|
}));
|
|
}
|
|
|
|
for (const mode of ["file", "stdin"] as const) {
|
|
test(`protected ${mode} plan follows tea contract without credential argv, logs or actual provisioning`, () => protectedInput((dir, file) => {
|
|
const plan = planSecretInput("fixture-login", file, mode, resolve("."));
|
|
assert.equal(plan.command, "tea");
|
|
assert.deepEqual(plan.args, ["actions", "secrets", "create", "--login", "fixture-login", "--repo", "dmg/purpur-potato",
|
|
...(mode === "file" ? ["--file", file] : ["--stdin"]), "RELEASE_TOKEN"]);
|
|
assert.equal(plan.stdinFile, mode === "stdin" ? file : undefined);
|
|
assert.ok(!JSON.stringify(plan).includes(fixture));
|
|
// Execute only an isolated local fake, NEVER the installed tea command.
|
|
const fake = join(dir, "fake-tea.cjs");
|
|
writeFileSync(fake, `
|
|
const fs = require('node:fs');
|
|
const args = process.argv.slice(2);
|
|
const bytes = args.includes('--stdin') ? fs.readFileSync(0, 'utf8') : fs.readFileSync(args[args.indexOf('--file') + 1], 'utf8');
|
|
if (bytes !== ${JSON.stringify(fixture)} || args.join(' ').includes(bytes)) process.exit(1);
|
|
process.stdout.write('Accepted protected input for RELEASE_TOKEN.\\n');
|
|
`, { mode: 0o600 });
|
|
const fd = plan.stdinFile ? openSync(plan.stdinFile, "r") : undefined;
|
|
try {
|
|
const child = spawnSync(process.execPath, [fake, ...plan.args], {
|
|
env: {}, encoding: "utf8", stdio: [fd ?? "ignore", "pipe", "pipe"],
|
|
});
|
|
assert.equal(child.status, 0, child.stderr);
|
|
assert.equal(child.stdout, "Accepted protected input for RELEASE_TOKEN.\n");
|
|
assert.equal(child.stderr, "");
|
|
} finally { if (fd !== undefined) closeSync(fd); }
|
|
}));
|
|
}
|