Ctrl K
ComunidadGuías…

tests/checks2.test.js

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
// 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");
});