fix(backend): harden principal child isolation
This commit is contained in:
@@ -83,6 +83,31 @@ test("run omits ambient THT_DATA_ROOT when config does not provide one", async (
|
||||
}
|
||||
});
|
||||
|
||||
test("principal-bound tht child replaces stale principal env and omits an absent display name", async () => {
|
||||
const saved = Object.fromEntries([
|
||||
"THT_PRINCIPAL_ISSUER", "THT_PRINCIPAL_SUBJECT", "THT_PRINCIPAL_DISPLAY_NAME", "THT_PRINCIPAL_IS_ADMIN",
|
||||
].map((key) => [key, process.env[key]]));
|
||||
Object.assign(process.env, {
|
||||
THT_PRINCIPAL_ISSUER: "stale-issuer", THT_PRINCIPAL_SUBJECT: "stale-subject",
|
||||
THT_PRINCIPAL_DISPLAY_NAME: "Stale Name", THT_PRINCIPAL_IS_ADMIN: "true",
|
||||
});
|
||||
try {
|
||||
(spawn as any).mockClear();
|
||||
const runner = new ThtRunner({ thtBin: "tht", harnessDir: "/h", configPath: "config/tht.yaml" })
|
||||
.withPrincipal({ issuer: "portal", subject: "42", isAdmin: false });
|
||||
await runner.run(["session", "list", "--json"]);
|
||||
const env = (spawn as any).mock.calls[0][2].env;
|
||||
expect(env).toMatchObject({
|
||||
THT_PRINCIPAL_ISSUER: "portal", THT_PRINCIPAL_SUBJECT: "42", THT_PRINCIPAL_IS_ADMIN: "false",
|
||||
});
|
||||
expect(env).not.toHaveProperty("THT_PRINCIPAL_DISPLAY_NAME");
|
||||
} finally {
|
||||
for (const [key, value] of Object.entries(saved)) {
|
||||
if (value === undefined) delete process.env[key]; else process.env[key] = value;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test("run with exit != 0 propagates error with stderr", async () => {
|
||||
const r = new ThtRunner({ thtBin: "tht", harnessDir: "/h", configPath: "config/tht.yaml" });
|
||||
r.run = async () => ({ code: 1, stdout: "", stderr: "ERRORE: boom" });
|
||||
@@ -137,23 +162,25 @@ test("setName builds the right argv", async () => {
|
||||
const calls: string[][] = [];
|
||||
const r = new ThtRunner({ thtBin: "tht", harnessDir: "/h", configPath: "config/tht.yaml" });
|
||||
r.run = async (args) => { calls.push(args); return { code: 0, stdout: "", stderr: "" }; };
|
||||
await r.setName("sid", "Mio nome");
|
||||
await r.setName("sid", "Mio nome", "tenant-a");
|
||||
expect(calls[0]).toEqual(["session", "set-name", "sid", "--name", "Mio nome"]);
|
||||
});
|
||||
|
||||
test("setGroup / archive / unarchive / deleteSession build argv", async () => {
|
||||
const calls: string[][] = [];
|
||||
test("mutation and document commands retain their requested workspace", async () => {
|
||||
const calls: Array<{ args: string[]; workspace?: string }> = [];
|
||||
const r = new ThtRunner({ thtBin: "tht", harnessDir: "/h", configPath: "config/tht.yaml" });
|
||||
r.run = async (args) => { calls.push(args); return { code: 0, stdout: "", stderr: "" }; };
|
||||
await r.setGroup("sid", "G1");
|
||||
await r.archive("sid");
|
||||
await r.unarchive("sid");
|
||||
await r.deleteSession("sid");
|
||||
r.run = async (args, workspace) => { calls.push({ args, workspace }); return { code: 0, stdout: "[]", stderr: "" }; };
|
||||
await r.setGroup("sid", "G1", "tenant-a");
|
||||
await r.archive("sid", "tenant-a");
|
||||
await r.unarchive("sid", "tenant-a");
|
||||
await r.documents("sid", "tenant-a");
|
||||
await r.deleteSession("sid", "tenant-a");
|
||||
expect(calls).toEqual([
|
||||
["session", "set-group", "sid", "--group", "G1"],
|
||||
["session", "archive", "sid"],
|
||||
["session", "unarchive", "sid"],
|
||||
["session", "delete", "sid"],
|
||||
{ args: ["session", "set-group", "sid", "--group", "G1"], workspace: "tenant-a" },
|
||||
{ args: ["session", "archive", "sid"], workspace: "tenant-a" },
|
||||
{ args: ["session", "unarchive", "sid"], workspace: "tenant-a" },
|
||||
{ args: ["session", "documents", "sid", "--json"], workspace: "tenant-a" },
|
||||
{ args: ["session", "delete", "sid"], workspace: "tenant-a" },
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user