Fresh history cut from cynarAI/evoo antigravity-handoff/project-timeline (Voice First + project timeline). Brand docs/titles as evOo; keep @rakazo/* packages as tech debt. Excludes node_modules, caches, secrets, voice blobs.
124 lines
4.2 KiB
TypeScript
124 lines
4.2 KiB
TypeScript
import { constants } from "node:fs";
|
|
import {
|
|
chmod,
|
|
lstat,
|
|
mkdir,
|
|
mkdtemp,
|
|
open,
|
|
rename,
|
|
rm,
|
|
symlink,
|
|
writeFile,
|
|
} from "node:fs/promises";
|
|
import { tmpdir } from "node:os";
|
|
import path from "node:path";
|
|
import { afterEach, describe, expect, it } from "vitest";
|
|
import { assertComputerHomeWritable, assertOpenedDirectoryBeneathRoot } from "./home-ownership.js";
|
|
|
|
const roots: string[] = [];
|
|
const DIRECTORY_OPEN_FLAGS = constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW;
|
|
|
|
afterEach(async () => {
|
|
await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
|
|
});
|
|
|
|
describe("computer home ownership", () => {
|
|
it("rejects a missing home instead of creating it as root", async () => {
|
|
const parent = await mkdtemp(path.join(tmpdir(), "rakazo-home-missing-"));
|
|
roots.push(parent);
|
|
|
|
await expect(
|
|
assertComputerHomeWritable(path.join(parent, "missing"), 1000, 1000),
|
|
).rejects.toThrow(/does not exist/);
|
|
});
|
|
|
|
it("rejects a symlink as the home root", async () => {
|
|
const parent = await mkdtemp(path.join(tmpdir(), "rakazo-home-root-link-"));
|
|
roots.push(parent);
|
|
const outside = path.join(parent, "outside");
|
|
const home = path.join(parent, "home");
|
|
await mkdir(outside);
|
|
await symlink(outside, home);
|
|
|
|
await expect(assertComputerHomeWritable(home, 1000, 1000)).rejects.toThrow(/symbolic link/);
|
|
});
|
|
|
|
it("rejects a writable regular file as the home root", async () => {
|
|
const parent = await mkdtemp(path.join(tmpdir(), "rakazo-home-file-root-"));
|
|
roots.push(parent);
|
|
const home = path.join(parent, "home");
|
|
await writeFile(home, "{}");
|
|
await chmod(home, 0o600);
|
|
|
|
const stat = await lstat(home);
|
|
await expect(assertComputerHomeWritable(home, stat.uid, stat.gid)).rejects.toThrow(
|
|
/must be a directory/,
|
|
);
|
|
});
|
|
|
|
it("rejects an existing entry that the host-run computer cannot write", async () => {
|
|
const parent = await mkdtemp(path.join(tmpdir(), "rakazo-home-writable-"));
|
|
roots.push(parent);
|
|
const home = path.join(parent, "home");
|
|
const file = path.join(home, "profile.json");
|
|
await mkdir(home);
|
|
await chmod(home, 0o777);
|
|
await writeFile(file, "{}");
|
|
await chmod(file, 0o400);
|
|
|
|
const stat = await lstat(home);
|
|
await expect(assertComputerHomeWritable(home, stat.uid + 1, stat.gid + 1)).rejects.toThrow(
|
|
/chown -R/,
|
|
);
|
|
});
|
|
|
|
it("rejects an owner-owned file that is not writable by that owner", async () => {
|
|
const parent = await mkdtemp(path.join(tmpdir(), "rakazo-home-owner-mode-"));
|
|
roots.push(parent);
|
|
const home = path.join(parent, "home");
|
|
const file = path.join(home, "profile.json");
|
|
await mkdir(home);
|
|
await writeFile(file, "{}");
|
|
await chmod(file, 0o400);
|
|
|
|
const stat = await lstat(file);
|
|
await expect(assertComputerHomeWritable(home, stat.uid, stat.gid)).rejects.toThrow(/chown -R/);
|
|
});
|
|
|
|
it("does not follow symlinks while checking host-run compatibility", async () => {
|
|
const parent = await mkdtemp(path.join(tmpdir(), "rakazo-home-writable-link-"));
|
|
roots.push(parent);
|
|
const home = path.join(parent, "home");
|
|
const outside = path.join(parent, "outside");
|
|
await mkdir(home);
|
|
await writeFile(outside, "outside");
|
|
await chmod(outside, 0o400);
|
|
await symlink(outside, path.join(home, "link"));
|
|
|
|
const stat = await lstat(home);
|
|
await expect(assertComputerHomeWritable(home, stat.uid, stat.gid)).resolves.toBeUndefined();
|
|
});
|
|
|
|
it.skipIf(process.platform !== "linux")(
|
|
"rejects an opened directory that was moved outside the home",
|
|
async () => {
|
|
const parent = await mkdtemp(path.join(tmpdir(), "rakazo-home-moved-"));
|
|
roots.push(parent);
|
|
const home = path.join(parent, "home");
|
|
const outside = path.join(parent, "outside");
|
|
const child = path.join(home, "nested");
|
|
await mkdir(child, { recursive: true });
|
|
await mkdir(outside, { recursive: true });
|
|
const handle = await open(child, DIRECTORY_OPEN_FLAGS);
|
|
try {
|
|
await rename(child, path.join(outside, "nested"));
|
|
await expect(assertOpenedDirectoryBeneathRoot(handle, home)).rejects.toThrow(
|
|
/escaped validated root/,
|
|
);
|
|
} finally {
|
|
await handle.close();
|
|
}
|
|
},
|
|
);
|
|
});
|