tests/checks2.test.js
Rama main · réplica de lectura del repositorio original.
// SPDX-License-Identifier: GPL-3.0-or-later
import { test } from "node:test";
import assert from "node:assert/strict";
import { detectDistro } from "../src/distro.js";
import { loadThresholds } from "../src/thresholds.js";
import { ssh } from "../src/checks/ssh.js";
import { snap } from "../src/checks/snap.js";
import { zram } from "../src/checks/zram.js";
import { locales } from "../src/checks/locales.js";
import { autologin } from "../src/checks/autologin.js";
import { fstrim } from "../src/checks/fstrim.js";
function stubCtx(map, osRelease = { id: "bazzite", id_like: "fedora" }) {
return {
dist: detectDistro(osRelease),
thresholds: loadThresholds({}),
run: async (cmd) => {
const entry = map[cmd];
if (entry === undefined) return { ok: false, code: 1, stdout: "", stderr: "" };
return { ok: true, code: 0, stdout: entry, stderr: "" };
},
};
}
const SSH_CFG = "grep -rhE '^\\s*(PermitRootLogin|PasswordAuthentication)\\s+' /etc/ssh/sshd_config /etc/ssh/sshd_config.d/ 2>/dev/null";
test("ssh: silent when no SSH server is installed", async () => {
const ctx = stubCtx({});
assert.deepEqual(await ssh.run(ctx), []);
});
test("ssh: root login with password is high severity", async () => {
const ctx = stubCtx({
"systemctl is-active sshd 2>/dev/null": "active",
[SSH_CFG]: "PermitRootLogin yes\nPasswordAuthentication yes\n",
});
const findings = await ssh.run(ctx);
assert.equal(findings.length, 1);
assert.equal(findings[0].severity, "high");
assert.match(findings[0].code, /ssh\/root-password/);
});
test("ssh: root login key-only still allows password mode is medium", async () => {
const ctx = stubCtx({
"systemctl is-active sshd 2>/dev/null": "active",
[SSH_CFG]: "PermitRootLogin yes\nPasswordAuthentication no\n",
});
const findings = await ssh.run(ctx);
assert.equal(findings.length, 1);
assert.equal(findings[0].severity, "medium");
assert.match(findings[0].code, /ssh\/root-login/);
});
test("ssh: last drop-in wins (prohibit-password overrides earlier yes)", async () => {
const ctx = stubCtx({
"systemctl is-active sshd 2>/dev/null": "active",
[SSH_CFG]: "PermitRootLogin yes\nPermitRootLogin prohibit-password\n",
});
const findings = await ssh.run(ctx);
assert.equal(findings.length, 1);
assert.equal(findings[0].severity, "info");
assert.match(findings[0].code, /ssh\/ok/);
});
test("ssh: sane config (or defaults) is informational", async () => {
const ctx = stubCtx({ "systemctl is-active sshd 2>/dev/null": "active" });
const findings = await ssh.run(ctx);
assert.equal(findings.length, 1);
assert.equal(findings[0].severity, "info");
assert.match(findings[0].code, /ssh\/ok/);
});
test("snap: silent when snap is not installed", async () => {
const ctx = stubCtx({});
assert.deepEqual(await snap.run(ctx), []);
});
test("snap: pending refreshes with an active timer are informational", async () => {
const ctx = stubCtx({
"command -v snap 2>/dev/null": "/usr/bin/snap",
"snap refresh --list 2>/dev/null": "Name Version Rev Publisher Notes\ncore20 2/2026 2285 canonical** -\nfirefox 140/2026 3797 mozilla -\n",
"systemctl list-timers snapd.* --no-pager 2>/dev/null": "NEXT LEFT LAST PASSED UNIT\nMon 2026-08-24 00:00:00 UTC 4 days N/A N/A snapd.refresh.timer",
});
const findings = await snap.run(ctx);
assert.equal(findings.length, 1);
assert.equal(findings[0].severity, "info");
assert.match(findings[0].code, /snap\/pending/);
});
test("snap: a large backlog is medium severity", async () => {
const ctx = stubCtx({
"command -v snap 2>/dev/null": "/usr/bin/snap",
"snap refresh --list 2>/dev/null": "Name Version Rev Publisher\ncore20 1\ncore22 1\nfirefox 1\nspotify 1\nvlc 1\ngimp 1\n",
"systemctl list-timers snapd.* --no-pager 2>/dev/null": "NEXT LAST UNIT\nMon 2026-08-24 00:00:00 UTC N/A snapd.refresh.timer",
});
const findings = await snap.run(ctx);
assert.equal(findings.length, 1);
assert.equal(findings[0].severity, "medium");
});
test("snap: disabled refresh timer is flagged even with zero pending", async () => {
const ctx = stubCtx({
"command -v snap 2>/dev/null": "/usr/bin/snap",
"snap refresh --list 2>/dev/null": "",
"snap list 2>/dev/null": "Name Version Rev Tracking\nfirefox 140 3797 latest/stable",
});
const findings = await snap.run(ctx);
assert.equal(findings.length, 1);
assert.equal(findings[0].severity, "medium");
assert.match(findings[0].code, /snap\/no-timer/);
});
test("zram: healthy zram is informational", async () => {
const ctx = stubCtx({
"cat /proc/sys/vm/swappiness": "60",
"swapon --show --bytes": "NAME TYPE SIZE USED PRIO\n/dev/zram0 partition 8388608000 1048576000 100",
});
const findings = await zram.run(ctx);
assert.equal(findings.length, 1);
assert.equal(findings[0].severity, "info");
assert.match(findings[0].code, /zram\/ok/);
});
test("zram: compressed swap nearly full is medium", async () => {
const ctx = stubCtx({
"cat /proc/sys/vm/swappiness": "60",
"swapon --show --bytes": "NAME TYPE SIZE USED PRIO\n/dev/zram0 partition 8388608000 7717519360 100",
});
const findings = await zram.run(ctx);
assert.equal(findings.length, 1);
assert.equal(findings[0].severity, "medium");
assert.match(findings[0].code, /zram\/full/);
});
test("zram: no zram but very high swappiness is informational", async () => {
const ctx = stubCtx({
"cat /proc/sys/vm/swappiness": "100",
});
const findings = await zram.run(ctx);
assert.equal(findings.length, 1);
assert.equal(findings[0].severity, "info");
assert.match(findings[0].code, /zram\/swappiness/);
});
test("zram: no zram and sane swappiness is silent", async () => {
const ctx = stubCtx({ "cat /proc/sys/vm/swappiness": "60" });
assert.deepEqual(await zram.run(ctx), []);
});
test("locales: broken locale is medium", async () => {
const ctx = stubCtx({
"env -u LC_ALL locale 2>&1": "locale: Cannot set LC_CTYPE to default locale: No such file or directory\nLANG=en_US.UTF-8\nLC_ALL=\n",
});
const findings = await locales.run(ctx);
assert.equal(findings.length, 1);
assert.equal(findings[0].severity, "medium");
assert.match(findings[0].code, /locales\/broken/);
});
test("locales: healthy locale is silent", async () => {
const ctx = stubCtx({
"env -u LC_ALL locale 2>&1": "LANG=en_US.UTF-8\nLC_CTYPE=\"en_US.UTF-8\"\nLC_ALL=\n",
});
assert.deepEqual(await locales.run(ctx), []);
});
// The check greps widely and decides in JS, so the raw matches include things
// that are not autologin: commented examples that ship in the default GDM
// config, an explicit `false`, and a bare `[Autologin]` section header. All
// three used to be reported as "Automatic login is enabled".
const AUTOLOGIN_GREP =
"grep -rEn 'AutomaticLoginEnable|AutologinUser|autologin-user|\\[Autologin\\]|^[[:space:]]*User[[:space:]]*=' /etc/gdm /etc/gdm3 /etc/sddm.conf /etc/sddm.conf.d/ /etc/lightdm/ /etc/lxdm/ 2>/dev/null";
// Debian/Ubuntu/Mint keep the GDM config in /etc/gdm3 (Red Hat uses /etc/gdm),
// so a check that only listed /etc/gdm never read an enabled autologin there.
test("autologin: the Debian GDM path (/etc/gdm3) is read", async () => {
const ctx = stubCtx({
[AUTOLOGIN_GREP]:
"/etc/gdm3/daemon.conf:3:AutomaticLoginEnable=true\n/etc/gdm3/daemon.conf:4:AutomaticLogin=jane\n",
});
const findings = await autologin.run(ctx);
assert.equal(findings.length, 1);
assert.equal(findings[0].severity, "medium");
});
// The file Debian ships is full of commented examples, including
// "# AutomaticLoginEnable = true". Adding the path is only safe together with
// the comment filter above.
test("autologin: the commented example in Debian's daemon.conf is not enabled", async () => {
const ctx = stubCtx({
[AUTOLOGIN_GREP]: "/etc/gdm3/daemon.conf:10:# AutomaticLoginEnable = true\n/etc/gdm3/daemon.conf:11:# AutomaticLogin = user1\n",
});
assert.deepEqual(await autologin.run(ctx), []);
});
test("autologin: enabled automatic login is medium", async () => {
const ctx = stubCtx({
[AUTOLOGIN_GREP]: "/etc/sddm.conf.d/autologin.conf:3:AutologinUser=jane\n",
});
const findings = await autologin.run(ctx);
assert.equal(findings.length, 1);
assert.equal(findings[0].severity, "medium");
assert.match(findings[0].code, /security\/autologin/);
});
test("autologin: a commented example is not enabled", async () => {
const ctx = stubCtx({
[AUTOLOGIN_GREP]: "/etc/gdm/custom.conf:12:# AutomaticLoginEnable=true\n",
});
assert.deepEqual(await autologin.run(ctx), []);
});
test("autologin: an explicit false is not enabled", async () => {
const ctx = stubCtx({
[AUTOLOGIN_GREP]: "/etc/gdm/custom.conf:12:AutomaticLoginEnable=false\n",
});
assert.deepEqual(await autologin.run(ctx), []);
});
test("autologin: a bare [Autologin] section header sets nothing", async () => {
const ctx = stubCtx({
[AUTOLOGIN_GREP]: "/etc/sddm.conf.d/autologin.conf:1:[Autologin]\n",
});
assert.deepEqual(await autologin.run(ctx), []);
});
test("autologin: SDDM User= inside an [Autologin] section is medium", async () => {
const ctx = stubCtx({
[AUTOLOGIN_GREP]:
"/etc/sddm.conf.d/autologin.conf:1:[Autologin]\n/etc/sddm.conf.d/autologin.conf:2:User=jane\n",
});
const findings = await autologin.run(ctx);
assert.equal(findings.length, 1);
assert.equal(findings[0].severity, "medium");
assert.match(findings[0].evidence, /User=jane/);
});
test("autologin: no autologin config is silent", async () => {
const ctx = stubCtx({});
assert.deepEqual(await autologin.run(ctx), []);
});
test("fstrim: weekly timer enabled is informational", async () => {
const ctx = stubCtx({
"lsblk -dno NAME,ROTA 2>/dev/null": "sda 1\nnvme0n1 0\n",
"systemctl is-enabled fstrim.timer 2>/dev/null": "enabled\n",
"systemctl show fstrim.timer -p LastTriggerUSec --value 2>/dev/null": "Mon 2026-08-17 00:12:44 EEST\n",
});
const findings = await fstrim.run(ctx);
assert.equal(findings.length, 1);
assert.equal(findings[0].severity, "info");
assert.match(findings[0].code, /fstrim\/ok$/);
});
test("fstrim: continuous discard mount covers without a timer", async () => {
const ctx = stubCtx({
"lsblk -dno NAME,ROTA 2>/dev/null": "nvme0n1 0\n",
"systemctl is-enabled fstrim.timer 2>/dev/null": "disabled\n",
"findmnt -no OPTIONS -t ext4,xfs,btrfs,f2fs 2>/dev/null": "rw,relatime,discard=async,ssd\n",
});
const findings = await fstrim.run(ctx);
assert.equal(findings.length, 1);
assert.equal(findings[0].severity, "info");
assert.match(findings[0].code, /discard/);
});
test("fstrim: HDD-only system stays silent", async () => {
const ctx = stubCtx({
"lsblk -dno NAME,ROTA 2>/dev/null": "sda 1\nsdb 1\n",
});
assert.deepEqual(await fstrim.run(ctx), []);
});
test("fstrim: SSD with no TRIM mechanism is medium with an enable fix", async () => {
const ctx = stubCtx({
"lsblk -dno NAME,ROTA 2>/dev/null": "nvme0n1 0\nsda 1\n",
"systemctl is-enabled fstrim.timer 2>/dev/null": "disabled\n",
});
const findings = await fstrim.run(ctx);
assert.equal(findings.length, 1);
assert.equal(findings[0].severity, "medium");
assert.match(findings[0].fix, /systemctl enable --now fstrim\.timer/);
});
test("fstrim: zram/loop ROTA=0 devices are not treated as SSDs", async () => {
const ctx = stubCtx({ "lsblk -dno NAME,ROTA 2>/dev/null": "zram0 0\nloop0 0\n" });
const findings = await fstrim.run(ctx);
assert.equal(findings.length, 0, "no trimmable storage means TRIM is not applicable");
});
test("zram: substantial compressed swap in use is described honestly, not called healthy", async () => {
// 4.6 GiB of 7.7 GiB is 60%: normal over a long uptime, so the severity must
// not be inflated (that would be grading by distance from normal). But
// "healthy" hides that the kernel is compressing a lot of memory and the
// machine can feel slow because of it, and says nothing about what to do.
const ctx = stubCtx({
"cat /proc/sys/vm/swappiness": "60",
"swapon --show --bytes": "NAME TYPE SIZE USED PRIO\n/dev/zram0 partition 8271224832 4939212390 100",
});
const findings = await zram.run(ctx);
assert.equal(findings.length, 1);
assert.equal(findings[0].severity, "info", "60% is not a risk level; do not inflate it");
assert.ok(!/healthy/i.test(findings[0].title), `do not call 60% occupancy healthy: ${findings[0].title}`);
assert.match(findings[0].detail, /processes|cache|reclaimable|compressing/i, "point at what is actionable");
});