deps(go): bump github.com/prometheus/common from 0.68.1 to 0.69.0 in the go-minor-patch group across 1 directory #1841
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # ============================================================================= | |
| # NFTBan - CI: Architecture Policy | |
| # ============================================================================= | |
| # SPDX-License-Identifier: MPL-2.0 | |
| # Purpose: Enforce architecture invariants and code policy gates | |
| # | |
| # Checks: | |
| # - nft write policy (single-writer via daemon) | |
| # - Netlink import boundary (CLI must use IPC) | |
| # - Public API surface guard (only pkg/ipc + pkg/version) | |
| # - Suppression comment audit (//lint:ignore only, no //nolint:) | |
| # - Import boundary guard (CLI cannot import nftbackend) | |
| # - FHS spec generated files match committed versions | |
| # - Distro config completeness (all .conf files valid) | |
| # - Hardcoded path detector (paths should come from distro config) | |
| # - systemd directive version guard (min systemd 249) | |
| # - nft template placeholder guard (rendered config must be boot-safe) | |
| # - Anchor jump placement lint G1-G3 (v1.64.0) | |
| # - Anchor marker count validation (v1.64.0) | |
| # - Bash hazard lint H1-H2 (v1.66.3): nft -a flag + set -e traps | |
| # - Update safety lint U1-U2 (v1.70.0): rebuild failure must be fatal | |
| # - Module isolation lint R-10 (v1.110.0): distinct ModuleName + EventBan | |
| # source-label + Status.Extra cross-module key isolation (with baseline) | |
| # ============================================================================= | |
| name: Architecture Policy | |
| on: | |
| push: | |
| branches: [main, master, develop] | |
| pull_request: | |
| branches: [main, master] | |
| concurrency: | |
| group: ci-arch-${{ github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| jobs: | |
| architecture-check: | |
| name: Policy Gates | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| # ===================================================================== | |
| # EXISTING CHECKS | |
| # ===================================================================== | |
| - name: Check nft write policy | |
| run: ./scripts/ci/check-nft-writes.sh | |
| env: | |
| # Explicit: NEVER use --warn-all in CI (would disable enforcement) | |
| WARN_ALL: "0" | |
| # v1.192 transition-atomicity class (D-NFTBAN-SOAK-...-DROP-WINDOW + F-FEED/F-GEO): | |
| # forbid flush/delete-then-repopulate in a separate transaction on the live | |
| # nftban table (V-NFT-REBUILD-ATOMICITY) or shared sets (V-NFT-SET-REFRESH-ATOMICITY). | |
| - name: Check nft transition atomicity | |
| run: ./scripts/ci/check-nft-atomicity.sh | |
| - name: Check netlink import policy | |
| run: | | |
| echo "=== Checking netlink import architecture policy ===" | |
| VIOLATIONS=0 | |
| echo "Checking cmd/nftban-core for forbidden netlink imports..." | |
| if grep -r "github.com/google/nftables" cmd/nftban-core/ 2>/dev/null; then | |
| echo "::error::cmd/nftban-core imports netlink directly — CLI must use IPC (pkg/ipc)" | |
| VIOLATIONS=1 | |
| else | |
| echo "✓ cmd/nftban-core: No direct netlink imports" | |
| fi | |
| echo "" | |
| echo "Verifying daemon uses netlink (expected)..." | |
| if grep -q "github.com/google/nftables" cmd/nftband/main.go 2>/dev/null || \ | |
| grep -q "nftbackend" cmd/nftband/main.go 2>/dev/null; then | |
| echo "✓ cmd/nftband: Uses nftbackend (correct — daemon owns nft writes)" | |
| fi | |
| if [[ $VIOLATIONS -gt 0 ]]; then | |
| exit 1 | |
| fi | |
| echo "✓ Netlink import policy: PASSED" | |
| - name: Public API surface guard | |
| run: | | |
| echo "=== Checking public API boundary (pkg/) ===" | |
| VIOLATIONS=0 | |
| for gofile in $(find pkg/ -name "*.go" -type f 2>/dev/null); do | |
| dir=$(dirname "$gofile") | |
| case "$dir" in | |
| pkg/ipc|pkg/ipc/*|pkg/version|pkg/version/*) ;; | |
| *) | |
| echo "::error::$gofile is outside allowed pkg/ directories — move to internal/" | |
| VIOLATIONS=1 | |
| ;; | |
| esac | |
| done | |
| if [[ $VIOLATIONS -gt 0 ]]; then | |
| exit 1 | |
| fi | |
| echo "✓ Public API surface: PASS (only pkg/ipc and pkg/version)" | |
| - name: Suppression comment audit | |
| run: | | |
| echo "=== Auditing suppression comments ===" | |
| NOSEC_COUNT=$(grep -r '#nosec' --include="*.go" . 2>/dev/null | wc -l || echo 0) | |
| NOLINT_COUNT=$(grep -r '//nolint:' --include="*.go" . 2>/dev/null | wc -l || echo 0) | |
| LINT_IGNORE_COUNT=$(grep -r '//lint:ignore' --include="*.go" . 2>/dev/null | wc -l || echo 0) | |
| echo " #nosec: $NOSEC_COUNT" | |
| echo " //nolint:: $NOLINT_COUNT" | |
| echo " //lint:ignore: $LINT_IGNORE_COUNT" | |
| echo "" | |
| if [[ "$NOLINT_COUNT" -gt 0 ]]; then | |
| echo "::error::Found $NOLINT_COUNT //nolint: directives — use //lint:ignore <CHECK> <reason>" | |
| grep -rn '//nolint:' --include="*.go" . 2>/dev/null || true | |
| exit 1 | |
| fi | |
| echo "✓ Suppression audit: PASS" | |
| - name: Import boundary guard | |
| run: | | |
| echo "=== Checking import boundaries ===" | |
| if grep -r 'internal/nftbackend' cmd/nftban-core/ --include="*.go" 2>/dev/null; then | |
| echo "::error::cmd/nftban-core/ imports internal/nftbackend — CLI must use pkg/ipc" | |
| exit 1 | |
| fi | |
| echo "✓ Import boundary: PASS (CLI does not import nftbackend)" | |
| - name: FHS spec generated files check | |
| run: | | |
| echo "=== Verifying FHS spec generated files are up to date ===" | |
| if ! ./build/generate-fhs-outputs.sh --check; then | |
| echo "" | |
| echo "::error::Generated FHS files are out of date — run: ./build/generate-fhs-outputs.sh" | |
| exit 1 | |
| fi | |
| echo "✓ FHS spec: PASS (generated files match YAML spec)" | |
| - name: Systemd install-list generated file check | |
| run: | | |
| echo "=== Verifying systemd install-list is up to date ===" | |
| if ! ./build/generate-systemd-install-list.sh --check; then | |
| echo "" | |
| echo "::error::install/packaging/systemd/nftban-systemd-install.list is stale — run: ./build/generate-systemd-install-list.sh" | |
| exit 1 | |
| fi | |
| echo "✓ Systemd install-list: PASS (matches install/systemd source)" | |
| - name: docs/systemd/UNITS.md count parity | |
| run: | | |
| echo "=== Verifying docs/systemd/UNITS.md counts match install list ===" | |
| list=install/packaging/systemd/nftban-systemd-install.list | |
| fs_timer=$(grep -cE '\.timer$' "$list") | |
| fs_svc=$(grep -cE '\.service$' "$list") | |
| fs_socket=$(grep -cE '\.socket$' "$list") | |
| doc_timer=$(grep -oE '^## Timers \([0-9]+\)' docs/systemd/UNITS.md | grep -oE '[0-9]+' || echo 0) | |
| doc_svc=$(grep -oE '^## Services \([0-9]+\)' docs/systemd/UNITS.md | grep -oE '[0-9]+' || echo 0) | |
| doc_socket=$(grep -oE '^## Sockets \([0-9]+\)' docs/systemd/UNITS.md | grep -oE '[0-9]+' || echo 0) | |
| fail=0 | |
| [ "$fs_timer" = "$doc_timer" ] || { echo "::error::timer count drift: list=$fs_timer doc=$doc_timer"; fail=1; } | |
| [ "$fs_svc" = "$doc_svc" ] || { echo "::error::service count drift: list=$fs_svc doc=$doc_svc"; fail=1; } | |
| [ "$fs_socket" = "$doc_socket" ] || { echo "::error::socket count drift: list=$fs_socket doc=$doc_socket"; fail=1; } | |
| [ "$fail" = "0" ] || exit 1 | |
| echo "✓ docs/systemd/UNITS.md counts: PASS ($fs_timer timers, $fs_svc services, $fs_socket socket)" | |
| - name: Systemd maintainer-script generated files check | |
| run: | | |
| echo "=== Verifying systemd maintainer-script artifacts are up to date ===" | |
| if ! ./build/generate-systemd-maintainer-scripts.sh --check; then | |
| echo "" | |
| echo "::error::Systemd maintainer-script artifacts are stale — run: ./build/generate-systemd-maintainer-scripts.sh" | |
| exit 1 | |
| fi | |
| echo "✓ Systemd maintainer-scripts: PASS (matches generator output)" | |
| - name: C3 maintainer-script structural assertions (A5) | |
| run: | | |
| echo "=== A5: structural assertions on generated cleanup artifacts ===" | |
| rpm_art=install/packaging/rpm/nftban-preun-systemd-cleanup.inc | |
| deb_art=install/packaging/deb/nftban-prerm-systemd-cleanup.inc | |
| fail=0 | |
| # A5.1 — both artifacts contain mask_if_exists helper | |
| grep -qE '^mask_if_exists\(\)' "$rpm_art" || { echo "::error::RPM artifact missing mask_if_exists() helper"; fail=1; } | |
| grep -qE '^mask_if_exists\(\)' "$deb_art" || { echo "::error::DEB artifact missing mask_if_exists() helper"; fail=1; } | |
| # A5.2 — both artifacts have the readlink /dev/null boundary check | |
| grep -qE 'readlink "/etc/systemd/system/\$unit".*= *"/dev/null"' "$rpm_art" || { echo "::error::RPM artifact missing readlink /dev/null check (A1 policy)"; fail=1; } | |
| grep -qE 'readlink "/etc/systemd/system/\$unit".*= *"/dev/null"' "$deb_art" || { echo "::error::DEB artifact missing readlink /dev/null check (A1 policy)"; fail=1; } | |
| # A5.3 — no unconditional `systemctl mask nftban-ui*` literal outside mask_if_exists in any maintainer-script source | |
| if grep -nE 'systemctl mask "[^"]*nftban-ui[^"]*"' packaging/build_nftban.sh packaging/deb/prerm 2>/dev/null \ | |
| | grep -v 'mask_if_exists'; then | |
| echo "::error::unconditional systemctl mask nftban-ui* found outside mask_if_exists (A1 regression)" | |
| fail=1 | |
| fi | |
| [ "$fail" = "0" ] || exit 1 | |
| echo "✓ C3 structural assertions: PASS (mask_if_exists + /dev/null boundary + no unconditional mask)" | |
| # ===================================================================== | |
| # MFST-C6 (v1.107): packaging-wire structural assertions (AM-4) | |
| # ===================================================================== | |
| # Proves both packagers consume generated manifest authority artifacts. | |
| # If a future edit silently replaces a generator-driven %include / while-read | |
| # loop with hardcoded content, these greps fail before the build runs. | |
| # AM-4.1: RPM Source1 + %include of nftban-files.inc | |
| # AM-4.2: DEB build_deb() while-read of nftban.dirs | |
| # AM-4.3: RPM/DEB consume nftban-systemd-install.list | |
| - name: MFST-C6 packaging-wire structural assertions (AM-4) | |
| run: | | |
| echo "=== MFST-C6 / AM-4: packaging-wire structural assertions ===" | |
| spec=packaging/build_nftban.sh | |
| fail=0 | |
| # AM-4.1 — RPM consumes generated nftban-files.inc | |
| grep -qE '^Source1:[[:space:]]+nftban-files\.inc' "$spec" \ | |
| || { echo "::error::AM-4.1: RPM Source1: nftban-files.inc missing in $spec"; fail=1; } | |
| grep -qE '^%include[[:space:]]+%\{_sourcedir\}/nftban-files\.inc' "$spec" \ | |
| || { echo "::error::AM-4.1: %include %{_sourcedir}/nftban-files.inc missing in $spec"; fail=1; } | |
| # AM-4.2 — DEB build_deb() consumes generated nftban.dirs via while-read | |
| if ! awk '/^build_deb\(\)/,/^\}/' "$spec" \ | |
| | grep -qE 'install/packaging/deb/nftban\.dirs'; then | |
| echo "::error::AM-4.2: build_deb() does not reference install/packaging/deb/nftban.dirs in $spec" | |
| fail=1 | |
| fi | |
| if ! awk '/^build_deb\(\)/,/^\}/' "$spec" \ | |
| | grep -qE 'while[[:space:]]+(IFS=.*[[:space:]]+)?read'; then | |
| echo "::error::AM-4.2: build_deb() lacks while-read loop consuming nftban.dirs in $spec" | |
| fail=1 | |
| fi | |
| # AM-4.3 — RPM and DEB both consume generated nftban-systemd-install.list | |
| grep -qE '^Source2:[[:space:]]+nftban-systemd-install\.list' "$spec" \ | |
| || { echo "::error::AM-4.3: RPM Source2: nftban-systemd-install.list missing in $spec"; fail=1; } | |
| # %install RPM body must reference the generated install list | |
| grep -qE '%\{_sourcedir\}/nftban-systemd-install\.list' "$spec" \ | |
| || { echo "::error::AM-4.3: RPM %install does not reference %{_sourcedir}/nftban-systemd-install.list in $spec"; fail=1; } | |
| # build_deb() must reference the generated install list | |
| if ! awk '/^build_deb\(\)/,/^\}/' "$spec" \ | |
| | grep -qE 'install/packaging/systemd/nftban-systemd-install\.list'; then | |
| echo "::error::AM-4.3: build_deb() does not reference install/packaging/systemd/nftban-systemd-install.list in $spec" | |
| fail=1 | |
| fi | |
| [ "$fail" = "0" ] || exit 1 | |
| echo "✓ MFST-C6 / AM-4: PASS (RPM Source1+%include + DEB while-read + RPM/DEB systemd-install-list)" | |
| # ===================================================================== | |
| # MFST-C6 (v1.107): UNITS.md per-row parity (E6) | |
| # ===================================================================== | |
| # Closes content-gap left by the existing count-only check above. | |
| # Asserts every active unit listed in nftban-systemd-install.list (the C1 | |
| # source-of-truth) appears as a backticked table row in UNITS.md. | |
| - name: MFST-C6 docs/systemd/UNITS.md table-row parity (E6) | |
| run: | | |
| echo "=== MFST-C6 / E6: UNITS.md per-row parity vs systemd install list ===" | |
| list=install/packaging/systemd/nftban-systemd-install.list | |
| doc=docs/systemd/UNITS.md | |
| fail=0 | |
| missing=0 | |
| while IFS= read -r unit; do | |
| # Skip blank lines and comments | |
| case "$unit" in | |
| ''|\#*) continue ;; | |
| esac | |
| # Each unit must appear as a backticked token somewhere in UNITS.md | |
| if ! grep -qF "\`${unit}\`" "$doc"; then | |
| echo "::error::E6: docs/systemd/UNITS.md missing row for unit: ${unit}" | |
| missing=$((missing + 1)) | |
| fail=1 | |
| fi | |
| done < "$list" | |
| [ "$fail" = "0" ] || { echo "::error::E6: ${missing} unit(s) missing from $doc"; exit 1; } | |
| echo "✓ MFST-C6 / E6: PASS (every unit in $list has a backticked row in $doc)" | |
| # ===================================================================== | |
| # NEW CHECKS (v1.50.1 — Distribution Compatibility Audit) | |
| # ===================================================================== | |
| - name: Distro config completeness | |
| run: | | |
| echo "=== Validating distribution config files ===" | |
| ./cli/lib/nftban/tests/validate_distro_configs.sh etc/nftban/distros/ | |
| - name: Hardcoded path detector | |
| run: | | |
| echo "=== Checking for hardcoded binary paths ===" | |
| # | |
| # Shell scripts should use distro config variables, not hardcoded paths. | |
| # Allowed exceptions: | |
| # - etc/nftban/distros/*.conf (distro configs define the paths) | |
| # - cli/lib/nftban/tests/ (test fixtures) | |
| # - cli/lib/nftban/data/ (FHS spec, schemas) | |
| # - install/ (install scripts target known paths) | |
| # - packaging/ (RPM/DEB specs target known paths) | |
| # - .github/ (CI scripts) | |
| # | |
| VIOLATIONS=0 | |
| TMPFILE=$(mktemp) | |
| trap 'rm -f "$TMPFILE"' EXIT | |
| # Detect hardcoded /var/log/secure or /var/log/auth.log in runtime code | |
| # These differ between RHEL (/var/log/secure) and Debian (/var/log/auth.log) | |
| grep -rn '/var/log/secure\|/var/log/auth\.log' \ | |
| --include="*.sh" \ | |
| cli/lib/nftban/core/ \ | |
| cli/lib/nftban/cli/ \ | |
| cli/lib/nftban/lib/ \ | |
| cli/lib/nftban/setup/ \ | |
| 2>/dev/null | \ | |
| grep -v '#.*hardcoded\|DISTRO_PATHS\|fallback\|default\|example\|# ' | \ | |
| grep -v 'nftban_distro_config\.sh' \ | |
| >> "$TMPFILE" || true | |
| COUNT=$(wc -l < "$TMPFILE" | tr -d ' ') | |
| if [[ "$COUNT" -gt 0 ]]; then | |
| echo "⚠ Found $COUNT hardcoded auth log paths (should use DISTRO_PATHS[auth_log]):" | |
| cat "$TMPFILE" | |
| echo "" | |
| echo "NOTE: Reporting as warning — fix in future PR" | |
| else | |
| echo "✓ No hardcoded auth log paths in runtime code" | |
| fi | |
| # Detect hardcoded /sbin/nft (Debian legacy path) — should always be /usr/sbin/nft or variable | |
| grep -rn '"/sbin/nft"\|/sbin/nft ' \ | |
| --include="*.sh" --include="*.go" \ | |
| cli/lib/nftban/core/ \ | |
| cli/lib/nftban/cli/ \ | |
| cli/lib/nftban/lib/ \ | |
| cmd/ internal/ \ | |
| 2>/dev/null | \ | |
| grep -v '#.*\|//.*\|test\|example' \ | |
| > "$TMPFILE" || true | |
| COUNT=$(wc -l < "$TMPFILE" | tr -d ' ') | |
| if [[ "$COUNT" -gt 0 ]]; then | |
| echo "" | |
| echo "::error::Found $COUNT references to /sbin/nft (legacy path) — use /usr/sbin/nft or distro config" | |
| cat "$TMPFILE" | |
| VIOLATIONS=1 | |
| else | |
| echo "✓ No legacy /sbin/nft paths" | |
| fi | |
| if [[ $VIOLATIONS -gt 0 ]]; then | |
| exit 1 | |
| fi | |
| echo "" | |
| echo "✓ Hardcoded path check: PASS" | |
| - name: systemd directive version guard | |
| run: | | |
| echo "=== Checking systemd directives against minimum version (249) ===" | |
| # | |
| # NFTBan supports systemd 249+ (EL9 baseline). | |
| # Flag directives that require newer systemd versions. | |
| # | |
| VIOLATIONS=0 | |
| # Directives requiring systemd >= 250+ | |
| FORBIDDEN_DIRECTIVES=( | |
| "SetCredential=" # 250 | |
| "LoadCredential=" # 250 | |
| "LoadCredentialEncrypted=" # 250 | |
| "RuntimeRandomizedExtraSec=" # 251 | |
| "ExecSearchPath=" # 252 | |
| "OpenFile=" # 253 | |
| "MemoryZSwapMax=" # 254 | |
| "CoredumpReceive=" # 255 | |
| "ExitType=" # 256 | |
| "SurviveFinalKillSignal=" # 256 | |
| ) | |
| for directive in "${FORBIDDEN_DIRECTIVES[@]}"; do | |
| name="${directive%=}" | |
| if grep -rn "$directive" install/systemd/ 2>/dev/null | grep -v '^#' | grep -v '#.*'; then | |
| echo "::error::Found $name in systemd units — requires systemd > 249 (min supported)" | |
| VIOLATIONS=$((VIOLATIONS + 1)) | |
| fi | |
| done | |
| if [[ $VIOLATIONS -gt 0 ]]; then | |
| exit 1 | |
| fi | |
| echo "✓ All systemd directives compatible with systemd 249+ (EL9 baseline)" | |
| - name: nft template placeholder guard | |
| run: | | |
| echo "=== Verifying boot-safety of shipped nft config ===" | |
| # | |
| # Invariant: install/nftables/nftables.conf must NEVER contain placeholders. | |
| # It is included by systemd nftables.service at boot — raw placeholders = no firewall. | |
| # | |
| # The template (nftables.conf.tpl) SHOULD have placeholders. | |
| # | |
| # 1. Rendered config must be placeholder-free | |
| if grep -qE '__[A-Z_]{3,}__' install/nftables/nftables.conf 2>/dev/null; then | |
| echo "::error::install/nftables/nftables.conf contains placeholders — BOOT UNSAFE!" | |
| echo "This file is loaded by nftables.service at boot. Placeholders cause nft syntax errors." | |
| echo "Render with safe defaults: __SSH_PORT__→22, __CT_LIMIT_SSH__→15, etc." | |
| grep -n '__[A-Z_]\+__' install/nftables/nftables.conf | |
| exit 1 | |
| fi | |
| echo "✓ Rendered config (nftables.conf) is placeholder-free" | |
| # 2. Template must HAVE placeholders (sanity check) | |
| if [[ -f install/nftables/nftables.conf.tpl ]]; then | |
| if ! grep -qE '__[A-Z_]{3,}__' install/nftables/nftables.conf.tpl; then | |
| echo "::error::install/nftables/nftables.conf.tpl has NO placeholders — template is broken" | |
| exit 1 | |
| fi | |
| echo "✓ Template (nftables.conf.tpl) contains placeholders" | |
| fi | |
| # 3. Safe config must not have CT_LIMIT placeholders | |
| if [[ -f install/nftables/nftables-safe.conf ]]; then | |
| if grep -qE '__CT_LIMIT_[A-Z]+__' install/nftables/nftables-safe.conf; then | |
| echo "::error::nftables-safe.conf contains CT_LIMIT placeholders — must use hardcoded defaults" | |
| exit 1 | |
| fi | |
| echo "✓ Safe config has no CT_LIMIT placeholders" | |
| fi | |
| echo "" | |
| echo "✓ Boot-safety check: PASS" | |
| # v1.64.0: Anchor jump placement lint (static analysis — no nft binary needed) | |
| - name: Lint jump placement (G1-G3) | |
| run: bash scripts/lint-jump-placement.sh | |
| # v1.66.3: Bash hazard lint — nft -a flag + set -e traps | |
| - name: Lint bash hazards (H1-H2) | |
| run: bash scripts/lint-bash-hazards.sh | |
| # v1.70.0: Update safety lint — rebuild failure must be fatal, no fallback reload | |
| - name: Lint update safety (U1-U2) | |
| run: bash scripts/lint-update-safety.sh | |
| # v1.72.0: RPM postinst source-leak regression test | |
| - name: Test RPM postinst source leak (T1-T2) | |
| run: bash scripts/test-rpm-postinst-source-leak.sh | |
| # v1.64.0: Anchor marker count in template | |
| - name: Validate anchor markers in template | |
| run: | | |
| echo "=== Verifying NFTBAN_ANCHOR markers ===" | |
| TPL="install/nftables/nftables.conf.tpl" | |
| CONF="install/nftables/nftables.conf" | |
| for f in "$TPL" "$CONF"; do | |
| count=$(grep -c 'NFTBAN_ANCHOR:ANCHOR_' "$f" || true) | |
| if [[ "$count" -ne 28 ]]; then | |
| echo "::error::${f}: expected 28 NFTBAN_ANCHOR markers, found ${count}" | |
| exit 1 | |
| fi | |
| echo "✓ ${f}: ${count} anchor markers" | |
| done | |
| echo "" | |
| echo "✓ Anchor marker check: PASS" | |
| # v1.77.0: Registry parity lint — completion matches registry | |
| # (G15-A; G15-B man-page parity removed in V123 B-1 / PR #646 + | |
| # residual-refs PR — man-page source decommissioned in favour of | |
| # the registry-canonical `nftban help` + Wiki + completion). | |
| - name: Lint registry parity (G15) | |
| run: bash scripts/lint-registry-parity.sh | |
| # ===================================================================== | |
| # v1.79.0: Truth and Rebuild Safety Gates (G16-G18) | |
| # ===================================================================== | |
| # G16: Validator unit tests - already covered by secure-go.yml (go test ./...) | |
| # The validator tests in internal/validator/validator_test.go run as part of | |
| # the standard Go test suite. This comment documents the gate for traceability. | |
| # G17: Health JSON schema validation | |
| - name: Lint health JSON schema (G17) | |
| run: bash scripts/lint-health-json-schema.sh | |
| # G18: Rebuild safety simulation | |
| - name: Test rebuild safety patterns (G18) | |
| run: bash scripts/test-rebuild-safety.sh | |
| # ===================================================================== | |
| # v1.84 M84-4: Contract Freeze Gates | |
| # ===================================================================== | |
| # G1-1: Vocabulary enforcement — no banned terms in CLI output | |
| - name: Banned phrase scan (G1-1) | |
| run: bash cli/lib/nftban/tests/test_banned_phrases.sh | |
| # G2-3: Schema version guard — Go source matches CLI expectations | |
| - name: Schema version guard (G2-3) | |
| run: bash cli/lib/nftban/tests/test_schema_version_guard.sh | |
| # ===================================================================== | |
| # v1.85 M85-3: Module Completeness Gates | |
| # ===================================================================== | |
| # G8-1/G8-2/G8-3 run as Go tests (already in ci-go.yml via go test ./...) | |
| # G8-4 is a runtime smoke test — skips gracefully in CI (no validator binary) | |
| - name: Module smoke test (G8-4) | |
| run: bash cli/lib/nftban/tests/test_module_selftest.sh | |
| # ===================================================================== | |
| # v1.134 PR-D: help/code subcommand correlation gates | |
| # ===================================================================== | |
| # Doctest guard: every primary-dispatch subcommand a command accepts must | |
| # be documented in help, declared in the intentional-alias allowlist, or | |
| # structural. Catches NEW undocumented subcommands (the drift class that | |
| # produced D-05..D-11). Unparseable families are SKIPPED, never failed. | |
| - name: Help/code doctest guard (v1.134 PR-D) | |
| run: bash cli/lib/nftban/tests/v130_subcommand_flag_help_code_test.sh | |
| # Allowlist integrity + D-26 (health rbl/fhs documented) + D-28 (metrics | |
| # error-usage complete); fails if an allowlisted token is not actually | |
| # dispatched (no phantom allowlist entries). | |
| - name: Alias allowlist integrity (v1.134 PR-D P3) | |
| run: bash cli/lib/nftban/tests/v134_pr_d_p3_alias_allowlist_test.sh | |
| # ===================================================================== | |
| # v1.139.2 hotfix: CLI safety + truth-telling gates | |
| # ===================================================================== | |
| # Rollback help-guard: `nftban rollback --help` MUST show help text and | |
| # MUST NOT trigger a real rollback. Prior to v1.139.2 the dispatcher | |
| # invoked _do_rollback without parsing --help; a CLI-audit invocation | |
| # downgraded a host. Defense-in-depth guard is at _do_rollback's entry | |
| # so every current and future caller is safe. | |
| - name: Rollback --help guard (v1.139.2) | |
| run: bash cli/lib/nftban/tests/rollback_help_guard_v1_139_2_test.sh | |
| # CLI exit-code contract: bogus subcommand, ban (no arg), ban (invalid | |
| # IP), and update --dry-run MUST return rc>=1 (declared by `nftban --help` | |
| # itself: "0 Success / 1 Error / 2 Warning"). Locks the current correct | |
| # behavior so a future PR cannot regress to silently swallowing errors | |
| # in `nftban X || alert`-style automation. | |
| - name: CLI exit-code contract (v1.139.2) | |
| run: bash cli/lib/nftban/tests/cli_error_rc_contract_v1_139_2_test.sh | |
| # ===================================================================== | |
| # v1.144.0 PR-D: D-UXV-13 runtime-hint reachability guard (class-killer) | |
| # ===================================================================== | |
| # Reverse-direction guard for the doctest pair at lines 510 + 516 above: | |
| # the v130 forward guard checks that every dispatched subcommand is | |
| # documented; this v144 reverse guard checks that every echo/printf | |
| # 'nftban X Y' literal in cli/sbin/nftban + cli/lib/nftban/cli/cmd_*.sh | |
| # resolves to a reachable command. Catches the drift class that | |
| # produced D-UXV-14 (cmd_connector.sh:234 'nftban connector edit' — | |
| # no edit arm) and D-UXV-15 (cmd_port.sh four sites 'nftban reload' / | |
| # 'nftban port reload' — neither command exists; canonical is | |
| # 'nftban firewall reload'). Would also have caught the 2026-06-01 | |
| # audit's incorrect-by-half proposed replacement. | |
| # | |
| # T3-3/4/5 inside the test are the HARD pass criteria (would-have- | |
| # caught the 3 historical broken hints). T3-2 reports legacy | |
| # parser-heuristic drift as a v1.145.x sweep candidate (does not | |
| # block CI). New broken hints introduced in any future PR will | |
| # surface as additional violations and require explicit fix or | |
| # allowlist entry with `reason` field. | |
| - name: Runtime-hint reachability guard (v1.144.0 PR-D D-UXV-13) | |
| run: bash cli/lib/nftban/tests/cli_runtime_hint_reachability_v144_test.sh | |
| # Companion that locks the allowlist's `reason` field requirement + | |
| # restates the would-have-caught hypotheticals as defense-in-depth. | |
| - name: Help-block reachability companion (v1.144.0 PR-D) | |
| run: bash cli/lib/nftban/tests/cli_help_block_reachability_v144_test.sh | |
| # ===================================================================== | |
| # v1.145 SSH-port lifecycle guards (PR-A/PR-B/PR-C2 + PR-G wiring) | |
| # ===================================================================== | |
| # These static guards lock the set-driven SSH brute-force rate-limit | |
| # (`tcp dport @ssh_ports ct count`): the ssh_ports set in every template, | |
| # its presence in the schema inventory (both families), both-set | |
| # (tcp_ports_in + ssh_ports) runtime parity, union detection, and the | |
| # apply-path hardening. They existed since v1.145 but were never wired | |
| # into CI (v1.145 PR-G audit finding); without these `run:` steps the | |
| # guards provided zero regression protection. | |
| - name: SSH ssh_ports template set-driven guard (v1.145 PR-A) | |
| run: bash cli/lib/nftban/tests/v145_ssh_port_template_set_driven_static_test.sh | |
| - name: SSH union detection guard (v1.145 PR-B) | |
| run: bash cli/lib/nftban/tests/v145_ssh_port_detect_test.sh | |
| - name: SSH both-set parity runtime guard (v1.145 PR-B) | |
| run: bash cli/lib/nftban/tests/v145_pr_b_runtime_static_guard_test.sh | |
| - name: SSH apply-path hardening guard (v1.145 PR-C2) | |
| run: bash cli/lib/nftban/tests/v145_pr_c2_apply_hardening_test.sh | |
| - name: ssh_ports systemic-alignment invariants (v1.145 PR-G) | |
| run: bash cli/lib/nftban/tests/v145_pr_g_ssh_ports_invariants_test.sh | |
| # External admin SSH-port guard (OBS-SSHPORT-55000-FAMILY): warn-only + | |
| # lockout-net. Asserts the guard warns on an external :55000->:22 redirect, | |
| # session-whitelists the admin IP via the IPC whitelist-session path only | |
| # (no direct nft write), keeps ssh_ports = {22} (REJECT import), and is a | |
| # no-op off-session / on dry-run / on opt-out / on re-entry. | |
| - name: External admin SSH-port guard (OBS-SSHPORT-55000) | |
| run: bash cli/lib/nftban/tests/ssh_admin_port_guard_v150_test.sh | |
| # v1.155 PR-1 (item 3.2): socket-activated sshd port-mismatch warning. | |
| # Asserts the guard warns ONLY when configured (sshd -T) != listening (ss) | |
| # AND sshd is socket-activated, prints the exact remediation hint | |
| # (`systemctl daemon-reload && systemctl restart ssh.socket`), and is | |
| # silent when ports match or on a plain sshd.service. Hermetic. | |
| - name: Socket-activated SSH port-mismatch warning (v1.155 PR-1) | |
| run: bash cli/lib/nftban/tests/ssh_socket_port_mismatch_v155_test.sh | |
| # v1.155 PR-2 (item 3.3): SSH-port-change lifecycle validator invariants. | |
| # Exercises tools/validation/ssh_port_change_lifecycle_validate.sh against a | |
| # good rendered-ruleset fixture (exit 0) and four injected drifts (listener | |
| # not in ssh_ports / not in tcp_ports_in; literal `tcp dport 22 ct count` | |
| # instead of @ssh_ports; missing @ssh_ports rule), each failing with the | |
| # right invariant message. Hermetic (inputs injected via env). | |
| - name: SSH-port-change lifecycle validator (v1.155 PR-2) | |
| run: bash cli/lib/nftban/tests/ssh_port_change_lifecycle_v155_test.sh | |
| # v1.161: default-enabled-timer first-run guard (geoban lesson, v1.156->v1.159). | |
| # Enumerates the installer's auto-enabled timers (coreTimers in | |
| # internal/installer/services/timers.go) and asserts each timer's .service | |
| # EITHER has all ExecStart deps shipped/always-present OR carries an | |
| # ExecCondition that SKIPS (not fails) when an optional dep is absent — so a | |
| # default-enabled timer cannot hard-fail on a package-default host and flip | |
| # INSTALL_STATE=DEGRADED (the v1.156 nftban-geoban-refresh regression). | |
| # Hermetic / repo-static (no systemd, no host). | |
| - name: Default-enabled timer first-run guard (v1.161) | |
| run: bash cli/lib/nftban/tests/default_enabled_timer_first_run_guard_v161.sh | |
| # v1.162 PR-A (DELTA §3.1): SSH durable multi-port render + reboot-sim. | |
| # Sources cmd_firewall.sh's _firewall_substitute_placeholders, mocks the | |
| # SSH detector to a multi-port union (22, 2222, 55000), renders the real | |
| # install/nftables/nftables.conf.tpl, and asserts the durable `set | |
| # ssh_ports` carries the FULL union (not primary-only) in BOTH families, | |
| # the SSH ct-count rule stays set-driven (`tcp dport @ssh_ports`), no | |
| # __SSH_PORT__ placeholder survives, and (reboot-sim) the durable file | |
| # alone re-reads with every union port intact (no kernel reconcile). | |
| # Hermetic (TMPDIR sandbox; no host, no systemd, no sshd). | |
| - name: SSH durable multi-port render + reboot-sim (v1.162 PR-A) | |
| run: bash cli/lib/nftban/tests/ssh_durable_multiport_render_v162.sh | |
| # v1.162 PR-B (DELTA §3.5): static fallback install/nftables/nftables.conf | |
| # must be set-driven — `set ssh_ports` defined in both ip/ip6 tables and the | |
| # SSH ct-count rule reads `tcp dport @ssh_ports` (no literal `tcp dport 22`), | |
| # set defined-before-use. Static grep guard (the boot-baseline `nft -c` | |
| # parse is exercised by the package install/runtime CI legs). | |
| - name: Static nftables fallback is set-driven (v1.162 PR-B) | |
| run: bash cli/lib/nftban/tests/static_nftables_ssh_set_driven_v162.sh | |
| # v1.165 PR-A: generated-spec comment-macro lint. rpm 4.16 (EL9/EL10) | |
| # parses a bare unescaped %install token in a SPEC COMMENT as a second | |
| # %install section (`error: second %install`) and fails Build RPM | |
| # el9/el10 deterministically — this bit twice (v1.157 FIX_V157_PR_A, | |
| # v1.164 PR-C revert) because rpm 4.18 (lab2) + rpm 6.x are lenient and | |
| # miss it. This hermetic static guard catches the class BEFORE the | |
| # expensive el9/el10 rpmbuild: zero %install outside the single section | |
| # header, and no bare RPM section macro in any generated-spec comment. | |
| - name: RPM spec has no section macro in a comment (v1.165 PR-A) | |
| run: bash cli/lib/nftban/tests/rpm_spec_no_section_macro_in_comment_v165_test.sh | |
| # v1.165 PR-B: the %files section both %include nftban-files.inc (which | |
| # %dir-owns every /usr/lib/nftban payload dir) and used to repeat those | |
| # dirs as bare payload paths, so strict rpm 4.16 warned "File listed | |
| # twice". PR-B lists the 12 lib dirs as <dir>/* (inc keeps sole %dir | |
| # ownership) and strips dotfiles in %install so the /* glob (which skips | |
| # dotfiles) does not orphan tests/.gitkeep. This static guard asserts the | |
| # dedup shape + the dotfile strip; the full no-double-listing / no-orphan | |
| # parse is confirmed by Build RPM el9/el10. | |
| - name: RPM %files lib-dir dedup (no double dir listing) (v1.165 PR-B) | |
| run: bash cli/lib/nftban/tests/rpm_files_no_double_dir_listing_v165_test.sh | |
| # v1.166 PR-C: templates %files dedup via FHS-generator correction. The | |
| # bare /usr/share/nftban/templates line both double-listed the generator | |
| # %dir-owned dirs and solely owned the email/+partials/ staged .html (no | |
| # inc %dir), so it could not be dropped naively. PR-C adds %dir for | |
| # templates/email + templates/partials to build/fhs-spec.yaml (regenerated | |
| # into nftban-files.inc), lists the 4 content subdirs as <dir>/*, keeps | |
| # zabbix as an empty %dir, and strips dotfiles in %install. This guard | |
| # asserts the dedup shape + generator ownership; the no-orphan/no-double | |
| # parse is confirmed by Build RPM el9/el10. (FHS body changes by design — | |
| # the "FHS spec generated files check" gate enforces generator parity.) | |
| - name: RPM %files templates dedup (FHS-generator correction) (v1.166 PR-C) | |
| run: bash cli/lib/nftban/tests/rpm_files_templates_dedup_v166_test.sh | |
| # v1.167 UX-residual lane guards (were merged un-wired; wired here as part | |
| # of the v1.167 blocker fix so future regressions are caught in CI). | |
| - name: Feed-counter unification (v1.167 PR-1, BUG-CtCount-feeds) | |
| run: bash cli/lib/nftban/tests/feed_counters_unify_v167_test.sh | |
| - name: CLI output-truth + flag-parity (v1.167 PR-2) | |
| run: bash cli/lib/nftban/tests/cli_output_truth_v167_test.sh | |
| - name: No re-introduced orphan modules (v1.167 PR-3) | |
| run: bash cli/lib/nftban/tests/no_reintroduced_orphan_modules_v167_test.sh | |
| # v1.167 SHIP-BLOCKER guard: `nftban update --dry-run` must return rc=1 with | |
| # the 3-line hint (NOT command-not-found rc=127). Also asserts the dispatcher | |
| # sources cmd_common.sh so the 6 CLI-BUG-1 sibling error paths are reachable. | |
| - name: update --dry-run hint rc=1 + CLI-BUG-1 reachability (v1.167) | |
| run: bash cli/lib/nftban/tests/cli_update_dry_run_hint_v167_test.sh | |
| # v1.168 CLI-BUG-2 whitelist-TTL lane guards: the 4 whitelist set decls | |
| # must carry interval,timeout (render prereq) AND the DEB/RPM upgrade path | |
| # must re-activate timeout-capable sets (GetOrCreateIntervalSet reuses | |
| # existing flagless sets). The TTL-survives-FullSync invariant is a Go test | |
| # (internal/setsync) run by ci-go.yml. | |
| - name: Whitelist sets carry timeout flag (v1.168 render prereq) | |
| run: bash cli/lib/nftban/tests/whitelist_set_timeout_flag_v168_test.sh | |
| - name: Whitelist-TTL upgrade activation backstop (v1.168 deb+rpm) | |
| run: bash cli/lib/nftban/tests/whitelist_timeout_upgrade_activation_v168_test.sh | |
| # v1.169 CLI-BUG-3: `whitelist list` labels timed/session vs durable entries. | |
| - name: Whitelist list timed/session label (v1.169 CLI-BUG-3) | |
| run: bash cli/lib/nftban/tests/whitelist_list_timed_label_v169_test.sh | |
| # v1.169 CI-TEST-GAP: wire 6 substantive guards that shipped with their | |
| # lanes (v1.158–v1.163) but were never added to any workflow. Each is | |
| # hermetic (no host/root) — they only catch a future regression once run. | |
| - name: MAC posture advisory surface (v1.158) | |
| run: bash cli/lib/nftban/tests/mac_posture_v158_test.sh | |
| - name: Geoban-refresh ExecCondition skip-not-fail (v1.159) | |
| run: bash cli/lib/nftban/tests/geoban_refresh_execcondition_v159_test.sh | |
| - name: Firewall-pkg state-aware wording (v1.160) | |
| run: bash cli/lib/nftban/tests/firewall_pkg_wording_v160.sh | |
| - name: Permissions optional-module skip (v1.160) | |
| run: bash cli/lib/nftban/tests/permissions_optional_module_skip_v160.sh | |
| - name: Whitelist verify read-only (v1.163) | |
| run: bash cli/lib/nftban/tests/whitelist_verify_v163_test.sh | |
| - name: Immutable-flag health verify (v1.163) | |
| run: bash cli/lib/nftban/tests/immutable_health_verify_v163_test.sh | |
| # v1.170 BUG-STATS-IP-HISTORY: `nftban stats ip <IP>` must single-emit (no | |
| # pipefail double-`[]` arith crash for zero-record IPs) AND read compressed/ | |
| # rotated bans.log archives (zgrep); + gzip dep stays declared. | |
| - name: stats ip history pipefail + compressed-log fix (v1.170) | |
| run: bash cli/lib/nftban/tests/stats_ip_history_v170_test.sh | |
| # v1.171 §4.2/§4.3/§3.6: daemon state writes are atomic (route through | |
| # safety.SafeWriteFile); ssh_ports counter seeded. The atomic mechanism | |
| # itself is proven by Go test internal/safety/atomic_write_v171_test.go. | |
| - name: state-write atomicity call-site lock (v1.171) | |
| run: bash cli/lib/nftban/tests/state_write_atomicity_v171_test.sh | |
| # v1.172 CLI-PIPEFAIL-ARITH sweep: zero-match grep / SIGPIPE / jq-parse failures | |
| # under set -Eeuo pipefail must not double-emit a numeric ("0\n0") that crashes a | |
| # downstream [[ -eq/-gt ]] / jq tonumber. Behavioral single-emit checks + static | |
| # lint that the previously-broken arith-fed sites no longer carry the unsafe forms. | |
| - name: CLI pipefail-arith sweep (v1.172) | |
| run: bash cli/lib/nftban/tests/cli_pipefail_arith_sweep_v172_test.sh | |
| # v1.173 §4.1 session-whitelist flock (shell side): each of the 3 | |
| # cmd_firewall.sh session RMW funcs serializes its read-modify-write under | |
| # flock(9) on the shared cross-language lock /run/nftban/session_whitelist.lock | |
| # (same path the Go installer flock(2)s). Go side is covered by | |
| # internal/installer/safety/session_whitelist_flock_v173_test.go. | |
| - name: session-whitelist flock shell-side (v1.173) | |
| run: bash cli/lib/nftban/tests/session_whitelist_flock_v173_test.sh | |
| # v1.174 HEALTH-OOM-JOURNALCTL: every actual journalctl invocation on the | |
| # nftban-health.service health-check path is bounded (-n/--since), and the | |
| # unit's MemoryMax is raised 128M->256M for the validator + concurrent- | |
| # children RSS on large journals. The Go validator read is already bounded | |
| # (internal/validator/journal.go --since -15m -n 200). The stale failed- | |
| # unit classification half is covered by Go test | |
| # internal/installer/validate/systemd_payload_v174_test.go. | |
| - name: health journalctl bounded + MemoryMax 256M (v1.174) | |
| run: bash cli/lib/nftban/tests/health_journalctl_bounded_v174_test.sh | |
| # v1.174 ALERT-THROTTLE-NONFATAL: a state-write (throttle/diagnostics/ | |
| # failure-metric) permission failure must NOT fail/latch nftban-alert@*.service | |
| # (monitor root cause), but a real alert DELIVERY failure still follows the rc | |
| # contract. cli/sbin/nftban-service-alert. | |
| - name: alert bookkeeping non-fatal, delivery keeps rc (v1.174) | |
| run: bash cli/lib/nftban/tests/alert_bookkeeping_nonfatal_v174_test.sh | |
| # v1.174 update-summary messaging: surface an auto-recovered pre-existing | |
| # failed unit to the operator (transparency) + fix the misleading DEGRADED | |
| # "known transient on the exporter" wording. cli/lib/nftban/cli/cmd_update.sh. | |
| - name: update messaging recovered + DEGRADED (v1.174) | |
| run: bash cli/lib/nftban/tests/update_msg_recovered_v174_test.sh | |
| # v1.175 FHS lane: (T1) GENERIC guard — no root-owned tmpfiles entry under a | |
| # non-root declared parent (the systemd-tmpfiles exit-73 "unsafe path transition" | |
| # class — structural BUG-TMPFILES regression lock); auditors→package + | |
| # firewall-validate→ExecStartPre out of tmpfiles; ALERT-THROTTLE-FHS alerts dir; | |
| # FHS-SMELL-SIDSTATS cache.go DataDir relocation + migration. | |
| - name: FHS lane invariants — tmpfiles transition guard + relocations (v1.175) | |
| run: bash cli/lib/nftban/tests/fhs_lane_v175_test.sh | |
| # v1.176 DAEMON RELIABILITY: FSYNC-RESIDUAL durability-idiom class lock — | |
| # no NEW hand-rolled temp+rename Go state writer outside internal/safety | |
| # (route through safety.SafeWriteFile); regression-guards the F-1/F-2 fixes. | |
| - name: FSYNC-RESIDUAL durability-idiom guard (v1.176) | |
| run: bash cli/lib/nftban/tests/fsync_residual_guard_v176_test.sh | |
| # v1.177 HTTP LOG DISCOVERY: panel-aware access-log discovery helper — | |
| # DirectAdmin/cPanel/Plesk/generic/LiteSpeed globs, multi-log, dedup, | |
| # error/rotated exclusion, MAX_FILES bound, IPv6 parse, incremental offset | |
| # + rotation/copytruncate, and the legacy-miss vs new-find regression. | |
| - name: HTTP log discovery + BotScan panel paths (v1.177) | |
| run: bash cli/lib/nftban/tests/http_log_discovery_v177_test.sh | |
| - name: BotScan read-authority collector + spool-first (v1.178-A) | |
| run: bash cli/lib/nftban/tests/botscan_read_authority_v178_test.sh | |
| - name: BotScan processor deadline + rotation (v1.185 Lane B) | |
| run: bash cli/lib/nftban/tests/botscan_deadline_rotation_v185_test.sh | |
| # ===================================================================== | |
| # v1.86 B86-4: Contract Enforcement — Legacy Regression Blockers | |
| # ===================================================================== | |
| # These greps prevent reintroduction of deleted legacy constructs. | |
| # If any match, the contract has been violated and CI must fail. | |
| - name: No ModuleTruth reintroduction (B86-1 guard) | |
| run: | | |
| echo "Checking for legacy ModuleTruth references..." | |
| if grep -rn "ModuleTruth\|deriveModuleTruth\|ModuleStatus\|ModuleInfo\|module_truth" \ | |
| internal/validator/*.go \ | |
| --include="*.go" \ | |
| | grep -v "B86-1.*deleted\|B86-1.*removed\|_test.go.*deleted"; then | |
| echo "::error::Legacy ModuleTruth reference found — B86-1 contract violated" | |
| exit 1 | |
| fi | |
| echo "OK: no legacy ModuleTruth references" | |
| - name: No legacy fallback reintroduction (M84-2 guard) | |
| run: | | |
| echo "Checking for legacy fallback references..." | |
| if grep -rn "protection_state_legacy\|FORCE_LEGACY_STATE\|nftban_invariant_validator" \ | |
| cli/lib/nftban/cli/*.sh \ | |
| internal/validator/*.go \ | |
| | grep -v "deleted\|removed\|B86\|M84\|v1.84\|v1.86"; then | |
| echo "::error::Legacy fallback reference found — M84-2 contract violated" | |
| exit 1 | |
| fi | |
| echo "OK: no legacy fallback references" | |
| # ===================================================================== | |
| # SURICATA INVARIANT GATES (v1.92 — GS-001..GS-014) | |
| # Source: V192_FINAL_ARCHITECTURE_DECISION.md | |
| # ===================================================================== | |
| - name: "GS-003: Suricata ban_handler uses IPC only" | |
| run: | | |
| echo "Checking ban_handler.go for direct nft calls..." | |
| if grep -n 'exec\.Command.*"nft"' internal/suricata/ban_handler.go 2>/dev/null; then | |
| echo "::error::ban_handler.go contains direct nft command — INV-S-004 violation" | |
| exit 1 | |
| fi | |
| echo "OK: ban_handler uses IPC only" | |
| - name: "GS-004: No scope creep reintroduction" | |
| run: | | |
| echo "Checking deleted packages don't exist..." | |
| FAIL=0 | |
| for dir in internal/suricata/customrules internal/suricata/recommendations internal/suricata/scanner; do | |
| if [ -d "$dir" ]; then | |
| echo "::error::$dir exists — scope creep reintroduction" | |
| FAIL=1 | |
| fi | |
| done | |
| [ $FAIL -eq 0 ] || exit 1 | |
| echo "OK: no scope creep directories" | |
| - name: "GS-008: Inline exception disabled by default" | |
| run: | | |
| echo "Checking inline default config..." | |
| if grep -q 'INLINE_EXCEPTION_ENABLED="true"' etc/nftban/modules/inline_exceptions.conf 2>/dev/null; then | |
| echo "::error::Inline exception enabled in default config — INV-S-009 violation" | |
| exit 1 | |
| fi | |
| echo "OK: inline exception disabled by default" | |
| - name: "GS-014: Rule engine cannot trigger inline" | |
| run: | | |
| echo "Checking rule engine for inline references..." | |
| if grep -rn 'nfqueue\|inline.*drop\|InlineException' internal/suricata/policy.go 2>/dev/null; then | |
| echo "::error::Rule engine/policy references inline enforcement — INV-S-012 violation" | |
| exit 1 | |
| fi | |
| echo "OK: rule engine isolated from inline" | |
| # ==================================================================== | |
| # V108 Item 2 — +i lifecycle matrix CI gate | |
| # ==================================================================== | |
| # Verifies build/+i-lifecycle-matrix.yaml is in sync with the 4 source | |
| # surfaces that handle chattr +i lifecycle: | |
| # 1. Go SetImmutableFlags (internal/installer/validate/authority.go) | |
| # 2. RPM scriptlets (packaging/build_nftban.sh: %pretrans + %preun) | |
| # 3. DEB scriptlets (packaging/deb/preinst, postinst, prerm) | |
| # The matrix yaml is the single source of truth. Drift between yaml and | |
| # any source surface FAILs the gate. | |
| # | |
| # Closes v1.107.2-class regression where Go SetImmutableFlags extended | |
| # its protected list silently and the scriptlets fell out of sync, | |
| # producing "cpio: rename failed - No data available" on package upgrade. | |
| # ==================================================================== | |
| - name: "V108 Item 2: +i lifecycle matrix" | |
| run: | | |
| set -Eeuo pipefail | |
| echo "=== V108 Item 2 — +i lifecycle matrix gate ===" | |
| bash scripts/ci/test-immutable-lifecycle-matrix.sh scan | |
| # ==================================================================== | |
| # V108 Item 6 — install-method detection (source-install mismatch) | |
| # ==================================================================== | |
| # Validates _detect_install_type + _classify_for_pkg_mgr_update across | |
| # 9 fixtures covering rpm/deb/source/source-git/unknown/mixed/cross- | |
| # family/confirms-db. Closes the dns2-class defect where source- | |
| # installed hosts fell through to "unknown" because the detector | |
| # didn't read update-history.json first-entry "type". | |
| # ==================================================================== | |
| - name: "V108 Item 6: install-method detection" | |
| run: | | |
| set -Eeuo pipefail | |
| echo "=== V108 Item 6 — install-method detection gate ===" | |
| bash scripts/ci/test-install-method-detection.sh suite | |
| # ==================================================================== | |
| # V108 Item 3 — heredoc command-substitution safety gate | |
| # ==================================================================== | |
| # Detects unsafe shell expansion (unescaped backticks, $(...), $((...))) | |
| # in unquoted heredoc bodies in build/generator scripts. | |
| # | |
| # Defends against the v1.107.1-class defect (PR #585 fixup da14e182): | |
| # markdown backticks in unquoted RPM-spec heredoc were interpreted by | |
| # bash as command substitution at heredoc-write time, corrupting the | |
| # generated spec content. el10's rpmbuild failed; el9's tolerated. | |
| # | |
| # Scope: packaging/build_nftban.sh, build/generate-*.sh, scripts/*.sh, | |
| # scripts/ci/*.sh. Function-context heredocs (usage()) are auto- | |
| # detected and allowed to use $(...) at function-call time. | |
| # ==================================================================== | |
| - name: "V108 Item 3: heredoc command-substitution safety" | |
| run: | | |
| set -Eeuo pipefail | |
| echo "=== V108 Item 3 — heredoc-safety gate ===" | |
| bash scripts/ci/test-heredoc-safety.sh scan | |
| # ==================================================================== | |
| # V110 R-10 — Module Isolation Lint | |
| # ==================================================================== | |
| # Enforces three invariants over registered Module implementers: | |
| # A1 — Distinct ModuleName across all `func (m *Module) Name() string` | |
| # implementers (rejects accidental name reuse) | |
| # A2 — Every `eventbus.NewEvent(eventbus.EventBan, ...)` publish uses | |
| # the calling module's own ModuleName const (rejects cross- | |
| # attribution and string-literal sources) | |
| # A3 — Status().Extra cross-module key isolation: locks the current | |
| # baseline of legitimate shared keys (`mode`, `suricata_available`, | |
| # `tracked_ips`) and blocks NEW cross-module key introductions | |
| # | |
| # Schema impact: NONE. Lint is repository-static. | |
| # Origin: V110 R-10 (FM-J) per AUDIT_190_MODULE_ISOLATION/REMEDIATION_PLAN.md | |
| # Workspace anchor: V110_MOD_ISOLATION_DELTA_AUDIT.md §5 | |
| # ==================================================================== | |
| - name: "V110 R-10: module isolation lint" | |
| run: | | |
| set -Eeuo pipefail | |
| echo "=== V110 R-10 — module isolation lint ===" | |
| bash scripts/lint-module-isolation.sh |