@glassmkr/crucible 0.13.16 → 0.13.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,50 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ // Mock the fs existsSync (wrapper presence) and the exec run() so we can
3
+ // exercise runPrivileged's wrapper-vs-root-direct branching without touching
4
+ // the real filesystem or spawning processes. Keep the rest of each module real.
5
+ vi.mock("fs", async (orig) => ({ ...(await orig()), existsSync: vi.fn() }));
6
+ vi.mock("../exec.js", async (orig) => ({ ...(await orig()), run: vi.fn() }));
7
+ import { existsSync } from "fs";
8
+ import { run } from "../exec.js";
9
+ import { runPrivileged, WRAPPER_PATH } from "../privileged.js";
10
+ const existsMock = existsSync;
11
+ const runMock = run;
12
+ describe("runPrivileged: wrapper vs root-direct fallback", () => {
13
+ beforeEach(() => {
14
+ existsMock.mockReset();
15
+ runMock.mockReset();
16
+ runMock.mockResolvedValue("output");
17
+ });
18
+ it("uses the sudo wrapper when it is installed", async () => {
19
+ existsMock.mockReturnValue(true);
20
+ await runPrivileged("ipmi-sensor");
21
+ expect(runMock).toHaveBeenCalledWith("sudo", ["-n", WRAPPER_PATH, "ipmi-sensor"], expect.any(Number));
22
+ });
23
+ it("falls back to the direct command when the wrapper is absent and we are root", async () => {
24
+ existsMock.mockReturnValue(false);
25
+ const getuid = vi.spyOn(process, "getuid").mockReturnValue(0);
26
+ await runPrivileged("ipmi-sensor");
27
+ expect(runMock).toHaveBeenCalledWith("ipmitool", ["sensor"], expect.any(Number));
28
+ getuid.mockRestore();
29
+ });
30
+ it("validates the smart device path on the direct fallback", async () => {
31
+ existsMock.mockReturnValue(false);
32
+ const getuid = vi.spyOn(process, "getuid").mockReturnValue(0);
33
+ await runPrivileged("smart", ["/dev/sda"]);
34
+ expect(runMock).toHaveBeenCalledWith("smartctl", ["--json", "--all", "/dev/sda"], expect.any(Number));
35
+ runMock.mockClear();
36
+ const rejected = await runPrivileged("smart", ["/etc/shadow"]);
37
+ expect(rejected).toBeNull();
38
+ expect(runMock).not.toHaveBeenCalled();
39
+ getuid.mockRestore();
40
+ });
41
+ it("returns null when the wrapper is absent and we are NOT root (no escalation)", async () => {
42
+ existsMock.mockReturnValue(false);
43
+ const getuid = vi.spyOn(process, "getuid").mockReturnValue(1000);
44
+ const r = await runPrivileged("ipmi-sensor");
45
+ expect(r).toBeNull();
46
+ expect(runMock).not.toHaveBeenCalled();
47
+ getuid.mockRestore();
48
+ });
49
+ });
50
+ //# sourceMappingURL=run-privileged.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-privileged.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/run-privileged.test.ts"],"names":[],"mappings":"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"}
@@ -10,12 +10,21 @@ export declare function isAllowedSmartDevice(dev: string): boolean;
10
10
  * chars from a restricted set; no slashes/spaces so no arg injection. */
11
11
  export declare function isAllowedIface(name: string): boolean;
12
12
  /**
13
- * Invoke a privileged collection action through the sudo wrapper. Returns
14
- * the command stdout, or null on any failure (wrapper/sudoers not installed,
15
- * tool absent, non-zero exit) - callers already treat null as "capability
16
- * unavailable", so a missing wrapper degrades gracefully rather than
17
- * crashing. When the agent still runs as root (pre-migration or old installs)
18
- * `sudo -n` simply runs the wrapper without a password prompt.
13
+ * Invoke a privileged collection action. Preferred path is the fixed-argv
14
+ * sudo wrapper (the unprivileged `glassmkr` service user escalates only
15
+ * through it). Returns the command stdout, or null on any failure - callers
16
+ * treat null as "capability unavailable", so a missing tool degrades
17
+ * gracefully rather than crashing.
18
+ *
19
+ * Root fallback: if the wrapper is NOT installed (e.g. an upgrade via
20
+ * `npm i -g` without re-running `init`, or a host still on User=root that
21
+ * never migrated) AND we are running as root, run the underlying command
22
+ * directly. As root that is exactly what the wrapper would exec, so it
23
+ * restores collection instead of silently returning null for every
24
+ * hardware/security probe. This honors init.ts's documented "User=root
25
+ * never loses collection" invariant, which the wrapper-only path (audit
26
+ * §2.1) had regressed on wrapper-less root hosts. A non-root agent without
27
+ * the wrapper still gets null (it cannot and must not escalate).
19
28
  */
20
29
  export declare function runPrivileged(action: PrivilegedAction, args?: string[], timeoutMs?: number): Promise<string | null>;
21
30
  export declare const WRAPPER_SCRIPT = "#!/bin/sh\n# Glassmkr Crucible privileged-collection facade (audit \u00A72.1).\n# Installed root:root 0755. The sole sudo grant for the unprivileged\n# 'glassmkr' service user. Fixed action -> fixed argv; no passthrough.\n# Managed by glassmkr-crucible init; do not edit by hand.\nset -eu\n\naction=\"${1:-}\"\n[ -n \"$action\" ] || { echo \"crucible-collect: missing action\" >&2; exit 64; }\nshift 2>/dev/null || true\n\nvalid_device() {\n case \"$1\" in\n /dev/sd[a-z]|/dev/sd[a-z][a-z]|/dev/hd[a-z]|/dev/hd[a-z][a-z]) return 0 ;;\n /dev/nvme[0-9]*n[0-9]*|/dev/nvme[0-9]*) return 0 ;;\n /dev/bus/[0-9]*) return 0 ;;\n *) return 1 ;;\n esac\n}\nvalid_iface() {\n case \"$1\" in\n \"\" | *[!A-Za-z0-9._:-]*) return 1 ;;\n *) [ \"${#1}\" -le 15 ] ;;\n esac\n}\n\ncase \"$action\" in\n ipmi-sensor) exec ipmitool sensor ;;\n ipmi-sel-info) exec ipmitool sel info ;;\n ipmi-sel-elist) exec ipmitool sel elist ;;\n ipmi-fan) exec ipmitool sdr type Fan ;;\n smart)\n dev=\"${1:-}\"\n valid_device \"$dev\" || { echo \"crucible-collect: rejected device: $dev\" >&2; exit 65; }\n exec smartctl --json --all \"$dev\" ;;\n zpool) exec zpool status ;;\n raid-perccli) exec perccli /c0 show all J ;;\n raid-storcli) exec storcli /call show all J ;;\n raid-ssacli) exec ssacli ctrl all show status ;;\n raid-arcconf) exec arcconf list ;;\n dmesg-errcrit) exec dmesg --level=err,crit --since \"5 min ago\" ;;\n dmesg-io) exec sh -c 'dmesg -T --since \"10 minutes ago\" 2>/dev/null | grep -i \"I/O error\\\\|Buffer I/O error\\\\|blk_update_request.*error\"' ;;\n iptables) exec iptables -L -n ;;\n nft) exec nft list ruleset ;;\n ufw) exec ufw status ;;\n firewall-cmd) exec firewall-cmd --state ;;\n pve-firewall) exec pve-firewall status ;;\n sshd) exec sshd -T ;;\n lvs) exec lvs --reportformat=json --options=lv_name,vg_name,lv_attr,data_percent,metadata_percent --units=b --noheadings ;;\n ethtool)\n ifc=\"${1:-}\"\n valid_iface \"$ifc\" || { echo \"crucible-collect: rejected iface: $ifc\" >&2; exit 65; }\n exec ethtool \"$ifc\" ;;\n last) exec last -x -F ;;\n *) echo \"crucible-collect: unknown action: $action\" >&2; exit 64 ;;\nesac\n";
@@ -19,6 +19,7 @@
19
19
  // root:root 0755 (not writable by `glassmkr`) so the sudo grant cannot be
20
20
  // hijacked by tampering with the script.
21
21
  import { run } from "./exec.js";
22
+ import { existsSync } from "fs";
22
23
  export const SERVICE_USER = "glassmkr";
23
24
  export const WRAPPER_PATH = "/usr/local/sbin/crucible-collect";
24
25
  export const SUDOERS_PATH = "/etc/sudoers.d/glassmkr-crucible";
@@ -34,15 +35,65 @@ export function isAllowedIface(name) {
34
35
  return name.length > 0 && name.length <= 15 && /^[A-Za-z0-9._:-]+$/.test(name);
35
36
  }
36
37
  /**
37
- * Invoke a privileged collection action through the sudo wrapper. Returns
38
- * the command stdout, or null on any failure (wrapper/sudoers not installed,
39
- * tool absent, non-zero exit) - callers already treat null as "capability
40
- * unavailable", so a missing wrapper degrades gracefully rather than
41
- * crashing. When the agent still runs as root (pre-migration or old installs)
42
- * `sudo -n` simply runs the wrapper without a password prompt.
38
+ * The real command each action maps to, mirroring WRAPPER_SCRIPT's `case`
39
+ * statement below. Used only for the root fallback in runPrivileged; the
40
+ * parameterized actions re-validate their argument so the fallback is as
41
+ * constrained as the wrapper. Returns null for an action we won't run
42
+ * directly (unknown, or a rejected smart/ethtool argument).
43
+ */
44
+ function directCommand(action, args) {
45
+ switch (action) {
46
+ case "ipmi-sensor": return { cmd: "ipmitool", args: ["sensor"] };
47
+ case "ipmi-sel-info": return { cmd: "ipmitool", args: ["sel", "info"] };
48
+ case "ipmi-sel-elist": return { cmd: "ipmitool", args: ["sel", "elist"] };
49
+ case "ipmi-fan": return { cmd: "ipmitool", args: ["sdr", "type", "Fan"] };
50
+ case "smart": return isAllowedSmartDevice(args[0] ?? "") ? { cmd: "smartctl", args: ["--json", "--all", args[0]] } : null;
51
+ case "zpool": return { cmd: "zpool", args: ["status"] };
52
+ case "raid-perccli": return { cmd: "perccli", args: ["/c0", "show", "all", "J"] };
53
+ case "raid-storcli": return { cmd: "storcli", args: ["/call", "show", "all", "J"] };
54
+ case "raid-ssacli": return { cmd: "ssacli", args: ["ctrl", "all", "show", "status"] };
55
+ case "raid-arcconf": return { cmd: "arcconf", args: ["list"] };
56
+ case "dmesg-errcrit": return { cmd: "dmesg", args: ["--level=err,crit", "--since", "5 min ago"] };
57
+ case "dmesg-io": return { cmd: "sh", args: ["-c", 'dmesg -T --since "10 minutes ago" 2>/dev/null | grep -i "I/O error\\|Buffer I/O error\\|blk_update_request.*error"'] };
58
+ case "iptables": return { cmd: "iptables", args: ["-L", "-n"] };
59
+ case "nft": return { cmd: "nft", args: ["list", "ruleset"] };
60
+ case "ufw": return { cmd: "ufw", args: ["status"] };
61
+ case "firewall-cmd": return { cmd: "firewall-cmd", args: ["--state"] };
62
+ case "pve-firewall": return { cmd: "pve-firewall", args: ["status"] };
63
+ case "sshd": return { cmd: "sshd", args: ["-T"] };
64
+ case "lvs": return { cmd: "lvs", args: ["--reportformat=json", "--options=lv_name,vg_name,lv_attr,data_percent,metadata_percent", "--units=b", "--noheadings"] };
65
+ case "ethtool": return isAllowedIface(args[0] ?? "") ? { cmd: "ethtool", args: [args[0]] } : null;
66
+ case "last": return { cmd: "last", args: ["-x", "-F"] };
67
+ default: return null;
68
+ }
69
+ }
70
+ /**
71
+ * Invoke a privileged collection action. Preferred path is the fixed-argv
72
+ * sudo wrapper (the unprivileged `glassmkr` service user escalates only
73
+ * through it). Returns the command stdout, or null on any failure - callers
74
+ * treat null as "capability unavailable", so a missing tool degrades
75
+ * gracefully rather than crashing.
76
+ *
77
+ * Root fallback: if the wrapper is NOT installed (e.g. an upgrade via
78
+ * `npm i -g` without re-running `init`, or a host still on User=root that
79
+ * never migrated) AND we are running as root, run the underlying command
80
+ * directly. As root that is exactly what the wrapper would exec, so it
81
+ * restores collection instead of silently returning null for every
82
+ * hardware/security probe. This honors init.ts's documented "User=root
83
+ * never loses collection" invariant, which the wrapper-only path (audit
84
+ * §2.1) had regressed on wrapper-less root hosts. A non-root agent without
85
+ * the wrapper still gets null (it cannot and must not escalate).
43
86
  */
44
87
  export function runPrivileged(action, args = [], timeoutMs = 10000) {
45
- return run("sudo", ["-n", WRAPPER_PATH, action, ...args], timeoutMs);
88
+ if (existsSync(WRAPPER_PATH)) {
89
+ return run("sudo", ["-n", WRAPPER_PATH, action, ...args], timeoutMs);
90
+ }
91
+ if (typeof process.getuid === "function" && process.getuid() === 0) {
92
+ const direct = directCommand(action, args);
93
+ if (direct)
94
+ return run(direct.cmd, direct.args, timeoutMs);
95
+ }
96
+ return Promise.resolve(null);
46
97
  }
47
98
  // The wrapper script, written verbatim to WRAPPER_PATH by init. POSIX sh.
48
99
  // Every case `exec`s a fixed command; the two parameterised cases validate
@@ -1 +1 @@
1
- {"version":3,"file":"privileged.js","sourceRoot":"","sources":["../../src/lib/privileged.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,EAAE;AACF,oEAAoE;AACpE,4EAA4E;AAC5E,8EAA8E;AAC9E,EAAE;AACF,yEAAyE;AACzE,6EAA6E;AAC7E,qEAAqE;AACrE,2EAA2E;AAC3E,uEAAuE;AACvE,4EAA4E;AAC5E,8EAA8E;AAC9E,8BAA8B;AAC9B,EAAE;AACF,4EAA4E;AAC5E,wEAAwE;AACxE,uEAAuE;AACvE,0EAA0E;AAC1E,yCAAyC;AAEzC,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAEhC,MAAM,CAAC,MAAM,YAAY,GAAG,UAAU,CAAC;AACvC,MAAM,CAAC,MAAM,YAAY,GAAG,kCAAkC,CAAC;AAC/D,MAAM,CAAC,MAAM,YAAY,GAAG,kCAAkC,CAAC;AAa/D;;iEAEiE;AACjE,MAAM,UAAU,oBAAoB,CAAC,GAAW;IAC9C,OAAO,sDAAsD,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC1E,CAAC;AAED;0EAC0E;AAC1E,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,MAAM,IAAI,EAAE,IAAI,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACjF,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAC3B,MAAwB,EACxB,OAAiB,EAAE,EACnB,SAAS,GAAG,KAAK;IAEjB,OAAO,GAAG,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,SAAS,CAAC,CAAC;AACvE,CAAC;AAED,0EAA0E;AAC1E,2EAA2E;AAC3E,6EAA6E;AAC7E,wCAAwC;AACxC,MAAM,CAAC,MAAM,cAAc,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwD7B,CAAC;AAEF,sEAAsE;AACtE,4EAA4E;AAC5E,iEAAiE;AACjE,MAAM,CAAC,MAAM,eAAe,GAAG;;WAEpB,YAAY;WACZ,YAAY;EACrB,YAAY,yBAAyB,YAAY;CAClD,CAAC"}
1
+ {"version":3,"file":"privileged.js","sourceRoot":"","sources":["../../src/lib/privileged.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glassmkr/crucible",
3
- "version": "0.13.16",
3
+ "version": "0.13.17",
4
4
  "description": "Lightweight bare metal server monitoring. IPMI, SMART, OS, network. Opinionated alerts.",
5
5
  "type": "module",
6
6
  "engines": {