Skip to content

deps(actions): bump actions/setup-node from 4.4.0 to 6.4.0 #838

deps(actions): bump actions/setup-node from 4.4.0 to 6.4.0

deps(actions): bump actions/setup-node from 4.4.0 to 6.4.0 #838

# =============================================================================
# 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
# =============================================================================
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"
- 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)"
# =====================================================================
# 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 + man page match registry
- 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.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"