Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
166 commits
Select commit Hold shift + click to select a range
7c94f7e
chore(build): add cmake build system and windows ci job
somethingwithproof Apr 8, 2026
63491a9
ci(windows): fail PRs on broken windows builds
somethingwithproof Apr 11, 2026
b7ebf54
ci(actions): enable fork branch and manual runs
somethingwithproof Apr 11, 2026
025b6ae
ci(windows): fix broken msys2 action pin
somethingwithproof Apr 11, 2026
718fa56
ci(windows): skip build when net-snmp is unavailable
somethingwithproof Apr 11, 2026
122286d
ci(actions): repin to node24-compatible actions
somethingwithproof Apr 11, 2026
bbfd67a
ci(actions): repin upload-artifact to v6
somethingwithproof Apr 11, 2026
c812844
test(platform): add ctest smoke and windows runtime layer
somethingwithproof Apr 11, 2026
f68d151
refactor(platform): split posix and windows backends
somethingwithproof Apr 11, 2026
cf63246
test(platform): add socket layer and focused ctest matrix
somethingwithproof Apr 11, 2026
cf39d72
refactor(platform): extract process and error helpers
somethingwithproof Apr 11, 2026
e8e008b
feat(platform): implement native process backend coverage
somethingwithproof Apr 11, 2026
6f6a765
docs(ci): clarify platform support matrix
somethingwithproof Apr 11, 2026
8ecc5b0
fix(platform): use native process handles on windows
somethingwithproof Apr 11, 2026
4501504
refactor(platform): abstract pipe io across runtimes
somethingwithproof Apr 11, 2026
007a811
docs(windows): prefer msys2 over cygwin
somethingwithproof Apr 11, 2026
04ee6be
refactor(ping): hide cygwin socket quirks behind platform helpers
somethingwithproof Apr 11, 2026
41ff3bd
feat(ping): allow ipv6 transport for tcp and udp
somethingwithproof Apr 11, 2026
a8b05a5
feat(ping): add icmpv6 echo support
somethingwithproof Apr 11, 2026
86b9531
test(socket): add ipv6 loopback smoke coverage
somethingwithproof Apr 11, 2026
eb0347c
test(socket): add loopback integration coverage
somethingwithproof Apr 11, 2026
2864526
build(cmake): make target graph and ci more idiomatic
somethingwithproof Apr 11, 2026
37cbeaf
build: remove legacy autotools and cygwin paths
somethingwithproof Apr 11, 2026
8ef4414
fix(ping/php): restore tcp/udp path and correct script buffer bound
somethingwithproof Apr 12, 2026
335243a
ci: add comprehensive CI/CD pipeline with security hardening (#462)
somethingwithproof Mar 26, 2026
a20fc37
chore: add include guard to spine.h requiring common.h first
somethingwithproof Apr 8, 2026
91b4aff
fix(ping): correct inverted strncasecmp in get_namebyhost
somethingwithproof Apr 8, 2026
6dce103
fix(cli): save --mode argument before comparing to avoid argv advance
somethingwithproof Apr 8, 2026
0482cb3
fix(cli): check strchr return for NULL before dereference in -O handler
somethingwithproof Apr 8, 2026
bcde312
fix(log): add missing break statements in get_date_format switch
somethingwithproof Apr 8, 2026
1a63885
fix(db): correct inverted return value in putsetting
somethingwithproof Apr 8, 2026
6295d61
fix(poller): add NULL check after db_get_connection
somethingwithproof Apr 8, 2026
1b1f163
ci(workflows): centralize apt COMMON_DEPS install via composite action
somethingwithproof Apr 12, 2026
9861bc2
platform: add freebsd lane, monotonic timing, and cross-platform smok…
somethingwithproof Apr 12, 2026
ed4644a
fix(ci+platform): unblock PR 523 builds and policy gates
somethingwithproof Apr 14, 2026
ea391a8
refactor(platform+build): C17 hardening and linux-idiomatic policy
somethingwithproof Apr 14, 2026
a0cd3eb
feat(ipv6+windows): add unicode spawn tests and portability profiles
somethingwithproof Apr 14, 2026
e3f0c2d
fix(windows-process): use PID-based OpenProcess lifecycle
somethingwithproof Apr 14, 2026
2661639
test(ipv6)+fix(win): add integration lane and strict spawn contracts
somethingwithproof Apr 14, 2026
ae1891e
docs(platform): add per-OS idioms and enforce safer script command po…
somethingwithproof Apr 14, 2026
d5a907a
revert(poller): remove script_command_is_safe allow-filter
somethingwithproof Apr 14, 2026
d7dd455
test+security: add script-command policy coverage and harden docker i…
somethingwithproof Apr 14, 2026
a11909b
ci+util: remove VLA/fallthrough warning patterns and run sanitizer in…
somethingwithproof Apr 14, 2026
a45e7bc
fix(test+snmp): stabilize integration suite and correct host/auth pro…
somethingwithproof Apr 14, 2026
fc4b142
docs(sql): document db_get_connection NULL-on-exhaustion contract
somethingwithproof Apr 14, 2026
f81caa6
docs(poller): justify result_string stack buffer size in poll_host
somethingwithproof Apr 14, 2026
23f53d6
fix(platform-win): use QPC for sub-millisecond spine_platform_sleep_us
somethingwithproof Apr 14, 2026
da2bab8
build(cmake): read project version from VERSION file with git describ…
somethingwithproof Apr 14, 2026
4b04fff
fix(platform-win): yield processor in QPC spin and fail closed on non…
somethingwithproof Apr 14, 2026
6f11572
fix(poller): release DB connections on host malloc failure
somethingwithproof Apr 14, 2026
fbca822
fix(sql): zero input_trimmed and guard trim_limit in db_escape
somethingwithproof Apr 14, 2026
dc68523
build(cmake): prefer pthread flag for find_package(Threads)
somethingwithproof Apr 14, 2026
a4016c1
revert(poller): remove command_policy module entirely per upstream re…
somethingwithproof Apr 14, 2026
cecb443
refactor(build): move platform abstraction files into platform/ subdi…
somethingwithproof Apr 14, 2026
3efe3c3
docs(readme): add Debian and EL install sections
somethingwithproof Apr 14, 2026
7f70686
build(cmake): gate -Wall on GNU/Clang, use CMAKE_DL_LIBS, parse net-s…
somethingwithproof Apr 14, 2026
2cd987e
ci(windows): mark Windows job advisory until port completes
somethingwithproof Apr 14, 2026
11ad1d3
refactor(src): move sources into src/, third_party/, etc/ layout
somethingwithproof Apr 14, 2026
c7e917c
fix(util): harden log open with O_NOFOLLOW and enforce spine.conf perms
somethingwithproof Apr 14, 2026
3456ed0
fix(sql): enforce SSL verify, harden db_escape overflow, add error diag
somethingwithproof Apr 14, 2026
2532a6f
fix(popen): sanitize child env and reset signal dispositions
somethingwithproof Apr 14, 2026
45a000b
fix(platform): set CLOEXEC on pipe fds to prevent leak to concurrent …
somethingwithproof Apr 14, 2026
dec2d98
fix(snmp): enable credential zeroing and safe protocol pointer init
somethingwithproof Apr 14, 2026
9ae88d7
fix(ping): randomize ICMP echo ID and use atomic seq counter
somethingwithproof Apr 14, 2026
f0dc756
build(cmake): add FORTIFY_SOURCE, stack protector, PIE, RELRO hardening
somethingwithproof Apr 14, 2026
1fd0ed2
docs: add SECURITY.md documenting trust model and deployment guidance
somethingwithproof Apr 14, 2026
4bef369
build(cmake): guard -pie and -fstack-clash-protection on AppleClang
somethingwithproof Apr 14, 2026
58eb795
build(cmake): deduplicate net-snmp libs, mark external includes SYSTEM
somethingwithproof Apr 14, 2026
98082ca
fix(popen,build): preserve user env blocking LD_*/DYLD_*/BASH_ENV; my…
somethingwithproof Apr 14, 2026
39285b6
fix(util): relax spine.conf perms check to world-readable/writable only
somethingwithproof Apr 14, 2026
774548c
fix(php): sanitize env for PHP Script Server spawn
somethingwithproof Apr 14, 2026
1bbb58b
fix(ping): declare ICMP seq as _Atomic uint16_t with pre-C11 fallback
somethingwithproof Apr 14, 2026
3a4b491
fix(sql): match MYSQL_OPT_SSL_VERIFY_SERVER_CERT arg to connector type
somethingwithproof Apr 14, 2026
d4b7eed
feat(platform): add platform_icmp abstraction header
somethingwithproof Apr 14, 2026
a9aa092
feat(platform-icmp): Windows dynamic-load + POSIX thin wrapper
somethingwithproof Apr 14, 2026
275eda9
feat(ping): standalone reply validator and IPv6 scope resolver
somethingwithproof Apr 14, 2026
52e78ba
feat(ping): RFC 3542 IPv6 + reply validation + bounds checks + DNS guard
somethingwithproof Apr 14, 2026
32b38ac
test(ping): unit tests for reply validation and IPv6 scope resolver
somethingwithproof Apr 14, 2026
a7f4523
fix(ping,icmp): address pre-push review findings
somethingwithproof Apr 14, 2026
258d8fd
fix(ping): consolidate wire format header + tighten facade validation
somethingwithproof Apr 14, 2026
4ca79f2
feat(systemd): idiomatic Type=notify integration with hardened unit
somethingwithproof Apr 14, 2026
fe660f8
fix(systemd): handle clock_gettime failure in reload path
somethingwithproof Apr 14, 2026
4528ed8
ci: add WITH_SYSTEMD=OFF build lane to verify no-systemd path
somethingwithproof Apr 14, 2026
117f8f8
test(systemd): unit test for sd_notify wrapper idempotency
somethingwithproof Apr 14, 2026
2017234
fix(systemd): tolerate NULL status fmt + document fprintf exception
somethingwithproof Apr 14, 2026
94862bb
docs(systemd): clarify fprintf fallback runs on main thread
somethingwithproof Apr 14, 2026
a24927e
ci: migrate all workflows from autotools to cmake
somethingwithproof Apr 14, 2026
d8fc7cb
test(systemd): add long-status truncation safety test
somethingwithproof Apr 14, 2026
f43b906
fix(platform): define _GNU_SOURCE before headers so pipe2 is visible …
somethingwithproof Apr 14, 2026
b9a4f05
test: add scripts/test-distros.sh for Docker multi-distro verification
somethingwithproof Apr 14, 2026
66fa5e0
ci: add distro matrix workflow covering 10 Linux distros plus macOS, …
somethingwithproof Apr 14, 2026
9d8e157
docs: document supported platforms and build requirements
somethingwithproof Apr 14, 2026
bc3e446
fix(ci): use gcc-13 on openSUSE Leap 15 for C17 support
somethingwithproof Apr 14, 2026
a2c55ba
ci: add UBI 9 advisory lane and document RHEL 9 coverage
somethingwithproof Apr 14, 2026
6daecb7
feat(platform): add AIX and Solaris compile guards
somethingwithproof Apr 14, 2026
5d06bec
feat(platform): extend BSD coverage to NetBSD, OpenBSD, DragonFly
somethingwithproof Apr 14, 2026
62b1a27
ci: add tier classification and advisory lanes for NetBSD/OpenBSD
somethingwithproof Apr 14, 2026
6cc16e3
docs: rewrite platforms.md with tier classification and full OS matrix
somethingwithproof Apr 14, 2026
6ccf270
docs(platforms): order Tier 1 by Cacti deployment footprint
somethingwithproof Apr 15, 2026
2c1607d
docs(platforms): promote FreeBSD 14 to Tier 1
somethingwithproof Apr 15, 2026
d36e89d
feat(platform): add portable spine_platform_set_thread_name wrapper
somethingwithproof Apr 15, 2026
56915ff
feat(sandbox): add platform_sandbox abstraction header with POSIX and…
somethingwithproof Apr 15, 2026
3efaccb
feat(sandbox): OpenBSD pledge + unveil, Linux NO_NEW_PRIVS, FreeBSD stub
somethingwithproof Apr 15, 2026
8b85d23
feat(win): confine spawned children in a Job Object
somethingwithproof Apr 15, 2026
5bb9371
build(cmake): respect SOURCE_DATE_EPOCH for reproducible builds
somethingwithproof Apr 15, 2026
1e99a84
build(cmake): add CPack rules for tgz, deb, and rpm packages
somethingwithproof Apr 15, 2026
ec26083
docs: add remote debugging guide for spine daemon
somethingwithproof Apr 15, 2026
3f7875f
feat(trace): add USDT probes for poll cycle and SNMP operations
somethingwithproof Apr 15, 2026
bf9a83a
feat(cli): add --check, --dump-config, --dry-run, --log-format flags
somethingwithproof Apr 15, 2026
09ca9e3
feat(log): JSON structured logging on non-TTY stderr
somethingwithproof Apr 15, 2026
7a6c063
feat(poller): SIGHUP actually reloads spine.conf
somethingwithproof Apr 15, 2026
55fe9fc
feat(poller): graceful SIGTERM drain of in-flight polls
somethingwithproof Apr 15, 2026
7901813
feat(poller): --dry-run mode skips SQL writes and logs would-be queries
somethingwithproof Apr 15, 2026
5bc69b2
feat(poller): per-host circuit breaker with exponential backoff
somethingwithproof Apr 15, 2026
b924cca
test(platform): thread-name readback and sandbox fork smoke
somethingwithproof Apr 15, 2026
94a63a9
test(log): spine_json_escape coverage with standalone TU
somethingwithproof Apr 15, 2026
0c50491
test(circuit-breaker): state machine and thread safety
somethingwithproof Apr 15, 2026
29fdd5f
test(cli): dump_config, check, and dry_run coverage
somethingwithproof Apr 15, 2026
416436b
test: wire new unit tests with Win and BSD guards
somethingwithproof Apr 15, 2026
07c01d0
docs(readme): rewrite as idiomatic modern C daemon README
somethingwithproof Apr 15, 2026
8ba59a6
ci(supply-chain): add release workflow with SBOM and cosign keyless s…
somethingwithproof Apr 15, 2026
54b2061
feat(oci): non-root runtime user and image labels in production Docke…
somethingwithproof Apr 15, 2026
ae8871c
ci(oci): publish multi-arch image to ghcr.io with cosign signing and …
somethingwithproof Apr 15, 2026
e6b073a
build(nix): add flake with package and dev shell
somethingwithproof Apr 15, 2026
78f3ff0
feat(devcontainer): VS Code dev container with C toolchain and clangd
somethingwithproof Apr 15, 2026
10ae479
ci: add scripts/test-workflows.sh for local act + policy testing
somethingwithproof Apr 15, 2026
6aef246
docs: explain local CI testing in CONTRIBUTING and debugging guides
somethingwithproof Apr 15, 2026
c498efb
test(platform): portable unsetenv and explicit errno.h include
somethingwithproof Apr 15, 2026
6d7df3b
fix(tests): grep -E for alternation in ipv6 integration test
somethingwithproof Apr 15, 2026
41a8af0
fix(scripts): require bash 4+ with clear macOS error message
somethingwithproof Apr 15, 2026
c32e461
build(cmake): detect SNMP_LOCALNAME feature parity with autotools
somethingwithproof Apr 15, 2026
f762337
feat(dev): add Vagrantfile for local BSD and niche-OS testing
somethingwithproof Apr 15, 2026
8b6180f
fix(platform,build): CI compile + link regressions
somethingwithproof Apr 15, 2026
3fd2c14
fix(platform,tests): portable sleep + feature-test macros for strict …
somethingwithproof Apr 15, 2026
1fe5ce2
fix(platform): truncate thread name on Linux and expose BSD visibility
somethingwithproof Apr 15, 2026
a1ac149
fix(build,ci): expose XSI helpers on BSDs and correct CodeQL action SHA
somethingwithproof Apr 15, 2026
f158c5f
fix(build,tests): propagate POSIX/BSD feature-test macros to test tar…
somethingwithproof Apr 15, 2026
f5aae4d
fix(build,ci): sys/types.h before netinet on BSDs; correct more CodeQ…
somethingwithproof Apr 15, 2026
75a3a87
fix(util): warn rather than FATAL on world-readable spine.conf
somethingwithproof Apr 15, 2026
bfd534f
fix(build): drop _POSIX_C_SOURCE on OpenBSD; set __BSD_VISIBLE in san…
somethingwithproof Apr 15, 2026
1586365
fix(ci,build): dash-compat install steps and OpenBSD-specific feature…
somethingwithproof Apr 15, 2026
b9d4c1a
test(integration): accept small host_errors count as fixture noise
somethingwithproof Apr 15, 2026
e475565
ci: IGNORE_OSVERSION on FreeBSD pkg; fail-fast on snmpd startup
somethingwithproof Apr 15, 2026
0317403
ci(static-analysis): create empty codespell ignore file, drop --plist…
somethingwithproof Apr 15, 2026
ef8e75b
fix(snmp,tests): plug localname leak and silence cppcheck false posit…
somethingwithproof Apr 15, 2026
c682b2b
fix(platform-win): acquire-fence ICMP loader pointers for ARM64 visib…
somethingwithproof Apr 15, 2026
a3e380b
fix(util): preserve original euid for spine.conf owner check after pr…
somethingwithproof Apr 15, 2026
7d46278
build(cmake): hoist _GNU_SOURCE into spine_posix_features for Linux
somethingwithproof Apr 15, 2026
77b013c
docs(platform-idioms): drop reverted command_policy mention; align wi…
somethingwithproof Apr 15, 2026
33c7868
feat(platform-win): use GetTickCount64 to avoid 49-day wrap
somethingwithproof Apr 15, 2026
7159809
build(cmake): drop hardcoded STDC_HEADERS; C17 is required
somethingwithproof Apr 15, 2026
a89cb43
docs,log: document 512B sd_status buffer; log weak ping entropy fallback
somethingwithproof Apr 15, 2026
4bb8b21
docs(readme): correct platform and feature claims
somethingwithproof Apr 15, 2026
a71b21b
test(env): exercise spine_build_child_env LD_*/DYLD_* scrubbing
somethingwithproof Apr 15, 2026
23c68d2
test(icmp-win): multi-thread loader race smoke test
somethingwithproof Apr 15, 2026
cb57b8f
feat(sandbox-linux): real seccomp allowlist, landlock, PR_SET_DUMPABLE
Apr 15, 2026
b6d3071
feat(spine): --mlock to pin credentials against swap
Apr 15, 2026
732a337
build(systemd): LimitCORE=0, LimitMEMLOCK=infinity, LimitAS=4G
Apr 15, 2026
2110794
fix(sql): DB_UseSSL defaults to preferred (1); document tri-state
Apr 15, 2026
ebc0402
feat(packaging): AppArmor profile for distro-managed enforcement
Apr 15, 2026
2ba410f
feat(packaging): SELinux policy module skeleton with enablement docs
Apr 15, 2026
9896527
docs(security): hardened_malloc and LD_PRELOAD deployment guide
Apr 15, 2026
3b16601
feat(audit): libaudit hook for lifecycle + circuit breaker events
somethingwithproof Apr 15, 2026
472e17b
feat(audit): emit AUDIT_USER_CMD events on reload/term/breaker trip
Apr 15, 2026
021c00f
Revert "fix(sql): DB_UseSSL defaults to preferred (1); document tri-s…
somethingwithproof Apr 15, 2026
6896444
fix(sql): DB_UseSSL defaults to preferred (1); document tri-state
Apr 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .clang-tidy
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
Checks: >
clang-analyzer-*,
bugprone-*,
cert-*,
-cert-err58-cpp
WarningsAsErrors: ""
HeaderFilterRegex: ".*"
FormatStyle: none
...
Empty file added .codespell-ignore-words.txt
Empty file.
31 changes: 31 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "spine dev",
"image": "mcr.microsoft.com/devcontainers/cpp:ubuntu-24.04",
"features": {
"ghcr.io/devcontainers/features/common-utils:2": {
"installZsh": true
},
"ghcr.io/devcontainers/features/github-cli:1": {}
},
"postCreateCommand": "sudo apt-get update && sudo apt-get install -y libsnmp-dev libmariadb-dev-compat libssl-dev libsystemd-dev pkg-config cmake ninja-build cppcheck clang-tools && cmake -G Ninja -S . -B build -DSPINE_BUILD_MAIN=ON && cmake --build build -j",
"customizations": {
"vscode": {
"extensions": [
"ms-vscode.cpptools",
"llvm-vs-code-extensions.vscode-clangd",
"twxs.cmake",
"ms-vscode.cmake-tools",
"github.vscode-github-actions",
"github.vscode-pull-request-github"
],
"settings": {
"C_Cpp.intelliSenseEngine": "disabled",
"clangd.arguments": [
"--background-index",
"--compile-commands-dir=build"
]
}
}
},
"remoteUser": "vscode"
}
15 changes: 12 additions & 3 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
# Test fixtures and CI scripts -- not needed for the spine build
tests/
.git/
.github/
.claude/
.omc/
.worktrees/
build/
build-*/
build-reports/
*.md
config/
m4/
autom4te.cache/
.omc/
*.conf.dist
*.log
*.o
*.a
*.so
*.dylib
.php-cs-fixer.cache
16 changes: 16 additions & 0 deletions .github/actions/install-apt-deps/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: Install apt dependencies
description: Update apt cache and install a whitespace-delimited package list.
inputs:
packages:
description: Whitespace-delimited package names to install.
required: true
runs:
using: composite
steps:
- name: Install packages
shell: bash
run: |
set -euo pipefail
sudo apt-get update
# Intentionally unquoted to split package tokens.
sudo apt-get install -y ${{ inputs.packages }}

Check failure

Code scanning / Semgrep OSS

Semgrep Finding: yaml.github-actions.security.run-shell-injection.run-shell-injection Error

Using variable interpolation ${...} with github context data in a run: step could allow an attacker to inject their own code into the runner. This would allow them to steal secrets and code. github context data can have arbitrary user input and should be treated as untrusted. Instead, use an intermediate environment variable with env: to store the data and use the environment variable in the run: script. Be sure to use double-quotes the environment variable, like this: "$ENVVAR".
Comment on lines +12 to +16
1 change: 1 addition & 0 deletions .github/cppcheck-baseline.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

5 changes: 5 additions & 0 deletions .github/instructions/instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ GNU autotools.
bounds.
- String buffers: declare length constants; do not use magic numbers for
buffer sizes.
- Public APIs: prefer `const char *` for input-only string parameters.
Document ownership expectations in function comments when transfer is not
obvious.

## SNMP

Expand Down Expand Up @@ -62,6 +65,8 @@ GNU autotools.
- Before opening a PR, run `cppcheck --enable=all --std=c11 *.c *.h`
locally and fix all errors (warnings are informational).
- flawfinder level-5 hits fail CI; lower levels are informational.
- CI has a guardrail for newly introduced unsafe C APIs (`sprintf`, `strcpy`,
`strcat`, `gets`, `vsprintf`) and fails closed on additions.

## Commits and PRs

Expand Down
12 changes: 12 additions & 0 deletions .github/nightly-leak-baseline.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"valgrind": {
"max_definitely_lost_bytes": 0,
"max_indirectly_lost_bytes": 0,
"max_possibly_lost_bytes": 0,
"max_error_summary": 0
},
"asan": {
"max_asan_error_events": 0,
"max_ubsan_error_events": 0
}
}
20 changes: 20 additions & 0 deletions .github/perf-baseline.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"sample_size": 20,
"commands": {
"./spine --version": {
"median_seconds": 0.35,
"allowed_regression_factor": 1.5,
"max_rss_kb": 32768
},
"./spine --help": {
"median_seconds": 0.45,
"allowed_regression_factor": 1.5,
"max_rss_kb": 40960
},
"snmpget -v2c -c public -On 127.0.0.1:1161 1.3.6.1.2.1.1.3.0": {
"median_seconds": 0.25,
"allowed_regression_factor": 2.0,
"max_rss_kb": 32768
}
}
}
92 changes: 92 additions & 0 deletions .github/scripts/check-leak-trend.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#!/usr/bin/env python3
"""Parse sanitizer/valgrind logs and enforce nightly leak thresholds."""

from __future__ import annotations

import argparse
import glob
import json
import re
from pathlib import Path


DEF_RE = re.compile(r"definitely lost:\s*([0-9,]+)\s+bytes")
IND_RE = re.compile(r"indirectly lost:\s*([0-9,]+)\s+bytes")
POS_RE = re.compile(r"possibly lost:\s*([0-9,]+)\s+bytes")
ERR_RE = re.compile(r"ERROR SUMMARY:\s*([0-9,]+)\s+errors")


def as_int(value: str) -> int:
return int(value.replace(",", ""))


def parse_valgrind(log_text: str) -> dict[str, int]:
return {
"definitely_lost_bytes": sum(as_int(v) for v in DEF_RE.findall(log_text)),
"indirectly_lost_bytes": sum(as_int(v) for v in IND_RE.findall(log_text)),
"possibly_lost_bytes": sum(as_int(v) for v in POS_RE.findall(log_text)),
"error_summary": sum(as_int(v) for v in ERR_RE.findall(log_text)),
}


def parse_asan(log_text: str) -> dict[str, int]:
return {
"asan_error_events": len(re.findall(r"AddressSanitizer", log_text)),
"ubsan_error_events": len(re.findall(r"runtime error:", log_text)),
}


def collect_text(patterns: list[str]) -> str:
parts: list[str] = []
for pat in patterns:
matches = sorted(glob.glob(pat))
for path in matches:
try:
parts.append(Path(path).read_text(encoding="utf-8", errors="replace"))
except OSError:
continue
return "\n".join(parts)


def enforce(summary: dict[str, int], baseline: dict[str, int]) -> list[str]:
failures: list[str] = []
for key, value in summary.items():
limit = int(baseline.get(f"max_{key}", 0))
if value > limit:
failures.append(f"{key}={value} exceeded max_{key}={limit}")
return failures


def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--mode", choices=("valgrind", "asan"), required=True)
parser.add_argument("--baseline", required=True)
parser.add_argument("--output", required=True)
parser.add_argument("--logs", nargs="+", required=True)
args = parser.parse_args()

baseline_doc = json.loads(Path(args.baseline).read_text(encoding="utf-8"))
mode_cfg = baseline_doc.get(args.mode, {})
text = collect_text(args.logs)

if args.mode == "valgrind":
summary = parse_valgrind(text)
else:
summary = parse_asan(text)

Path(args.output).write_text(json.dumps(summary, indent=2) + "\n", encoding="utf-8")

failures = enforce(summary, mode_cfg)
if failures:
print("Leak trend gate failed:")
for line in failures:
print(f"- {line}")
return 1

print(f"{args.mode} leak trend gate passed.")
print(json.dumps(summary, indent=2))
return 0


if __name__ == "__main__":
raise SystemExit(main())
30 changes: 30 additions & 0 deletions .github/scripts/check-unsafe-api-additions.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/usr/bin/env bash
set -euo pipefail

base_commit=""

if [[ -n "${GITHUB_BASE_REF:-}" ]]; then
git fetch --no-tags --unshallow origin "${GITHUB_BASE_REF}" 2>/dev/null ||
git fetch --no-tags origin "${GITHUB_BASE_REF}"
base_commit="$(git merge-base HEAD "origin/${GITHUB_BASE_REF}" 2>/dev/null || true)"
fi

if [[ -z "${base_commit}" ]]; then
base_commit="$(git rev-parse HEAD~1 2>/dev/null || git rev-list --max-parents=0 HEAD)"
fi

banned_regex='\b(sprintf|vsprintf|strcpy|strcat|gets)\s*\('

new_hits="$(
git diff --unified=0 "${base_commit}"...HEAD -- '*.c' '*.h' |
grep -E '^\+[^+]' |
grep -E "${banned_regex}" || true
)"

if [[ -n "${new_hits}" ]]; then
echo "Unsafe C APIs were newly added in this change:"
echo "${new_hits}"
exit 1
fi

echo "No newly added banned C APIs detected."
105 changes: 105 additions & 0 deletions .github/scripts/check-workflow-policy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#!/usr/bin/env python3
"""Enforce workflow hygiene policy on GitHub Actions files."""

from __future__ import annotations

import re
import sys
from pathlib import Path

import yaml


PINNED_REF_RE = re.compile(r"^[0-9a-f]{40}$")
CURL_PIPE_RE = re.compile(r"curl\b[^\n|]*\|\s*(?:sh|bash)\b")
# Accept either the strict bash form or the POSIX-sh-compatible 'set -eu'.
# Container steps on minimal images (alpine uses ash, some Debian fragments
# run under dash) cannot use 'pipefail' because dash/ash do not implement it.
ACCEPTED_FIRST_LINES = ("set -euo pipefail", "set -eu")
STRICT_LINE = "set -euo pipefail"
WORKFLOW_GLOB = ".github/workflows/*"
ALLOWLIST_CURL_PIPE = {}


def normalize_steps(job: dict) -> list[dict]:
steps = job.get("steps")
return steps if isinstance(steps, list) else []


def check_uses(path: str, step_name: str, uses_value: str, violations: list[str]) -> None:
if uses_value.startswith("./") or uses_value.startswith("docker://"):
return

if "@" not in uses_value:
violations.append(f"{path}:{step_name}: uses reference is missing @ref: {uses_value}")
return

ref = uses_value.split("@", 1)[1]
if not PINNED_REF_RE.fullmatch(ref):
violations.append(f"{path}:{step_name}: action ref must be a pinned SHA: {uses_value}")


def check_run(path: str, step_name: str, run_value: str, violations: list[str]) -> None:
lines = [ln.strip() for ln in run_value.splitlines() if ln.strip()]
if not lines:
return

if len(run_value.splitlines()) > 1:
if lines[0] not in ACCEPTED_FIRST_LINES:
violations.append(f"{path}:{step_name}: multiline run must start with one of {ACCEPTED_FIRST_LINES}")

for match in CURL_PIPE_RE.finditer(run_value):
_ = match
allow_tokens = ALLOWLIST_CURL_PIPE.get(path, [])
if not any(token in run_value for token in allow_tokens):
violations.append(f"{path}:{step_name}: curl|sh is not allowlisted")


def main() -> int:
root = Path(__file__).resolve().parents[2]
workflow_files = sorted(
p for p in root.glob(WORKFLOW_GLOB) if p.suffix in (".yml", ".yaml")
)
violations: list[str] = []

for wf in workflow_files:
rel = str(wf.relative_to(root))
try:
doc = yaml.safe_load(wf.read_text(encoding="utf-8"))
except Exception as exc: # pragma: no cover
violations.append(f"{rel}: failed to parse YAML: {exc}")
continue

jobs = doc.get("jobs", {}) if isinstance(doc, dict) else {}
if not isinstance(jobs, dict):
continue

for job_name, job in jobs.items():
if not isinstance(job, dict):
continue

for idx, step in enumerate(normalize_steps(job), start=1):
if not isinstance(step, dict):
continue
step_name = str(step.get("name", f"{job_name}.step{idx}"))

uses_value = step.get("uses")
if isinstance(uses_value, str):
check_uses(rel, step_name, uses_value.strip(), violations)

run_value = step.get("run")
if isinstance(run_value, str):
check_run(rel, step_name, run_value, violations)

if violations:
print("Workflow policy violations:")
for v in violations:
print(f"- {v}")
return 1

print("Workflow policy checks passed.")
return 0


if __name__ == "__main__":
raise SystemExit(main())
Loading
Loading