Skip to content

security: critical race, spine.conf policy, signal handler, fd leaks#538

Closed
somethingwithproof wants to merge 195 commits intoCacti:developfrom
somethingwithproof:fix/critical-security
Closed

security: critical race, spine.conf policy, signal handler, fd leaks#538
somethingwithproof wants to merge 195 commits intoCacti:developfrom
somethingwithproof:fix/critical-security

Conversation

@somethingwithproof
Copy link
Copy Markdown
Contributor

Six fixes from the recent security review (see internal report, 2026-04-17). Each is one commit with a matching unit test.

Scope

  • C1 (poller.c): guard kill(pid, SIGKILL) against nft_pchild returning -1. Without the guard, kill(-1, SIGKILL) wipes every process owned by the spine uid when the pid lookup fails (reachable from the select-timeout branch racing cleanup).
  • H1 (util.c): tighten spine.conf validation to match SECURITY.md. Refuse start on world-readable, group-readable, or owner-mismatch. Open config with O_NOFOLLOW|O_CLOEXEC. Previous behavior warned and continued.
  • H2 (error.c): rewrite fatal signal handler to async-signal-safe primitives. Pre-formatted messages + write(STDERR_FILENO, ...) + _exit(128+signo). Drops SIGPIPE from the fatal list and ignores it process-wide.
  • H3 (sockets, MySQL, audit, log): set FD_CLOEXEC so poll-script children do not inherit the raw ICMP socket, MySQL session, audit netlink, or log fd.
  • H8 (ping.c): fix 1-byte OOB write in get_namebyhost when the hostname token equals sizeof(name->hostname). Uses existing strncopy helper.
  • H9 (snmp.c): carry SNMPv3 contextEngineID as binary with explicit length. Previous strlen(engine_id) path truncated at the first 0x00, weakening USM key localization.

Verification

cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug && cmake --build build && ctest --test-dir build --output-on-failure — 24/24 pass. New tests: test_poller_pid_guard, test_config_perms, test_signal_handler, test_cloexec, test_get_namebyhost, test_snmp_engine_id.

Risk

  • H2 removes SIGPIPE as a fatal signal. Callers that rely on EPIPE to propagate via handler must check write return codes. Grep confirms no such caller exists in-tree.
  • H1 makes previously tolerated 0640/0644 spine.conf fatal. Operators on shared-group deployments must chmod 0600.

- Add CMakeLists.txt mirroring configure.ac feature checks
- Add config/config.h.cmake.in template for cmake builds
- Add build-cmake-linux CI job (gcc/clang matrix)
- Add build-windows CI job (MSYS2/MinGW-w64, continue-on-error)
- Windows crash dump collection via WER LocalDumps
- Gate -Wall by compiler ID (GNU/Clang vs MSVC)
- Use CMAKE_DL_LIBS instead of hardcoded -ldl
- Parse net-snmp-config --libs into proper NETSNMP_LIBRARIES
- Add SNMP_LOCALNAME compile check for feature parity
- No C source files modified

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
spine.h depends on types from common.h (MYSQL, pid_t, size_t,
pthread types, RESULTS_BUFFER from config.h). Add a compile-time
guard that produces a clear error if spine.h is included without
common.h, rather than cascading type errors.

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
strncasecmp() returns 0 on match, but the comparisons used the
raw return value as truthy, inverting the logic. TCP matched
non-TCP strings and vice versa.

Also fix: comparisons against 'hostname' instead of 'token'
(lines 1020, 1032), and strncasecmp length 3 for 4-char strings
"TCP6"/"UDP6" (should be 4).

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
getarg(opt, &argv) advances the argv pointer on each call. Three
successive calls in the --mode handler consumed three argv entries
instead of one, corrupting subsequent argument parsing when using
the space-separated form (--mode online).

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
… main() entry

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
…t vars

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
…ncation

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
platform_sandbox_linux.c uses uint64_t in add_path_rule() and the
apply_landlock() ro_roots loop but the HAVE_LANDLOCK block only included
linux/landlock.h and sys/syscall.h. On gcc without a transitively-included
stdint.h the type is unknown, which cascades into a parse failure on the
add_path_rule signature and then an implicit-declaration error at the first
call site (lines 110, 221, 247 in the CI error report).

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
glibc's <features.h> freezes the feature-test bitmap on first inclusion.
platform.h pulls <time.h> and platform_process.h pulls <sys/types.h>,
both of which reach <features.h> before CMake's -D_GNU_SOURCE=1 flag
can take effect in the preprocessor pipeline on clang/Linux. Without
the macro visible at that point, pthread_setname_np and pipe2 remain
undeclared, producing -Werror=implicit-function-declaration failures.

Add a `#if defined(__linux__) && !defined(_GNU_SOURCE)` guard at the
top of each affected TU so the macro is always set before any system
header is included, regardless of include order or compiler.

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Replace the bash case pattern *[^A-Za-z0-9._+-\ \t]*) with a
tr-based check. The old pattern treated +-\ as a character range
(0x2B-0x5C), which excluded space and tab and caused all multi-package
strings like 'cmake make pkg-config gcc' to be rejected.

The new approach pipes the input through LC_ALL=C tr -d with the
explicit allowed set (alnum, . _ + - space tab); any bytes surviving
the deletion are disallowed characters, reported verbatim in the error
message.

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
…rns -1

nft_pchild returns -1 on lookup failure. kill(-1, SIGKILL) signals every
process owned by the spine uid; kill(0, ...) hits the whole process group.
Accept only pids > 1. Type the local to spine_pid_t so Windows stays
aligned with the POSIX pid_t width.

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
SECURITY.md promises spine refuses to start when spine.conf has any
group or world bit set or is owned by someone other than root or the
startup euid. The old soft-warn path leaked credentials on a
misconfigured deploy. Open the file with O_NOFOLLOW so a planted
symlink at /etc/spine.conf cannot redirect credential loading, add
O_CLOEXEC so the fd does not leak into poll-script children, and
treat ELOOP, mode drift, and owner mismatch as fatal.

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
The previous handler called stdio, time(3), localtime(3), strftime(3),
and exit(3). None are async-signal-safe, so a SIGSEGV mid-allocation
could deadlock inside libc. Pre-format per-signal messages at startup,
emit with write(2), and use _exit(128 + signo) on SIGSEGV/SIGBUS/SIGFPE/
SIGABRT to skip atexit handlers. Drop SIGPIPE from the fatal set and
ignore it process-wide so a poll script closing stdout early no longer
kills spine. Remove the redundant legacy signal() loop.

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
…e into children

Every nft_popen'd poll script used to inherit the full descriptor table,
including privileged ICMP sockets, the authenticated MySQL connection,
the audit netlink fd, and the log file. A poll script that shells out
could reuse any of them. Set cloexec at creation time: SOCK_CLOEXEC
atomically on Linux/BSD with fcntl fallback for macOS, fcntl after each
raw ICMP socket(), the MySQL net.fd after connect, the audit fd after
audit_open(), and O_CLOEXEC on the log open().

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Two bugs: strncpy(name->hostname, hostname, sizeof(name->hostname))
leaves the buffer unterminated when the source equals or exceeds the
bound, and strncpy(...) followed by name->hostname[strlen(token)] = 0
writes one-past-the-buffer for tokens >= sizeof. Funnel both branches
through strncopy(), which caps at the bound and always NUL-terminates.

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
SNMPv3 engine IDs are opaque octet strings (RFC 3411). strlen() on the
engine-ID buffer truncates at the first 0x00, which any RFC-compliant
engine ID is free to contain. Decode the Cacti hex-string form at DB
load time, carry bytes + length in target_t and host_t, and pass both
to snmp_host_init(). The hex path stays as a transitional fallback.

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Copilot AI review requested due to automatic review settings April 17, 2026 19:21
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR addresses multiple security-review findings (process-kill guard, config hardening, async-signal-safe fatal handling, FD_CLOEXEC, ping hostname bounds, and SNMPv3 engine ID handling) and adds/updates a significant amount of supporting infrastructure (new platform abstraction layer, systemd integration, audit hooks, packaging/CI/test harness changes).

Changes:

  • Hardens security-sensitive runtime behavior (signal handling, descriptor inheritance, SNMPv3 engine ID handling).
  • Introduces new operational/security integration surfaces (systemd sd_notify helpers, audit logging + rate limiting, sandbox stubs, USDT probes).
  • Expands/updates CI, packaging, and test scaffolding (new workflows, distro matrix scripts, CMake-centric packaging/build).

Reviewed changes

Copilot reviewed 140 out of 179 changed files in this pull request and generated 15 comments.

Show a summary per file
File Description
tests/unit/test_cloexec.c Adds unit tests asserting sockets/log fd carry FD_CLOEXEC.
tests/unit/test_circuit_breaker.c Adds unit tests for new circuit breaker state machine and concurrency smoke coverage.
tests/unit/test_check_mode.c Adds unit coverage for spine_health_check() failure JSON envelope.
tests/unit/test_build_fixes.c Adjusts test to avoid null deref in assertions; removes legacy field.
tests/unit/test_arc4random.c Adds BSD-only arc4random smoke test.
tests/unit/json_escape_tu.c Adds test-only TU copy of JSON escaping helper.
tests/unit/build_child_env_tu.c Adds test-only TU copy of child env scrubber helper.
tests/unit/Makefile Reworks unit test makefile into lightweight platform smoke test harness.
tests/snmpv3/snmpd/snmpv3.conf Updates SNMPv3 auth protocol in test fixture to SHA.
tests/snmpv3/scripts/run_test.sh Refactors shell helper functions and error handling blocks.
tests/snmpv3/docker-compose.yml Updates healthcheck SNMP auth protocol to SHA.
tests/snmpv3/db/init.sql Updates seeded Cacti DB fixture auth protocol to SHA.
tests/integration/test_output_regex.sh Improves integration script robustness; uses --no-deps.
tests/integration/test_ipv6_transport.sh Adds new IPv6 integration test script.
tests/integration/test_db_column_detect.sh Improves integration script robustness; uses --no-deps.
tests/integration/smoke_test.sh Updates integration harness; adjusts compose runs and error handling.
src/util.h Adds startup EUID capture API; adds CLI helper declarations; removes add_slashes decl.
src/systemd_notify.h Adds systemd sd_notify integration header API.
src/systemd_notify.c Adds systemd sd_notify integration implementation (guarded by HAVE_LIBSYSTEMD).
src/sql.h Fixes macro trailing continuation (brace/backslash).
src/spine_probes.h Adds USDT probe macros with portable no-op fallbacks.
src/spine_audit.h Adds audit logging API + test seams (clock/reset).
src/spine_audit.c Adds audit event emission with token-bucket rate limiting and CLOEXEC hardening.
src/spine.h Adds include-order enforcement; adds log-format/config fields; expands SNMP protocol buffer sizes; adds binary engine-id fields; uses spine_pid_t.
src/snmp_engine_id.c Adds standalone hex engine-id decoder TU for unit testing.
src/snmp.h Extends snmp_host_init signature to accept binary engine ID + length; declares decoder.
src/snmp.c Uses platform setenv; supports binary engine ID; adds USDT probe site; tweaks credential zeroing behavior.
src/platform/platform_win.c Adds Windows platform backend helpers (sleep, env, thread naming, job object).
src/platform/platform_socket_win.c Adds Windows socket wrapper implementation.
src/platform/platform_socket_posix.c Adds POSIX socket wrapper with atomic CLOEXEC where supported.
src/platform/platform_socket.h Adds platform socket abstraction header.
src/platform/platform_sandbox_win.c Adds Windows sandbox stub TU.
src/platform/platform_sandbox_posix.c Adds generic POSIX sandbox stub TU.
src/platform/platform_sandbox_openbsd.c Adds OpenBSD pledge/unveil implementation.
src/platform/platform_sandbox_freebsd.c Adds FreeBSD sandbox stub with Capsicum rationale.
src/platform/platform_sandbox.h Adds sandbox abstraction header.
src/platform/platform_process_posix.c Adds POSIX process helpers (pipe2 CLOEXEC, spawn retry, wait/terminate).
src/platform/platform_process.h Adds process abstraction header (spine_pid_t, spawn/wait/terminate APIs).
src/platform/platform_posix.c Adds POSIX platform backend helpers (sleep via nanosleep, thread naming, isatty).
src/platform/platform_icmp_posix.c Adds POSIX ICMP façade forwarding into ping.c numeric helpers.
src/platform/platform_icmp.h Adds platform ICMP abstraction header.
src/platform/platform_fd_win.c Adds Windows fd read/write/wait wrappers.
src/platform/platform_fd_posix.c Adds POSIX fd read/write/wait wrappers.
src/platform/platform_fd.h Adds fd abstraction header.
src/platform/platform_error_win.c Adds Windows error-string helper.
src/platform/platform_error_posix.c Adds POSIX error-string helper with glibc/non-glibc strerror_r handling.
src/platform/platform_error.h Adds platform error abstraction header.
src/platform/platform_common.c Adds reference-counted platform init/cleanup shim.
src/platform/platform.h Adds platform abstraction header (init, env, sleep, tty, thread naming, Win job hooks).
src/ping_wire.h Centralizes ICMP wire payload struct + validation API/constant.
src/ping_validate.c Adds standalone ICMP payload validation TU.
src/ping_ipv6_scope.c Adds standalone IPv6 scope_id resolver TU.
src/ping.h Removes large Cygwin ICMP struct block; adds ping_init decl.
src/nft_popen.h Documents and exports spine_build_child_env API.
src/error.h Adds spine_signal_handler_init decl.
src/error.c Replaces prior signal handling with async-signal-safe handler + SIGPIPE ignore.
src/common.h Removes Cygwin-specific hacks; includes platform/platform.h.
src/circuit_breaker.h Adds circuit breaker public API header.
src/circuit_breaker.c Adds per-host circuit breaker implementation with audit emission.
scripts/verify.sh Switches scan-build path to CMake/Ninja build output.
scripts/test-workflows.sh Adds local GitHub Actions workflow testing helper script (act).
scripts/test-vagrant.sh Adds Vagrant-based BSD/niche OS test runner helper script.
scripts/test-distros.sh Adds Docker-based distro build+smoke test matrix script.
packaging/rpm/spine.spec Migrates RPM spec build to CMake + Ninja.
packaging/rpm/README Notes RPM spec uses CMake + Ninja.
packaging/debian/rules Migrates Debian rules to CMake + Ninja buildsystem.
packaging/debian/control Replaces autotools deps with cmake/ninja-build.
packaging/debian/README Notes Debian packaging uses CMake + Ninja.
packaging/README.md Updates packaging docs to reference CMake-based builds.
package Updates release packaging script to validate via CMake config; removes bootstrap dependency.
flake.nix Adds Nix flake build + devshell for CMake build path.
etc/systemd/spine.timer Adds systemd timer unit for periodic runs.
etc/systemd/spine.service Adds hardened systemd service unit using Type=notify and sandboxing knobs.
etc/spine.conf.dist Documents Script_Policy option in distributed config.
etc/selinux/spine.te Adds SELinux skeleton module for spine domain/types.
etc/selinux/spine.if Adds SELinux interfaces for domain transition/log/pid access.
etc/selinux/spine.fc Adds SELinux file contexts for spine paths.
etc/apparmor.d/usr.local.spine.bin.spine Adds AppArmor profile for spine.
error.c Removes legacy error.c implementation (file deleted).
docs/security-selinux.md Adds SELinux enablement documentation.
docs/security-hardening.md Adds runtime hardening documentation and guidance.
docs/security-alerts.md Adds audit trail of dismissed security alerts.
docs/platform-idioms.md Adds platform implementation idioms doc.
docs/debugging.md Adds debugging guide, including systemd/gdb and USDT tracing notes.
debug Adjusts debug helper script formatting.
config/config.h.cmake.in Adds CMake-generated config.h template.
bootstrap Removes legacy autotools bootstrap script (file deleted).
Vagrantfile Adds Vagrant boxes + provisioning for BSD/Alpine testing.
VERSION Adds VERSION file (1.3.0).
SECURITY.md Adds repo security policy and deployment guidance.
Makefile.am Removes autotools build file (file deleted).
Dockerfile.dev Migrates dev Docker build to CMake/Ninja and sanitizer flags.
Dockerfile Migrates runtime Docker build to CMake/Ninja; adds non-root user and config install.
CONTRIBUTING.md Adds contributing guide including local CI testing and distro matrix instructions.
CMakePresets.json Adds CI-oriented CMake presets.
CHANGELOG Adds notes/issues/features entries related to platform/CI/security work.
.github/workflows/weekly.yml Adds scheduled weekly deep checks workflow.
.github/workflows/security-posture.yml Adds security posture workflow (TruffleHog, Semgrep, Scorecard, policy gate).
.github/workflows/release.yml Adds release workflow with SBOM generation and cosign signing.
.github/workflows/release-verification.yml Adds release verification (hardening checks, staging install test, attestation).
.github/workflows/oci.yml Adds multi-arch OCI publish workflow with cosign signing.
.github/workflows/fuzzing.yml Adds sanitizer-based CLI fuzz smoke workflow.
.github/workflows/coverage.yml Adds GCC coverage workflow with lcov gate.
.github/workflows/codeql.yml Adds CodeQL workflow for C/C++.
.github/scripts/cppcheck_to_sarif.py Adds cppcheck output to SARIF converter.
.github/scripts/clang_tidy_to_sarif.py Adds clang-tidy output to SARIF converter.
.github/scripts/check-workflow-policy.py Adds workflow hygiene policy checker.
.github/scripts/check-unsafe-api-additions.sh Adds guardrail disallowing newly-added unsafe C APIs in diffs.
.github/scripts/check-leak-trend.py Adds nightly leak trend parser/gate for ASan/Valgrind logs.
.github/perf-baseline.json Adds performance baseline configuration.
.github/nightly-leak-baseline.json Adds nightly leak baseline thresholds.
.github/instructions/instructions.md Updates contributor instructions with API and CI guardrails.
.github/cppcheck-baseline.txt Adds placeholder cppcheck baseline file.
.github/actions/install-apt-deps/action.yml Adds composite action for safe apt dependency installs.
.dockerignore Expands docker ignore patterns (excludes CI/test/build artifacts).
.devcontainer/devcontainer.json Adds devcontainer for consistent Ubuntu 24.04 C++ tooling.
.clang-tidy Adds clang-tidy configuration.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/spine_audit.c
* -1 means "not yet attempted"; -2 means "attempted and failed, stop
* retrying" so a non-audit-enabled kernel doesn't burn syscalls forever. */
#ifdef HAVE_LIBAUDIT
static int g_audit_fd = -1;
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spine_audit_event()/rate_allow() mutate shared globals (g_buckets and g_audit_fd) without synchronization. In a multi-threaded poller this is a data race: counters can corrupt, rate limiting becomes unreliable, and g_audit_fd initialization can race (double audit_open(), fd leak, and inconsistent -2 state). Fix by adding a mutex (or atomics) that protects both the token-bucket state and the lazy audit_open/FD_CLOEXEC setup, or by using pthread_once for fd init plus per-bucket atomics for counters.

Copilot uses AI. Check for mistakes.
Comment thread src/spine_audit.c
Comment on lines +42 to +45
static struct rate_bucket g_buckets[AUDIT_BUCKET__COUNT];

static time_t default_clock(void) { return time(NULL); }
static spine_audit_clock_fn g_clock = default_clock;
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spine_audit_event()/rate_allow() mutate shared globals (g_buckets and g_audit_fd) without synchronization. In a multi-threaded poller this is a data race: counters can corrupt, rate limiting becomes unreliable, and g_audit_fd initialization can race (double audit_open(), fd leak, and inconsistent -2 state). Fix by adding a mutex (or atomics) that protects both the token-bucket state and the lazy audit_open/FD_CLOEXEC setup, or by using pthread_once for fd init plus per-bucket atomics for counters.

Copilot uses AI. Check for mistakes.
Comment thread src/spine_audit.c
Comment on lines +66 to +87
static int rate_allow(enum audit_bucket b, const char *op) {
time_t now = g_clock();
struct rate_bucket *rb = &g_buckets[b];

if (rb->window_start == 0 || now - rb->window_start >= SPINE_AUDIT_RATE_WINDOW_SECS) {
rb->window_start = now;
rb->count = 0;
rb->notified = 0;
}

if (rb->count < SPINE_AUDIT_RATE_MAX) {
rb->count++;
return 1;
}

if (!rb->notified) {
fprintf(stderr, "NOTE: audit rate limit reached for op=%s\n",
op ? op : "(null)");
rb->notified = 1;
}
return 0;
}
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spine_audit_event()/rate_allow() mutate shared globals (g_buckets and g_audit_fd) without synchronization. In a multi-threaded poller this is a data race: counters can corrupt, rate limiting becomes unreliable, and g_audit_fd initialization can race (double audit_open(), fd leak, and inconsistent -2 state). Fix by adding a mutex (or atomics) that protects both the token-bucket state and the lazy audit_open/FD_CLOEXEC setup, or by using pthread_once for fd init plus per-bucket atomics for counters.

Copilot uses AI. Check for mistakes.
Comment thread src/spine_audit.c
Comment on lines +94 to +99
if (g_audit_fd == -1) {
g_audit_fd = audit_open();
if (g_audit_fd < 0) {
g_audit_fd = -2;
return;
}
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spine_audit_event()/rate_allow() mutate shared globals (g_buckets and g_audit_fd) without synchronization. In a multi-threaded poller this is a data race: counters can corrupt, rate limiting becomes unreliable, and g_audit_fd initialization can race (double audit_open(), fd leak, and inconsistent -2 state). Fix by adding a mutex (or atomics) that protects both the token-bucket state and the lazy audit_open/FD_CLOEXEC setup, or by using pthread_once for fd init plus per-bucket atomics for counters.

Copilot uses AI. Check for mistakes.
Comment thread src/error.c
Comment on lines +115 to +117
static void spine_signal_handler(int spine_signal) {
const char *msg;
size_t len;
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new fatal handler only terminates the process for SIGSEGV/SIGBUS/SIGFPE/SIGABRT. For signals that are listed as fatal (SIGINT/SIGQUIT/SIGSYS), the handler writes a 'FATAL' message but then returns, allowing the process to continue in an interrupted state. If the intended behavior is 'die on fatal signals' (as described in the PR text), this should terminate for all signals handled here (e.g., _exit(128 + signo)), or it should set a volatile sig_atomic_t shutdown flag that the main loop observes to exit cleanly.

Copilot uses AI. Check for mistakes.
Comment thread src/circuit_breaker.c
Comment on lines +8 to +10
#include "common.h"
#include "spine.h"
#include "circuit_breaker.h"
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR description states the scope is six targeted security fixes (C1, H1, H2, H3, H8, H9), but the diff introduces substantial additional features/integration surfaces (circuit breaker implementation, systemd sd_notify, audit logging, sandbox framework, CI/workflow additions, packaging migration, etc.). This mismatch makes it difficult to review and risk-assess. Consider splitting non-security-review changes into separate PRs, or updating the PR description and risk assessment to explicitly include these new components.

Copilot uses AI. Check for mistakes.
Comment thread src/spine_audit.c
Comment on lines +33 to +34
#define SPINE_AUDIT_RATE_MAX 10
#define SPINE_AUDIT_RATE_WINDOW_SECS 60
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This introduces non-trivial behavior (token-bucket rate limiting, op classification, test seams for clock/reset, and lazy audit fd initialization). There are no corresponding unit tests shown here that validate: (1) per-bucket limiting behavior across windows, (2) the 'single NOTE' notification behavior, and (3) the fd initialization path sets FD_CLOEXEC only once. Adding a focused unit test would help prevent regressions in both security posture and operational logging.

Copilot uses AI. Check for mistakes.
Comment thread src/spine_audit.c
static time_t default_clock(void) { return time(NULL); }
static spine_audit_clock_fn g_clock = default_clock;

void spine_audit_set_clock(spine_audit_clock_fn fn) {
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This introduces non-trivial behavior (token-bucket rate limiting, op classification, test seams for clock/reset, and lazy audit fd initialization). There are no corresponding unit tests shown here that validate: (1) per-bucket limiting behavior across windows, (2) the 'single NOTE' notification behavior, and (3) the fd initialization path sets FD_CLOEXEC only once. Adding a focused unit test would help prevent regressions in both security posture and operational logging.

Copilot uses AI. Check for mistakes.
Comment thread src/spine_audit.c
g_clock = fn ? fn : default_clock;
}

void spine_audit_reset_for_test(void) {
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This introduces non-trivial behavior (token-bucket rate limiting, op classification, test seams for clock/reset, and lazy audit fd initialization). There are no corresponding unit tests shown here that validate: (1) per-bucket limiting behavior across windows, (2) the 'single NOTE' notification behavior, and (3) the fd initialization path sets FD_CLOEXEC only once. Adding a focused unit test would help prevent regressions in both security posture and operational logging.

Copilot uses AI. Check for mistakes.
Comment thread src/spine_audit.c
return 0;
}

void spine_audit_event(const char *op, const char *detail, int result) {
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This introduces non-trivial behavior (token-bucket rate limiting, op classification, test seams for clock/reset, and lazy audit fd initialization). There are no corresponding unit tests shown here that validate: (1) per-bucket limiting behavior across windows, (2) the 'single NOTE' notification behavior, and (3) the fd initialization path sets FD_CLOEXEC only once. Adding a focused unit test would help prevent regressions in both security posture and operational logging.

Copilot uses AI. Check for mistakes.
Comment thread src/util.c
chars = fgets(buff, BUFSIZE, fp);

if (chars != NULL && !feof(fp) && *buff != '#' && *buff != ' ' && *buff != '\n') {
sscanf(buff, "%15s %255s", p1, p2);
Comment thread src/util.c
chars = fgets(buff, BUFSIZE, fp);

if (chars != NULL && !feof(fp) && *buff != '#' && *buff != ' ' && *buff != '\n') {
sscanf(buff, "%15s %255s", p1, p2);
Comment thread src/poller.c
char *exec_poll(host_t *current_host, char *command, int id, const char *type) {
int cmd_fd;
int pid;
spine_pid_t pid;
H1 (SECURITY.md) refuses startup when spine.conf has any group or world
bit set or is owned by someone other than root or the startup euid.  The
integration fixtures shipped the file at the COPY/checkout default of
0644, which H1 correctly rejects.

Dockerfile: add RUN chmod 0600 after the COPY so the baked conf (owned
by root, mode 0600) satisfies both H1 checks.  This fixes the Docker
Integration Tests, Poll Timing Benchmark, and build-cmake-linux-
sanitizers jobs that use the production image without a bind-mount
override.

docker-compose.yml: the bind-mount of tests/snmpv3/spine/spine.conf
arrives at 0644 from the git checkout and cannot be chmod'd through a
:ro mount.  Switch to a staging mount at spine.conf.src, set user: "0:0"
so the process euid is root, and add a wrapper entrypoint that installs
the staged conf to /tmp/spine.conf at 0600 before exec-ing spine.

ci.yml / distro-matrix.yml: add chmod 0600 tests/snmpv3/spine/spine.conf
before the cmake build in both FreeBSD jobs so ctest passes on FreeBSD 14
(Tier 1).

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
@somethingwithproof
Copy link
Copy Markdown
Contributor Author

Superseded by consolidated PR #535 per maintainer's precedent in #523 (which explicitly cites merge-order conflict risk across util.c / spine.c / platform / CI files). All commits (C1 kill guard, H1 config perms, H2 signal handler, H3 FD_CLOEXEC, H8 strncopy, H9 engine ID binary length) are now on feat/distro-test-matrix via fast-forward. No content lost.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants