From 997419d684d9b1dfb0cb43c94d7b95b8499452fa Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 29 Aug 2026 16:25:07 +0000 Subject: [PATCH] refactor(test): deduplicate assertStatus helper across modules Extract the duplicate assertStatus test helper function across 11 module test files into testutil.AssertStatus. Added test coverage for the helper in testutil_test.go. Co-authored-by: jackby03 <70082129+jackby03@users.noreply.github.com> --- internal/modules/auditd/module_test.go | 33 ++++------ internal/modules/containers/module_test.go | 52 ++++++---------- internal/modules/crypto/module_test.go | 60 ++++++++----------- internal/modules/firewall/module_test.go | 48 ++++++--------- internal/modules/logging/module_test.go | 35 ++++------- internal/modules/mac/module_test.go | 41 +++++-------- internal/modules/network/module_test.go | 19 +----- internal/modules/ntp/module_test.go | 35 ++++------- internal/modules/ssh/module_test.go | 17 +----- internal/modules/updates/module_test.go | 33 ++++------ internal/modules/users/module_test.go | 41 +++++-------- internal/modules/util/testutil/testutil.go | 31 ++++++++++ .../modules/util/testutil/testutil_test.go | 39 ++++++++++++ 13 files changed, 205 insertions(+), 279 deletions(-) create mode 100644 internal/modules/util/testutil/testutil_test.go diff --git a/internal/modules/auditd/module_test.go b/internal/modules/auditd/module_test.go index 9bd0b68..8b7e77a 100644 --- a/internal/modules/auditd/module_test.go +++ b/internal/modules/auditd/module_test.go @@ -61,19 +61,6 @@ func serviceDisabled() map[string]fakeResult { } } -func assertStatus(t *testing.T, findings []modules.Finding, id string, want modules.Status) { - t.Helper() - for _, f := range findings { - if f.Check.ID == id { - if f.Status != want { - t.Errorf("check %s: got status %q, want %q (current=%q)", id, f.Status, want, f.Current) - } - return - } - } - t.Errorf("check %s: not found in findings", id) -} - // ── interface ───────────────────────────────────────────────────────────────── func TestModule_ImplementsInterface(t *testing.T) { @@ -129,13 +116,13 @@ func TestAudit_DefaultRules_RuleChecksNonCompliant(t *testing.T) { ruleIDs := []string{"aud-001", "aud-002", "aud-003", "aud-004", "aud-005", "aud-006", "aud-007", "aud-008", "aud-009"} for _, id := range ruleIDs { - assertStatus(t, findings, id, modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, id, modules.StatusNonCompliant) } // conf checks still pass - assertStatus(t, findings, "aud-010", modules.StatusCompliant) - assertStatus(t, findings, "aud-011", modules.StatusCompliant) - assertStatus(t, findings, "aud-012", modules.StatusCompliant) - assertStatus(t, findings, "aud-013", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "aud-010", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "aud-011", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "aud-012", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "aud-013", modules.StatusCompliant) } // ── audit: non-compliant conf ───────────────────────────────────────────────── @@ -153,9 +140,9 @@ func TestAudit_DefaultConf_ConfChecksNonCompliant(t *testing.T) { } // Default conf: max_log_file=5 (< 8), action=ROTATE (ok), space_left=SYSLOG (ok) - assertStatus(t, findings, "aud-010", modules.StatusNonCompliant) - assertStatus(t, findings, "aud-011", modules.StatusCompliant) // ROTATE is acceptable - assertStatus(t, findings, "aud-012", modules.StatusCompliant) // SYSLOG is acceptable + testutil.AssertStatus(t, findings, "aud-010", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "aud-011", modules.StatusCompliant) // ROTATE is acceptable + testutil.AssertStatus(t, findings, "aud-012", modules.StatusCompliant) // SYSLOG is acceptable } // ── audit: service disabled ─────────────────────────────────────────────────── @@ -171,7 +158,7 @@ func TestAudit_ServiceDisabled_AUD013NonCompliant(t *testing.T) { if err != nil { t.Fatalf("Audit() error: %v", err) } - assertStatus(t, findings, "aud-013", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "aud-013", modules.StatusNonCompliant) } // ── audit: missing rules dir ────────────────────────────────────────────────── @@ -188,7 +175,7 @@ func TestAudit_MissingRulesDir_AllRuleChecksNonCompliant(t *testing.T) { t.Fatalf("Audit() error: %v", err) } for _, id := range []string{"aud-001", "aud-009"} { - assertStatus(t, findings, id, modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, id, modules.StatusNonCompliant) } } diff --git a/internal/modules/containers/module_test.go b/internal/modules/containers/module_test.go index 3e9930f..782bf2c 100644 --- a/internal/modules/containers/module_test.go +++ b/internal/modules/containers/module_test.go @@ -56,20 +56,6 @@ func secOptKey() string { return "docker info --format " + containers.SecurityOptsFmt } -// assertStatus fails the test when the named check does not match the expected status. -func assertStatus(t *testing.T, findings []modules.Finding, id string, want modules.Status) { - t.Helper() - for _, f := range findings { - if f.Check.ID == id { - if f.Status != want { - t.Errorf("check %s: got status %q, want %q (detail: %s)", id, f.Status, want, f.Detail) - } - return - } - } - t.Errorf("check %s: not found in %d findings", id, len(findings)) -} - // emptyAuditDir creates a temporary directory with no .rules files. func emptyAuditDir(t *testing.T) string { t.Helper() @@ -157,7 +143,7 @@ func TestAudit_RootlessCompliant(t *testing.T) { if err != nil { t.Fatalf("Audit(): %v", err) } - assertStatus(t, findings, "cnt-001", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "cnt-001", modules.StatusCompliant) } func TestAudit_RootlessNonCompliant(t *testing.T) { @@ -174,7 +160,7 @@ func TestAudit_RootlessNonCompliant(t *testing.T) { if err != nil { t.Fatalf("Audit(): %v", err) } - assertStatus(t, findings, "cnt-001", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "cnt-001", modules.StatusNonCompliant) } // ---------------------------------------------------------------------------- @@ -195,7 +181,7 @@ func TestAudit_ICCCompliant(t *testing.T) { if err != nil { t.Fatalf("Audit(): %v", err) } - assertStatus(t, findings, "cnt-002", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "cnt-002", modules.StatusCompliant) } func TestAudit_ICCNonCompliant(t *testing.T) { @@ -212,7 +198,7 @@ func TestAudit_ICCNonCompliant(t *testing.T) { if err != nil { t.Fatalf("Audit(): %v", err) } - assertStatus(t, findings, "cnt-002", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "cnt-002", modules.StatusNonCompliant) } // ---------------------------------------------------------------------------- @@ -233,7 +219,7 @@ func TestAudit_UsernsRemapCompliant(t *testing.T) { if err != nil { t.Fatalf("Audit(): %v", err) } - assertStatus(t, findings, "cnt-003", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "cnt-003", modules.StatusCompliant) } func TestAudit_UsernsRemapNonCompliant(t *testing.T) { @@ -250,7 +236,7 @@ func TestAudit_UsernsRemapNonCompliant(t *testing.T) { if err != nil { t.Fatalf("Audit(): %v", err) } - assertStatus(t, findings, "cnt-003", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "cnt-003", modules.StatusNonCompliant) } // ---------------------------------------------------------------------------- @@ -272,7 +258,7 @@ func TestAudit_TLSSkippedNoTCPHost(t *testing.T) { if err != nil { t.Fatalf("Audit(): %v", err) } - assertStatus(t, findings, "cnt-004", modules.StatusSkipped) + testutil.AssertStatus(t, findings, "cnt-004", modules.StatusSkipped) } func TestAudit_TLSNonCompliantOnTCPHost(t *testing.T) { @@ -290,7 +276,7 @@ func TestAudit_TLSNonCompliantOnTCPHost(t *testing.T) { if err != nil { t.Fatalf("Audit(): %v", err) } - assertStatus(t, findings, "cnt-004", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "cnt-004", modules.StatusNonCompliant) } // ---------------------------------------------------------------------------- @@ -311,8 +297,8 @@ func TestAudit_SeccompAndMACNonCompliant(t *testing.T) { if err != nil { t.Fatalf("Audit(): %v", err) } - assertStatus(t, findings, "cnt-005", modules.StatusNonCompliant) - assertStatus(t, findings, "cnt-006", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "cnt-005", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "cnt-006", modules.StatusNonCompliant) } func TestAudit_SeccompAndMACCompliant(t *testing.T) { @@ -329,8 +315,8 @@ func TestAudit_SeccompAndMACCompliant(t *testing.T) { if err != nil { t.Fatalf("Audit(): %v", err) } - assertStatus(t, findings, "cnt-005", modules.StatusCompliant) - assertStatus(t, findings, "cnt-006", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "cnt-005", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "cnt-006", modules.StatusCompliant) } // ---------------------------------------------------------------------------- @@ -353,7 +339,7 @@ func TestAudit_PrivilegedContainerFound(t *testing.T) { if err != nil { t.Fatalf("Audit(): %v", err) } - assertStatus(t, findings, "cnt-007", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "cnt-007", modules.StatusNonCompliant) } func TestAudit_NoPrivilegedContainers(t *testing.T) { @@ -372,7 +358,7 @@ func TestAudit_NoPrivilegedContainers(t *testing.T) { if err != nil { t.Fatalf("Audit(): %v", err) } - assertStatus(t, findings, "cnt-007", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "cnt-007", modules.StatusCompliant) } // ---------------------------------------------------------------------------- @@ -397,7 +383,7 @@ func TestAudit_SocketMountFound(t *testing.T) { if err != nil { t.Fatalf("Audit(): %v", err) } - assertStatus(t, findings, "cnt-008", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "cnt-008", modules.StatusNonCompliant) } func TestAudit_NoSocketMount(t *testing.T) { @@ -418,7 +404,7 @@ func TestAudit_NoSocketMount(t *testing.T) { if err != nil { t.Fatalf("Audit(): %v", err) } - assertStatus(t, findings, "cnt-008", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "cnt-008", modules.StatusCompliant) } // ---------------------------------------------------------------------------- @@ -439,7 +425,7 @@ func TestAudit_ImageScanningAlwaysManual(t *testing.T) { if err != nil { t.Fatalf("Audit(): %v", err) } - assertStatus(t, findings, "cnt-009", modules.StatusManual) + testutil.AssertStatus(t, findings, "cnt-009", modules.StatusManual) } // ---------------------------------------------------------------------------- @@ -460,7 +446,7 @@ func TestAudit_AuditRulePresent(t *testing.T) { if err != nil { t.Fatalf("Audit(): %v", err) } - assertStatus(t, findings, "cnt-010", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "cnt-010", modules.StatusCompliant) } func TestAudit_AuditRuleMissing(t *testing.T) { @@ -477,5 +463,5 @@ func TestAudit_AuditRuleMissing(t *testing.T) { if err != nil { t.Fatalf("Audit(): %v", err) } - assertStatus(t, findings, "cnt-010", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "cnt-010", modules.StatusNonCompliant) } diff --git a/internal/modules/crypto/module_test.go b/internal/modules/crypto/module_test.go index 2e841d3..d92d6a3 100644 --- a/internal/modules/crypto/module_test.go +++ b/internal/modules/crypto/module_test.go @@ -16,6 +16,7 @@ package crypto_test import ( "context" + "github.com/hardbox-io/hardbox/internal/modules/util/testutil" "os" "path/filepath" "runtime" @@ -53,12 +54,12 @@ func TestAudit_RHEL_Compliant(t *testing.T) { if err != nil { t.Fatalf("Audit(): %v", err) } - assertStatus(t, findings, "cry-001", modules.StatusCompliant) - assertStatus(t, findings, "cry-002", modules.StatusCompliant) - assertStatus(t, findings, "cry-003", modules.StatusCompliant) - assertStatus(t, findings, "cry-004", modules.StatusCompliant) - assertStatus(t, findings, "cry-005", modules.StatusCompliant) - assertStatus(t, findings, "cry-006", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "cry-001", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "cry-002", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "cry-003", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "cry-004", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "cry-005", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "cry-006", modules.StatusCompliant) } func TestAudit_RHEL_Legacy(t *testing.T) { @@ -75,12 +76,12 @@ func TestAudit_RHEL_Legacy(t *testing.T) { if err != nil { t.Fatalf("Audit(): %v", err) } - assertStatus(t, findings, "cry-001", modules.StatusNonCompliant) - assertStatus(t, findings, "cry-002", modules.StatusNonCompliant) - assertStatus(t, findings, "cry-003", modules.StatusNonCompliant) - assertStatus(t, findings, "cry-004", modules.StatusNonCompliant) - assertStatus(t, findings, "cry-005", modules.StatusManual) - assertStatus(t, findings, "cry-006", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "cry-001", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "cry-002", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "cry-003", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "cry-004", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "cry-005", modules.StatusManual) + testutil.AssertStatus(t, findings, "cry-006", modules.StatusNonCompliant) } func TestAudit_Debian_Hardened(t *testing.T) { @@ -97,12 +98,12 @@ func TestAudit_Debian_Hardened(t *testing.T) { if err != nil { t.Fatalf("Audit(): %v", err) } - assertStatus(t, findings, "cry-001", modules.StatusCompliant) - assertStatus(t, findings, "cry-002", modules.StatusCompliant) - assertStatus(t, findings, "cry-003", modules.StatusCompliant) - assertStatus(t, findings, "cry-004", modules.StatusCompliant) - assertStatus(t, findings, "cry-005", modules.StatusManual) - assertStatus(t, findings, "cry-006", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "cry-001", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "cry-002", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "cry-003", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "cry-004", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "cry-005", modules.StatusManual) + testutil.AssertStatus(t, findings, "cry-006", modules.StatusCompliant) } func TestAudit_Debian_Weak(t *testing.T) { @@ -119,11 +120,11 @@ func TestAudit_Debian_Weak(t *testing.T) { if err != nil { t.Fatalf("Audit(): %v", err) } - assertStatus(t, findings, "cry-001", modules.StatusNonCompliant) - assertStatus(t, findings, "cry-002", modules.StatusNonCompliant) - assertStatus(t, findings, "cry-003", modules.StatusNonCompliant) - assertStatus(t, findings, "cry-004", modules.StatusNonCompliant) - assertStatus(t, findings, "cry-006", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "cry-001", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "cry-002", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "cry-003", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "cry-004", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "cry-006", modules.StatusNonCompliant) } func TestPlan_NoChanges(t *testing.T) { @@ -174,16 +175,3 @@ func TestReadStringIfExists_PermissionDenied(t *testing.T) { func td(name string) string { return filepath.Join("testdata", name) } - -func assertStatus(t *testing.T, findings []modules.Finding, id string, want modules.Status) { - t.Helper() - for _, f := range findings { - if f.Check.ID == id { - if f.Status != want { - t.Fatalf("%s: got %s want %s (current=%q detail=%q)", id, f.Status, want, f.Current, f.Detail) - } - return - } - } - t.Fatalf("check %s not found", id) -} diff --git a/internal/modules/firewall/module_test.go b/internal/modules/firewall/module_test.go index 688ef78..d8b7213 100644 --- a/internal/modules/firewall/module_test.go +++ b/internal/modules/firewall/module_test.go @@ -17,6 +17,7 @@ package firewall_test import ( "context" "fmt" + "github.com/hardbox-io/hardbox/internal/modules/util/testutil" "os" "path/filepath" "strings" @@ -54,12 +55,12 @@ func TestAudit_UFWCompliant(t *testing.T) { if len(findings) != 6 { t.Fatalf("expected 6 findings, got %d", len(findings)) } - assertStatus(t, findings, "fw-001", modules.StatusCompliant) - assertStatus(t, findings, "fw-002", modules.StatusCompliant) - assertStatus(t, findings, "fw-003", modules.StatusCompliant) - assertStatus(t, findings, "fw-004", modules.StatusCompliant) - assertStatus(t, findings, "fw-005", modules.StatusCompliant) - assertStatus(t, findings, "fw-006", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "fw-001", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "fw-002", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "fw-003", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "fw-004", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "fw-005", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "fw-006", modules.StatusCompliant) } func TestAudit_UFWSensitivePortOpen(t *testing.T) { @@ -72,8 +73,8 @@ func TestAudit_UFWSensitivePortOpen(t *testing.T) { if err != nil { t.Fatalf("Audit(): %v", err) } - assertStatus(t, findings, "fw-005", modules.StatusNonCompliant) - assertStatus(t, findings, "fw-006", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "fw-005", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "fw-006", modules.StatusNonCompliant) } func TestAudit_FirewalldInboundNotDrop(t *testing.T) { @@ -87,12 +88,12 @@ func TestAudit_FirewalldInboundNotDrop(t *testing.T) { if err != nil { t.Fatalf("Audit(): %v", err) } - assertStatus(t, findings, "fw-001", modules.StatusCompliant) - assertStatus(t, findings, "fw-002", modules.StatusNonCompliant) - assertStatus(t, findings, "fw-003", modules.StatusSkipped) - assertStatus(t, findings, "fw-004", modules.StatusCompliant) - assertStatus(t, findings, "fw-005", modules.StatusCompliant) - assertStatus(t, findings, "fw-006", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "fw-001", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "fw-002", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "fw-003", modules.StatusSkipped) + testutil.AssertStatus(t, findings, "fw-004", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "fw-005", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "fw-006", modules.StatusCompliant) } func TestAudit_NftablesCompliant(t *testing.T) { @@ -122,7 +123,7 @@ table ip6 filter { t.Fatalf("Audit(): %v", err) } for _, id := range []string{"fw-001", "fw-002", "fw-003", "fw-004", "fw-005", "fw-006"} { - assertStatus(t, findings, id, modules.StatusCompliant) + testutil.AssertStatus(t, findings, id, modules.StatusCompliant) } } @@ -136,7 +137,7 @@ func TestAudit_IPv6DisabledSkipsFW006(t *testing.T) { if err != nil { t.Fatalf("Audit(): %v", err) } - assertStatus(t, findings, "fw-006", modules.StatusSkipped) + testutil.AssertStatus(t, findings, "fw-006", modules.StatusSkipped) } func TestAudit_NoBackendDetectedReturnsErrors(t *testing.T) { @@ -199,20 +200,6 @@ func TestNftHasIPv6Rules(t *testing.T) { } } -func assertStatus(t *testing.T, findings []modules.Finding, id string, want modules.Status) { - t.Helper() - for _, f := range findings { - if f.Check.ID != id { - continue - } - if f.Status != want { - t.Fatalf("check %s: got %s want %s (current=%q detail=%q)", id, f.Status, want, f.Current, f.Detail) - } - return - } - t.Fatalf("check %s not found", id) -} - type fakeResult struct { out string err bool @@ -244,4 +231,3 @@ func writeIPv6DisableFile(t *testing.T, content string) string { func alwaysTrue(string) bool { return true } func alwaysFalse(string) bool { return false } - diff --git a/internal/modules/logging/module_test.go b/internal/modules/logging/module_test.go index 26dfc9f..712c7b1 100644 --- a/internal/modules/logging/module_test.go +++ b/internal/modules/logging/module_test.go @@ -63,19 +63,6 @@ func noSyslogActive() map[string]fakeResult { } } -func assertStatus(t *testing.T, findings []modules.Finding, id string, want modules.Status) { - t.Helper() - for _, f := range findings { - if f.Check.ID == id { - if f.Status != want { - t.Errorf("check %s: got status %q, want %q (current=%q)", id, f.Status, want, f.Current) - } - return - } - } - t.Errorf("check %s: not found in findings", id) -} - // noLogrotate returns a path that doesn't exist. func noLogrotate() string { return filepath.Join("testdata", "nonexistent_logrotate.conf") } @@ -161,12 +148,12 @@ func TestAudit_AllCompliant(t *testing.T) { t.Fatalf("expected 7 findings, got %d", len(findings)) } - assertStatus(t, findings, "log-001", modules.StatusCompliant) - assertStatus(t, findings, "log-002", modules.StatusCompliant) - assertStatus(t, findings, "log-004", modules.StatusCompliant) - assertStatus(t, findings, "log-005", modules.StatusCompliant) - assertStatus(t, findings, "log-006", modules.StatusCompliant) - assertStatus(t, findings, "log-007", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "log-001", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "log-002", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "log-004", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "log-005", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "log-006", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "log-007", modules.StatusCompliant) } // ── audit: no syslog service ────────────────────────────────────────────────── @@ -186,7 +173,7 @@ func TestAudit_NoSyslogService_LOG001NonCompliant(t *testing.T) { if err != nil { t.Fatalf("Audit() error: %v", err) } - assertStatus(t, findings, "log-001", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "log-001", modules.StatusNonCompliant) } // ── audit: no remote target ─────────────────────────────────────────────────── @@ -206,7 +193,7 @@ func TestAudit_LocalOnlyRsyslog_LOG002NonCompliant(t *testing.T) { if err != nil { t.Fatalf("Audit() error: %v", err) } - assertStatus(t, findings, "log-002", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "log-002", modules.StatusNonCompliant) } // ── audit: journald not persistent ─────────────────────────────────────────── @@ -226,8 +213,8 @@ func TestAudit_JournaldDefault_LOG004LOG005NonCompliant(t *testing.T) { if err != nil { t.Fatalf("Audit() error: %v", err) } - assertStatus(t, findings, "log-004", modules.StatusNonCompliant) - assertStatus(t, findings, "log-005", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "log-004", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "log-005", modules.StatusNonCompliant) } // ── audit: no logrotate ─────────────────────────────────────────────────────── @@ -247,7 +234,7 @@ func TestAudit_NoLogrotate_LOG006NonCompliant(t *testing.T) { if err != nil { t.Fatalf("Audit() error: %v", err) } - assertStatus(t, findings, "log-006", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "log-006", modules.StatusNonCompliant) } // ── plan: writes journald keys when non-compliant ───────────────────────────── diff --git a/internal/modules/mac/module_test.go b/internal/modules/mac/module_test.go index 41e1161..dc6a804 100644 --- a/internal/modules/mac/module_test.go +++ b/internal/modules/mac/module_test.go @@ -17,6 +17,7 @@ package mac_test import ( "context" "fmt" + "github.com/hardbox-io/hardbox/internal/modules/util/testutil" "os" "path/filepath" "strings" @@ -77,11 +78,11 @@ func TestAudit_AppArmor_Compliant(t *testing.T) { if err != nil { t.Fatalf("Audit: %v", err) } - assertStatus(t, findings, "mac-001", modules.StatusCompliant) - assertStatus(t, findings, "mac-002", modules.StatusCompliant) - assertStatus(t, findings, "mac-003", modules.StatusCompliant) - assertStatus(t, findings, "mac-004", modules.StatusCompliant) - assertStatus(t, findings, "mac-005", modules.StatusSkipped) + testutil.AssertStatus(t, findings, "mac-001", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "mac-002", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "mac-003", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "mac-004", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "mac-005", modules.StatusSkipped) } func TestAudit_SELinux_Compliant(t *testing.T) { @@ -103,11 +104,11 @@ func TestAudit_SELinux_Compliant(t *testing.T) { if err != nil { t.Fatalf("Audit: %v", err) } - assertStatus(t, findings, "mac-001", modules.StatusCompliant) - assertStatus(t, findings, "mac-002", modules.StatusCompliant) - assertStatus(t, findings, "mac-003", modules.StatusCompliant) - assertStatus(t, findings, "mac-004", modules.StatusSkipped) - assertStatus(t, findings, "mac-005", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "mac-001", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "mac-002", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "mac-003", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "mac-004", modules.StatusSkipped) + testutil.AssertStatus(t, findings, "mac-005", modules.StatusCompliant) } func TestAudit_AppArmor_NonCompliantUnconfined(t *testing.T) { @@ -128,9 +129,9 @@ func TestAudit_AppArmor_NonCompliantUnconfined(t *testing.T) { if err != nil { t.Fatalf("Audit: %v", err) } - assertStatus(t, findings, "mac-002", modules.StatusNonCompliant) - assertStatus(t, findings, "mac-003", modules.StatusNonCompliant) - assertStatus(t, findings, "mac-004", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "mac-002", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "mac-003", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "mac-004", modules.StatusNonCompliant) } func TestPlan_AuditOnly(t *testing.T) { @@ -155,17 +156,3 @@ func TestPlan_AuditOnly(t *testing.T) { t.Fatalf("Plan returned %d changes, want 0", len(changes)) } } - -func assertStatus(t *testing.T, findings []modules.Finding, id string, want modules.Status) { - t.Helper() - for _, f := range findings { - if f.Check.ID == id { - if f.Status != want { - t.Fatalf("%s: got %s, want %s", id, f.Status, want) - } - return - } - } - t.Fatalf("check %s not found", id) -} - diff --git a/internal/modules/network/module_test.go b/internal/modules/network/module_test.go index 2f27670..b08e596 100644 --- a/internal/modules/network/module_test.go +++ b/internal/modules/network/module_test.go @@ -16,6 +16,7 @@ package network_test import ( "context" + "github.com/hardbox-io/hardbox/internal/modules/util/testutil" "os" "path/filepath" "strings" @@ -59,7 +60,7 @@ func TestAudit_CompliantScenario(t *testing.T) { } for _, id := range []string{"net-001", "net-002", "net-003", "net-004", "net-005", "net-006", "net-007", "net-008"} { - assertStatus(t, findings, id, modules.StatusCompliant) + testutil.AssertStatus(t, findings, id, modules.StatusCompliant) } } @@ -120,7 +121,7 @@ func TestAudit_DetectsNetrcInHome(t *testing.T) { if err != nil { t.Fatalf("Audit(): %v", err) } - assertStatus(t, findings, "net-008", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "net-008", modules.StatusNonCompliant) } func buildFixture(t *testing.T, root string) network.TestOptions { @@ -182,17 +183,3 @@ func buildFixture(t *testing.T, root string) network.TestOptions { PasswdPath: passwdPath, } } - -func assertStatus(t *testing.T, findings []modules.Finding, id string, want modules.Status) { - t.Helper() - for _, f := range findings { - if f.Check.ID == id { - if f.Status != want { - t.Fatalf("%s: got %s, want %s", id, f.Status, want) - } - return - } - } - t.Fatalf("check %s not found", id) -} - diff --git a/internal/modules/ntp/module_test.go b/internal/modules/ntp/module_test.go index e6a395b..92275cc 100644 --- a/internal/modules/ntp/module_test.go +++ b/internal/modules/ntp/module_test.go @@ -17,6 +17,7 @@ package ntp_test import ( "context" "fmt" + "github.com/hardbox-io/hardbox/internal/modules/util/testutil" "os" "path/filepath" "strings" @@ -81,11 +82,11 @@ func TestAudit_NonCompliantScenarios(t *testing.T) { t.Fatalf("Audit(): unexpected error: %v", err) } - assertStatus(t, findings, "ntp-001", modules.StatusCompliant) - assertStatus(t, findings, "ntp-002", modules.StatusNonCompliant) - assertStatus(t, findings, "ntp-003", modules.StatusNonCompliant) - assertStatus(t, findings, "ntp-004", modules.StatusNonCompliant) - assertStatus(t, findings, "ntp-005", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "ntp-001", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "ntp-002", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "ntp-003", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "ntp-004", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "ntp-005", modules.StatusNonCompliant) } func TestAudit_SkipsChronyDirectivesWhenChronyNotInstalled(t *testing.T) { @@ -104,11 +105,11 @@ func TestAudit_SkipsChronyDirectivesWhenChronyNotInstalled(t *testing.T) { t.Fatalf("Audit(): unexpected error: %v", err) } - assertStatus(t, findings, "ntp-001", modules.StatusNonCompliant) - assertStatus(t, findings, "ntp-002", modules.StatusNonCompliant) - assertStatus(t, findings, "ntp-003", modules.StatusSkipped) - assertStatus(t, findings, "ntp-004", modules.StatusSkipped) - assertStatus(t, findings, "ntp-005", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "ntp-001", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "ntp-002", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "ntp-003", modules.StatusSkipped) + testutil.AssertStatus(t, findings, "ntp-004", modules.StatusSkipped) + testutil.AssertStatus(t, findings, "ntp-005", modules.StatusCompliant) } func TestPlan_UpdatesChronyAndReverts(t *testing.T) { @@ -183,19 +184,6 @@ func TestPlan_NoChangesWhenCompliant(t *testing.T) { } } -func assertStatus(t *testing.T, findings []modules.Finding, id string, want modules.Status) { - t.Helper() - for _, f := range findings { - if f.Check.ID == id { - if f.Status != want { - t.Fatalf("check %s: got %s, want %s", id, f.Status, want) - } - return - } - } - t.Fatalf("check %s not found", id) -} - type fakeResult struct { out string err bool @@ -214,4 +202,3 @@ func fakeRunner(results map[string]fakeResult) func(context.Context, string, ... return res.out, nil } } - diff --git a/internal/modules/ssh/module_test.go b/internal/modules/ssh/module_test.go index 90863b5..126c4fc 100644 --- a/internal/modules/ssh/module_test.go +++ b/internal/modules/ssh/module_test.go @@ -16,6 +16,7 @@ package ssh_test import ( "context" + "github.com/hardbox-io/hardbox/internal/modules/util/testutil" "os" "path/filepath" "runtime" @@ -105,19 +106,6 @@ func runAuditOnConfig(t *testing.T, configContent string) []modules.Finding { return findings } -func assertStatus(t *testing.T, findings []modules.Finding, id string, want modules.Status) { - t.Helper() - for _, f := range findings { - if f.Check.ID == id { - if f.Status != want { - t.Errorf("check %s: got %s, want %s (current=%q)", id, f.Status, want, f.Current) - } - return - } - } - t.Errorf("check %s: not found in findings", id) -} - func TestAudit_AllCompliant(t *testing.T) { findings := runAuditOnConfig(t, compliantConfig) if len(findings) != 17 { @@ -364,7 +352,7 @@ func TestAudit_TableDriven(t *testing.T) { for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { findings := runAuditOnConfig(t, tc.config) - assertStatus(t, findings, tc.checkID, tc.want) + testutil.AssertStatus(t, findings, tc.checkID, tc.want) }) } } @@ -509,4 +497,3 @@ func assertFileMode(t *testing.T, path string, want os.FileMode) { t.Errorf("file mode: got %04o, want %04o", got, want) } } - diff --git a/internal/modules/updates/module_test.go b/internal/modules/updates/module_test.go index 95ece73..e24f9b3 100644 --- a/internal/modules/updates/module_test.go +++ b/internal/modules/updates/module_test.go @@ -16,6 +16,7 @@ package updates_test import ( "context" + "github.com/hardbox-io/hardbox/internal/modules/util/testutil" "os" "path/filepath" "testing" @@ -48,11 +49,11 @@ func TestAudit_Debian_Compliant(t *testing.T) { t.Fatalf("Audit(): %v", err) } - assertStatus(t, findings, "upd-001", modules.StatusCompliant) - assertStatus(t, findings, "upd-002", modules.StatusCompliant) - assertStatus(t, findings, "upd-003", modules.StatusCompliant) - assertStatus(t, findings, "upd-004", modules.StatusCompliant) - assertStatus(t, findings, "upd-005", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "upd-001", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "upd-002", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "upd-003", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "upd-004", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "upd-005", modules.StatusCompliant) } func TestAudit_RHEL_Compliant(t *testing.T) { @@ -78,9 +79,9 @@ func TestAudit_RHEL_Compliant(t *testing.T) { t.Fatalf("Audit(): %v", err) } - assertStatus(t, findings, "upd-002", modules.StatusCompliant) - assertStatus(t, findings, "upd-003", modules.StatusCompliant) - assertStatus(t, findings, "upd-004", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "upd-002", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "upd-003", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "upd-004", modules.StatusCompliant) } func TestAudit_Debian_MissingGPG_NonCompliant(t *testing.T) { @@ -93,7 +94,7 @@ func TestAudit_Debian_MissingGPG_NonCompliant(t *testing.T) { t.Fatalf("Audit(): %v", err) } - assertStatus(t, findings, "upd-001", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "upd-001", modules.StatusNonCompliant) } func TestPlan_AuditOnly(t *testing.T) { @@ -158,17 +159,3 @@ func buildDebianFixture(t *testing.T, root string, withKey bool) updates.TestOpt USRShareKeyrings: keyringsDir, } } - -func assertStatus(t *testing.T, findings []modules.Finding, id string, want modules.Status) { - t.Helper() - for _, f := range findings { - if f.Check.ID == id { - if f.Status != want { - t.Fatalf("check %s status = %s, want %s", id, f.Status, want) - } - return - } - } - t.Fatalf("check %s not found", id) -} - diff --git a/internal/modules/users/module_test.go b/internal/modules/users/module_test.go index 8bd1694..251c998 100644 --- a/internal/modules/users/module_test.go +++ b/internal/modules/users/module_test.go @@ -16,6 +16,7 @@ package users_test import ( "context" + "github.com/hardbox-io/hardbox/internal/modules/util/testutil" "os" "path/filepath" "strings" @@ -33,19 +34,6 @@ func tdPath(elem ...string) string { func noSudoersDir() string { return tdPath("nonexistent_sudoers_d") } -func assertStatus(t *testing.T, findings []modules.Finding, id string, want modules.Status) { - t.Helper() - for _, f := range findings { - if f.Check.ID == id { - if f.Status != want { - t.Errorf("check %s: got %q, want %q (current=%q)", id, f.Status, want, f.Current) - } - return - } - } - t.Errorf("check %s: not found in findings", id) -} - func newHardenedModule() *users.Module { return users.NewModuleForTest( nil, @@ -140,10 +128,10 @@ func TestAudit_DefaultLoginDefs_NonCompliant(t *testing.T) { if err != nil { t.Fatalf("Audit(): %v", err) } - assertStatus(t, findings, "usr-001", modules.StatusNonCompliant) // 99999 > 90 - assertStatus(t, findings, "usr-002", modules.StatusNonCompliant) // 0 < 1 - assertStatus(t, findings, "usr-004", modules.StatusNonCompliant) // not set - assertStatus(t, findings, "usr-015", modules.StatusNonCompliant) // 022 not ≥ 027 bits + testutil.AssertStatus(t, findings, "usr-001", modules.StatusNonCompliant) // 99999 > 90 + testutil.AssertStatus(t, findings, "usr-002", modules.StatusNonCompliant) // 0 < 1 + testutil.AssertStatus(t, findings, "usr-004", modules.StatusNonCompliant) // not set + testutil.AssertStatus(t, findings, "usr-015", modules.StatusNonCompliant) // 022 not ≥ 027 bits } // ── audit: dirty passwd ─────────────────────────────────────────────────────── @@ -162,8 +150,8 @@ func TestAudit_DirtyPasswd_NonCompliant(t *testing.T) { if err != nil { t.Fatalf("Audit(): %v", err) } - assertStatus(t, findings, "usr-010", modules.StatusNonCompliant) // toor has UID 0 - assertStatus(t, findings, "usr-011", modules.StatusNonCompliant) // daemon has /bin/bash + testutil.AssertStatus(t, findings, "usr-010", modules.StatusNonCompliant) // toor has UID 0 + testutil.AssertStatus(t, findings, "usr-011", modules.StatusNonCompliant) // daemon has /bin/bash } // ── audit: nopasswd sudoers ─────────────────────────────────────────────────── @@ -182,7 +170,7 @@ func TestAudit_NopasswdSudoers_NonCompliant(t *testing.T) { if err != nil { t.Fatalf("Audit(): %v", err) } - assertStatus(t, findings, "usr-013", modules.StatusNonCompliant) + testutil.AssertStatus(t, findings, "usr-013", modules.StatusNonCompliant) } // ── audit: default PAM ──────────────────────────────────────────────────────── @@ -201,11 +189,11 @@ func TestAudit_DefaultPAM_NonCompliant(t *testing.T) { if err != nil { t.Fatalf("Audit(): %v", err) } - assertStatus(t, findings, "usr-005", modules.StatusNonCompliant) // no pwquality - assertStatus(t, findings, "usr-006", modules.StatusNonCompliant) // no history - assertStatus(t, findings, "usr-007", modules.StatusNonCompliant) // no faillock - assertStatus(t, findings, "usr-008", modules.StatusNonCompliant) // no unlock_time - assertStatus(t, findings, "usr-009", modules.StatusNonCompliant) // no even_deny_root + testutil.AssertStatus(t, findings, "usr-005", modules.StatusNonCompliant) // no pwquality + testutil.AssertStatus(t, findings, "usr-006", modules.StatusNonCompliant) // no history + testutil.AssertStatus(t, findings, "usr-007", modules.StatusNonCompliant) // no faillock + testutil.AssertStatus(t, findings, "usr-008", modules.StatusNonCompliant) // no unlock_time + testutil.AssertStatus(t, findings, "usr-009", modules.StatusNonCompliant) // no even_deny_root } // ── audit: default useradd ──────────────────────────────────────────────────── @@ -224,7 +212,7 @@ func TestAudit_DefaultUseradd_NonCompliant(t *testing.T) { if err != nil { t.Fatalf("Audit(): %v", err) } - assertStatus(t, findings, "usr-017", modules.StatusNonCompliant) // INACTIVE=-1 + testutil.AssertStatus(t, findings, "usr-017", modules.StatusNonCompliant) // INACTIVE=-1 } // ── plan: fixes login.defs ──────────────────────────────────────────────────── @@ -315,4 +303,3 @@ func TestPlan_AlreadyCompliant_NoChanges(t *testing.T) { t.Errorf("expected 0 changes when already compliant, got %d", len(changes)) } } - diff --git a/internal/modules/util/testutil/testutil.go b/internal/modules/util/testutil/testutil.go index bd647e0..c02464a 100644 --- a/internal/modules/util/testutil/testutil.go +++ b/internal/modules/util/testutil/testutil.go @@ -1,8 +1,25 @@ +// Copyright (C) 2024 Jack (jackby03) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + package testutil import ( "path/filepath" "testing" + + "github.com/hardbox-io/hardbox/internal/modules" ) // TestdataPath returns the relative path to a file or directory in testdata/. @@ -20,3 +37,17 @@ func TestdataAbsPath(t *testing.T, name string) string { } return p } + +// AssertStatus verifies that the finding with the given check ID matches the expected status. +func AssertStatus(t *testing.T, findings []modules.Finding, id string, want modules.Status) { + t.Helper() + for _, f := range findings { + if f.Check.ID == id { + if f.Status != want { + t.Fatalf("check %s: got status %s, want %s (current=%q detail=%q)", id, f.Status, want, f.Current, f.Detail) + } + return + } + } + t.Fatalf("check %s not found in findings", id) +} diff --git a/internal/modules/util/testutil/testutil_test.go b/internal/modules/util/testutil/testutil_test.go new file mode 100644 index 0000000..62732ea --- /dev/null +++ b/internal/modules/util/testutil/testutil_test.go @@ -0,0 +1,39 @@ +// Copyright (C) 2024 Jack (jackby03) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package testutil_test + +import ( + "testing" + + "github.com/hardbox-io/hardbox/internal/modules" + "github.com/hardbox-io/hardbox/internal/modules/util/testutil" +) + +func TestAssertStatus_Success(t *testing.T) { + findings := []modules.Finding{ + { + Check: modules.Check{ID: "test-001"}, + Status: modules.StatusCompliant, + }, + { + Check: modules.Check{ID: "test-002"}, + Status: modules.StatusNonCompliant, + }, + } + + testutil.AssertStatus(t, findings, "test-001", modules.StatusCompliant) + testutil.AssertStatus(t, findings, "test-002", modules.StatusNonCompliant) +}