Three new jobs in build.yml:
1. sanitizers (clang + ASan/UBSan)
Runs the same 88-test suite under AddressSanitizer +
UndefinedBehaviorSanitizer. -fno-sanitize-recover=all so any
finding fails CI loudly rather than scrolling past. -O1 + frame-
pointers preserved for usable backtraces. CC=clang because clang's
sanitizer integration is more mature than gcc's; gcc-built binaries
still get exercised by the matrix in the main 'build' job.
2. clang-tidy (advisory)
Lints core/ + skeletonkey.c (the files we control most directly;
module sources often bundle published PoC code we keep close to
upstream style, so they're excluded). continue-on-error: true for
now so it sets a baseline without blocking merges; we can tighten
incrementally as the warning surface shrinks.
3. drift-check (cron + workflow_dispatch)
Runs weekly (Mon 06:00 UTC) and on-demand. Two sub-steps:
- tools/refresh-cve-metadata.py --check (CISA KEV + NVD CWE)
- tools/refresh-kernel-ranges.py (Debian security tracker)
Both already exit non-zero on actionable drift. Network-required,
so NOT gated on regular PR runs — random PRs shouldn't fail because
CISA published a new KEV entry. The job runs ONLY on schedule +
manual trigger (if: github.event_name == 'schedule' || ...).
When it fires, the GH Actions warning annotation points the
maintainer at the right refresh script to rerun + commit.
Smoke-tested locally:
- macOS local ASan+UBSan build: kernel_range tests pass; detect()
tests skipped (non-Linux platform stubs).
- clang-tidy not installed locally; CI installs from apt.
Adds tests/test_detect.c — a standalone harness that constructs
synthetic struct skeletonkey_host fingerprints (vulnerable / patched /
specific-gate-closed) and asserts each migrated module's detect()
returns the expected verdict. First real test coverage for the corpus;
catches regressions in the host-fingerprint-consuming logic.
Initial coverage — 8 deterministic cases across the 4 modules that
already consume ctx->host:
- dirtydecrypt: 3 cases verifying 'kernel < 7.0 -> predates the bug'
short-circuit on synthetic 6.12 / 6.14 / 6.8 hosts.
- fragnesia: unprivileged_userns_allowed=false -> PRECOND_FAIL.
- pack2theroot: is_debian_family=false -> PRECOND_FAIL.
- pack2theroot: has_dbus_system=false -> PRECOND_FAIL.
- overlayfs: distro=debian / distro=fedora -> 'not Ubuntu' -> OK.
Coverage grows automatically as more modules migrate to ctx->host
(task #12 below adds them). Each new module that consults the host
fingerprint can have its precondition gates tested with a one-line
EXPECT_DETECT call against a pre-built fingerprint.
Wiring:
- Makefile: new MODULE_OBJS var consolidates the module .o list so
both the main binary and the test binary can share it without
duplication. New TEST_BIN := skeletonkey-test target. 'make test'
builds and runs the suite.
- .github/workflows/build.yml: install libglib2.0-dev + pkg-config so
pack2theroot builds with GLib in CI (was previously stub-compiling).
New 'tests — detect() unit suite' step runs 'make test' as a
non-root user so modules' 'already root' gates don't short-circuit
before the synthetic host checks fire.
- Test harness compiles cross-platform but assertions are #ifdef
__linux__ guarded (on non-Linux all module detect() bodies stub-out
to PRECOND_FAIL, making assertions tautological); macOS dev build
reports 'skipped'.
Module change:
- pack2theroot p2tr_detect now consults ctx->host->is_root (with a
geteuid() fallback when ctx->host is null) instead of calling
geteuid() directly. Production behaviour is identical
(host->is_root is populated from geteuid() at startup); tests can
now construct non-root fingerprints regardless of the test
process's actual euid. Exposed a real consistency issue worth
fixing.
Verified in docker as non-root: 8/8 pass on Linux. macOS reports
'skipped' as designed.
- .github/workflows/build.yml: matrix of {gcc, clang} x {default,
debug} builds on every push + PR. Smoke tests after build:
--version, --list, --scan, --detect-rules auditd, --detect-rules
sigma. Build failure breaks merge gate.
- Static-build job runs continue-on-error (glibc + NSS issue with
static linking — getpwnam pulls in NSS at runtime; legacy DIRTYFAIL
Makefile noted this. Revisit with musl-gcc to get a truly portable
static binary).
- Kernel-VM matrix placeholder commented at the bottom of build.yml.
Real kernel matrix needs self-hosted runners or a paid VM service —
out of scope for tonight, in scope for Phase 4 followup.