Skip to content

Commit f6751fa

Browse files
committed
test(presets): add unit tests for base and anonmapexec utility functions
Add unit test coverage for the pure-Go utility functions in the presets subsystem as part of the test coverage audit tracked in #2130. Two packages are covered: presets/base/common_test.go: - TestAddPolicyLogInfo: 6 table-driven cases covering empty policy, name-only, single tag, multiple tags, severity+message, and all-fields-set inputs. Verifies PolicyName, Tags (joined string), ATags (slice), Severity, Message, Type, and KubeArmorVersion fields. - TestUpdateMatchPolicy: 5 table-driven cases covering zero-value policy, full policy, missing policyName key, high severity, and single-tag inputs. Verifies PolicyName, Severity (strconv.Itoa conversion), Message, and Tags fields are correctly mapped from SecurityPolicy. - TestPresetConstants: verifies PRESET_ENFORCER, Audit, and Block constant values match their documented semantics. - TestNsKeyConstruction: verifies NsKey struct construction with zero, typical, and max uint32 values. presets/anonmapexec/utils_test.go: - TestParseProtectionFlags: 9 table-driven cases covering all bitmask combinations of PROT_READ (0x1), PROT_WRITE (0x2), PROT_EXEC (0x4), zero flags, and high-bit passthrough values. - TestParseMemoryFlags: 11 table-driven cases covering all 6 flag bits (MAP_SHARED, MAP_PRIVATE, MAP_FIXED, MAP_ANONYMOUS, MAP_GROWSDOWN, MAP_DENYWRITE) individually and in combination. BPF-dependent sub-packages (exec/, filelessexec/) are intentionally excluded — they require bpf2go codegen and cannot compile in standard go test without a kernel BPF runtime. Validation: GOOS=linux GOARCH=amd64 go vet and go test -c pass cleanly. Execution is verified on Linux CI (macOS dev env has case-insensitive FS and Linux-specific syscall constraints in transitive deps). Part of #2130 Signed-off-by: cynox-66 <devj2311@gmail.com>
1 parent 5df821c commit f6751fa

2 files changed

Lines changed: 367 additions & 0 deletions

File tree

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// Copyright 2026 Authors of KubeArmor
3+
4+
package anonmapexec
5+
6+
import "testing"
7+
8+
// ── ParseProtectionFlags ──────────────────────────────────────────────────────
9+
10+
func TestParseProtectionFlags(t *testing.T) {
11+
tests := []struct {
12+
name string
13+
prot uint64
14+
expected string
15+
}{
16+
{"zero_flags", 0x0, ""},
17+
{"read_only", 0x1, "PROT_READ"},
18+
{"write_only", 0x2, "PROT_WRITE"},
19+
{"exec_only", 0x4, "PROT_EXEC"},
20+
{"read_write", 0x3, "PROT_READ|PROT_WRITE"},
21+
{"read_exec", 0x5, "PROT_READ|PROT_EXEC"},
22+
{"write_exec", 0x6, "PROT_WRITE|PROT_EXEC"},
23+
{"all_flags", 0x7, "PROT_READ|PROT_WRITE|PROT_EXEC"},
24+
{"high_bits_ignored", 0xFF, "PROT_READ|PROT_WRITE|PROT_EXEC"},
25+
}
26+
27+
for _, tt := range tests {
28+
t.Run(tt.name, func(t *testing.T) {
29+
got := ParseProtectionFlags(tt.prot)
30+
if got != tt.expected {
31+
t.Errorf("ParseProtectionFlags(0x%x) = %q, want %q", tt.prot, got, tt.expected)
32+
}
33+
})
34+
}
35+
}
36+
37+
// ── ParseMemoryFlags ──────────────────────────────────────────────────────────
38+
39+
func TestParseMemoryFlags(t *testing.T) {
40+
tests := []struct {
41+
name string
42+
flag uint64
43+
expected string
44+
}{
45+
{"zero_flags", 0x0, ""},
46+
{"shared_only", 0x01, "MAP_SHARED"},
47+
{"private_only", 0x02, "MAP_PRIVATE"},
48+
{"fixed_only", 0x10, "MAP_FIXED"},
49+
{"anonymous_only", 0x20, "MAP_ANONYMOUS"},
50+
{"growsdown_only", 0x1000, "MAP_GROWSDOWN"},
51+
{"denywrite_only", 0x0800, "MAP_DENYWRITE"},
52+
{"shared_anonymous", 0x21, "MAP_SHARED|MAP_ANONYMOUS"},
53+
{"private_anonymous", 0x22, "MAP_PRIVATE|MAP_ANONYMOUS"},
54+
{"private_fixed_anonymous", 0x32, "MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS"},
55+
{
56+
"all_flags",
57+
0x01 | 0x02 | 0x10 | 0x20 | 0x1000 | 0x0800,
58+
"MAP_SHARED|MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS|MAP_GROWSDOWN|MAP_DENYWRITE",
59+
},
60+
}
61+
62+
for _, tt := range tests {
63+
t.Run(tt.name, func(t *testing.T) {
64+
got := ParseMemoryFlags(tt.flag)
65+
if got != tt.expected {
66+
t.Errorf("ParseMemoryFlags(0x%x) = %q, want %q", tt.flag, got, tt.expected)
67+
}
68+
})
69+
}
70+
}
Lines changed: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// Copyright 2026 Authors of KubeArmor
3+
4+
package base
5+
6+
import (
7+
"testing"
8+
9+
"github.com/kubearmor/KubeArmor/KubeArmor/buildinfo"
10+
tp "github.com/kubearmor/KubeArmor/KubeArmor/types"
11+
)
12+
13+
// ── AddPolicyLogInfo ──────────────────────────────────────────────────────────
14+
15+
func TestAddPolicyLogInfo(t *testing.T) {
16+
tests := []struct {
17+
name string
18+
ckv ContainerVal
19+
wantName string
20+
wantTags string
21+
wantATags []string
22+
wantSev string
23+
wantMsg string
24+
wantType string
25+
}{
26+
{
27+
name: "empty_policy",
28+
ckv: ContainerVal{},
29+
wantName: "",
30+
wantTags: "",
31+
wantATags: nil,
32+
wantSev: "",
33+
wantMsg: "",
34+
wantType: "MatchedPolicy",
35+
},
36+
{
37+
name: "policy_name_only",
38+
ckv: ContainerVal{
39+
Policy: tp.MatchPolicy{PolicyName: "test-policy"},
40+
},
41+
wantName: "test-policy",
42+
wantTags: "",
43+
wantATags: nil,
44+
wantSev: "",
45+
wantMsg: "",
46+
wantType: "MatchedPolicy",
47+
},
48+
{
49+
name: "single_tag",
50+
ckv: ContainerVal{
51+
Policy: tp.MatchPolicy{
52+
PolicyName: "p1",
53+
Tags: []string{"cis"},
54+
},
55+
},
56+
wantName: "p1",
57+
wantTags: "cis",
58+
wantATags: []string{"cis"},
59+
wantSev: "",
60+
wantMsg: "",
61+
wantType: "MatchedPolicy",
62+
},
63+
{
64+
name: "multiple_tags",
65+
ckv: ContainerVal{
66+
Policy: tp.MatchPolicy{
67+
PolicyName: "p2",
68+
Tags: []string{"cis", "nist", "pci"},
69+
},
70+
},
71+
wantName: "p2",
72+
wantTags: "cis,nist,pci",
73+
wantATags: []string{"cis", "nist", "pci"},
74+
wantSev: "",
75+
wantMsg: "",
76+
wantType: "MatchedPolicy",
77+
},
78+
{
79+
name: "severity_and_message",
80+
ckv: ContainerVal{
81+
Policy: tp.MatchPolicy{
82+
PolicyName: "p3",
83+
Severity: "5",
84+
Message: "suspicious exec detected",
85+
},
86+
},
87+
wantName: "p3",
88+
wantTags: "",
89+
wantATags: nil,
90+
wantSev: "5",
91+
wantMsg: "suspicious exec detected",
92+
wantType: "MatchedPolicy",
93+
},
94+
{
95+
name: "all_fields_set",
96+
ckv: ContainerVal{
97+
NsKey: NsKey{PidNS: 100, MntNS: 200},
98+
Policy: tp.MatchPolicy{
99+
PolicyName: "full-policy",
100+
Severity: "3",
101+
Message: "alert",
102+
Tags: []string{"cis", "nist"},
103+
},
104+
},
105+
wantName: "full-policy",
106+
wantTags: "cis,nist",
107+
wantATags: []string{"cis", "nist"},
108+
wantSev: "3",
109+
wantMsg: "alert",
110+
wantType: "MatchedPolicy",
111+
},
112+
}
113+
114+
for _, tt := range tests {
115+
t.Run(tt.name, func(t *testing.T) {
116+
log := &tp.Log{}
117+
AddPolicyLogInfo(log, &tt.ckv)
118+
119+
if log.PolicyName != tt.wantName {
120+
t.Errorf("PolicyName = %q, want %q", log.PolicyName, tt.wantName)
121+
}
122+
if log.Tags != tt.wantTags {
123+
t.Errorf("Tags = %q, want %q", log.Tags, tt.wantTags)
124+
}
125+
if len(log.ATags) != len(tt.wantATags) {
126+
t.Errorf("ATags length = %d, want %d", len(log.ATags), len(tt.wantATags))
127+
} else {
128+
for i := range tt.wantATags {
129+
if log.ATags[i] != tt.wantATags[i] {
130+
t.Errorf("ATags[%d] = %q, want %q", i, log.ATags[i], tt.wantATags[i])
131+
}
132+
}
133+
}
134+
if log.Severity != tt.wantSev {
135+
t.Errorf("Severity = %q, want %q", log.Severity, tt.wantSev)
136+
}
137+
if log.Message != tt.wantMsg {
138+
t.Errorf("Message = %q, want %q", log.Message, tt.wantMsg)
139+
}
140+
if log.Type != tt.wantType {
141+
t.Errorf("Type = %q, want %q", log.Type, tt.wantType)
142+
}
143+
// KubeArmorVersion mirrors buildinfo.GitSummary at call time.
144+
// In unit tests, GitSummary is "" (no build-time injection).
145+
if log.KubeArmorVersion != buildinfo.GitSummary {
146+
t.Errorf("KubeArmorVersion = %q, want %q (buildinfo.GitSummary)", log.KubeArmorVersion, buildinfo.GitSummary)
147+
}
148+
})
149+
}
150+
}
151+
152+
// ── UpdateMatchPolicy ─────────────────────────────────────────────────────────
153+
154+
func TestUpdateMatchPolicy(t *testing.T) {
155+
tests := []struct {
156+
name string
157+
metadata map[string]string
158+
severity int
159+
message string
160+
tags []string
161+
wantName string
162+
wantSev string
163+
wantMsg string
164+
wantTags []string
165+
}{
166+
{
167+
name: "zero_value_policy",
168+
metadata: map[string]string{},
169+
severity: 0,
170+
message: "",
171+
tags: nil,
172+
wantName: "",
173+
wantSev: "0",
174+
wantMsg: "",
175+
wantTags: nil,
176+
},
177+
{
178+
name: "full_policy",
179+
metadata: map[string]string{"policyName": "block-exec"},
180+
severity: 5,
181+
message: "blocked execution",
182+
tags: []string{"cis", "nist"},
183+
wantName: "block-exec",
184+
wantSev: "5",
185+
wantMsg: "blocked execution",
186+
wantTags: []string{"cis", "nist"},
187+
},
188+
{
189+
name: "missing_policyname_key",
190+
metadata: map[string]string{"other": "value"},
191+
severity: 3,
192+
message: "msg",
193+
tags: []string{"t1"},
194+
wantName: "",
195+
wantSev: "3",
196+
wantMsg: "msg",
197+
wantTags: []string{"t1"},
198+
},
199+
{
200+
name: "high_severity",
201+
metadata: map[string]string{"policyName": "critical-policy"},
202+
severity: 10,
203+
message: "critical",
204+
tags: []string{"critical"},
205+
wantName: "critical-policy",
206+
wantSev: "10",
207+
wantMsg: "critical",
208+
wantTags: []string{"critical"},
209+
},
210+
{
211+
name: "single_tag",
212+
metadata: map[string]string{"policyName": "p"},
213+
severity: 1,
214+
message: "",
215+
tags: []string{"pci"},
216+
wantName: "p",
217+
wantSev: "1",
218+
wantMsg: "",
219+
wantTags: []string{"pci"},
220+
},
221+
}
222+
223+
for _, tt := range tests {
224+
t.Run(tt.name, func(t *testing.T) {
225+
ckv := &ContainerVal{}
226+
secPolicy := &tp.SecurityPolicy{
227+
Metadata: tt.metadata,
228+
Spec: tp.SecuritySpec{
229+
Severity: tt.severity,
230+
Message: tt.message,
231+
Tags: tt.tags,
232+
},
233+
}
234+
235+
UpdateMatchPolicy(ckv, secPolicy)
236+
237+
if ckv.Policy.PolicyName != tt.wantName {
238+
t.Errorf("PolicyName = %q, want %q", ckv.Policy.PolicyName, tt.wantName)
239+
}
240+
if ckv.Policy.Severity != tt.wantSev {
241+
t.Errorf("Severity = %q, want %q", ckv.Policy.Severity, tt.wantSev)
242+
}
243+
if ckv.Policy.Message != tt.wantMsg {
244+
t.Errorf("Message = %q, want %q", ckv.Policy.Message, tt.wantMsg)
245+
}
246+
if len(ckv.Policy.Tags) != len(tt.wantTags) {
247+
t.Errorf("Tags length = %d, want %d", len(ckv.Policy.Tags), len(tt.wantTags))
248+
} else {
249+
for i := range tt.wantTags {
250+
if ckv.Policy.Tags[i] != tt.wantTags[i] {
251+
t.Errorf("Tags[%d] = %q, want %q", i, ckv.Policy.Tags[i], tt.wantTags[i])
252+
}
253+
}
254+
}
255+
})
256+
}
257+
}
258+
259+
// ── Constants (basePreset.go) ─────────────────────────────────────────────────
260+
261+
func TestPresetConstants(t *testing.T) {
262+
if PRESET_ENFORCER != "PRESET-" {
263+
t.Errorf("PRESET_ENFORCER = %q, want %q", PRESET_ENFORCER, "PRESET-")
264+
}
265+
if Audit != 1 {
266+
t.Errorf("Audit = %d, want 1", Audit)
267+
}
268+
if Block != 2 {
269+
t.Errorf("Block = %d, want 2", Block)
270+
}
271+
}
272+
273+
// ── Struct construction (basePreset.go) ───────────────────────────────────────
274+
275+
func TestNsKeyConstruction(t *testing.T) {
276+
tests := []struct {
277+
name string
278+
pidNS uint32
279+
mntNS uint32
280+
}{
281+
{"zero_values", 0, 0},
282+
{"typical_values", 4026531836, 4026531840},
283+
{"max_values", ^uint32(0), ^uint32(0)},
284+
}
285+
286+
for _, tt := range tests {
287+
t.Run(tt.name, func(t *testing.T) {
288+
key := NsKey{PidNS: tt.pidNS, MntNS: tt.mntNS}
289+
if key.PidNS != tt.pidNS {
290+
t.Errorf("PidNS = %d, want %d", key.PidNS, tt.pidNS)
291+
}
292+
if key.MntNS != tt.mntNS {
293+
t.Errorf("MntNS = %d, want %d", key.MntNS, tt.mntNS)
294+
}
295+
})
296+
}
297+
}

0 commit comments

Comments
 (0)