mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-28 22:25:24 +00:00
115 lines
3.9 KiB
TypeScript
115 lines
3.9 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
|
|
const execCommand = vi.fn();
|
|
vi.mock(
|
|
"../../../../../src/backend/hosts/metrics-shared/common-utils.js",
|
|
() => ({
|
|
execCommand: (...args: unknown[]) => execCommand(...args),
|
|
}),
|
|
);
|
|
|
|
import {
|
|
execElevated,
|
|
ElevationError,
|
|
} from "../../../../../src/backend/hosts/metrics-shared/exec-elevated.js";
|
|
import type { Client } from "ssh2";
|
|
|
|
const fakeClient = {} as Client;
|
|
|
|
function result(stdout: string, stderr = "", code: number | null = 0) {
|
|
return { stdout, stderr, code };
|
|
}
|
|
|
|
beforeEach(() => {
|
|
execCommand.mockReset();
|
|
});
|
|
|
|
describe("execElevated (no force)", () => {
|
|
it("returns the direct result when the command succeeds unprivileged", async () => {
|
|
execCommand.mockResolvedValueOnce(result("hello", "", 0));
|
|
const r = await execElevated(fakeClient, "echo hello", "pw");
|
|
expect(r.usedSudo).toBe(false);
|
|
expect(r.stdout).toBe("hello");
|
|
expect(execCommand).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("does NOT escalate when stdout merely contains scary words", async () => {
|
|
// A non-zero exit whose OUTPUT contains 'permission denied' but stderr does
|
|
// not should be surfaced as-is, never retried under sudo.
|
|
execCommand.mockResolvedValueOnce(
|
|
result("log line: permission denied for user foo", "", 1),
|
|
);
|
|
const r = await execElevated(fakeClient, "grep denied /tmp/app.log", "pw");
|
|
expect(r.usedSudo).toBe(false);
|
|
expect(execCommand).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("escalates when stderr indicates a permission problem", async () => {
|
|
execCommand
|
|
.mockResolvedValueOnce(result("", "Permission denied", 1))
|
|
.mockResolvedValueOnce(result("__TX_SUDO_OK__\nelevated output", "", 0));
|
|
const r = await execElevated(fakeClient, "cat /etc/shadow", "pw");
|
|
expect(r.usedSudo).toBe(true);
|
|
expect(r.stdout).toBe("elevated output");
|
|
expect(execCommand).toHaveBeenCalledTimes(2);
|
|
});
|
|
|
|
it("throws SUDO_REQUIRED when elevation is needed but no password is set", async () => {
|
|
execCommand.mockResolvedValueOnce(result("", "Permission denied", 1));
|
|
await expect(
|
|
execElevated(fakeClient, "cat /etc/shadow", undefined),
|
|
).rejects.toMatchObject({ code: "SUDO_REQUIRED" });
|
|
});
|
|
});
|
|
|
|
describe("execElevated (forced)", () => {
|
|
it("strips the success marker from stdout", async () => {
|
|
execCommand.mockResolvedValueOnce(
|
|
result("__TX_SUDO_OK__\nthe real output\n", "", 0),
|
|
);
|
|
const r = await execElevated(fakeClient, "id", "pw", { forceSudo: true });
|
|
expect(r.stdout).toBe("the real output\n");
|
|
expect(r.usedSudo).toBe(true);
|
|
});
|
|
|
|
it("does NOT throw when command output contains 'incorrect password' but sudo authenticated", async () => {
|
|
execCommand.mockResolvedValueOnce(
|
|
result(
|
|
"__TX_SUDO_OK__\nUser entered an incorrect password earlier",
|
|
"",
|
|
0,
|
|
),
|
|
);
|
|
const r = await execElevated(fakeClient, "tail /var/log/auth.log", "pw", {
|
|
forceSudo: true,
|
|
});
|
|
expect(r.usedSudo).toBe(true);
|
|
expect(r.stdout).toContain("incorrect password");
|
|
});
|
|
|
|
it("throws SUDO_FAILED on a real wrong-password (no marker, sudo stderr)", async () => {
|
|
execCommand.mockResolvedValueOnce(
|
|
result("", "sudo: 1 incorrect password attempt", 1),
|
|
);
|
|
await expect(
|
|
execElevated(fakeClient, "id", "wrong", { forceSudo: true }),
|
|
).rejects.toMatchObject({ code: "SUDO_FAILED" });
|
|
});
|
|
|
|
it("throws NOT_SUDOER when the user is not in sudoers", async () => {
|
|
execCommand.mockResolvedValueOnce(
|
|
result("", "deploy is not in the sudoers file.", 1),
|
|
);
|
|
await expect(
|
|
execElevated(fakeClient, "id", "pw", { forceSudo: true }),
|
|
).rejects.toMatchObject({ code: "NOT_SUDOER" });
|
|
});
|
|
|
|
it("throws SUDO_REQUIRED when forced without a password", async () => {
|
|
await expect(
|
|
execElevated(fakeClient, "id", undefined, { forceSudo: true }),
|
|
).rejects.toBeInstanceOf(ElevationError);
|
|
expect(execCommand).not.toHaveBeenCalled();
|
|
});
|
|
});
|