// apparmor.d - Full set of apparmor profiles // Copyright (C) 2021-2024 Alexandre Pujol // SPDX-License-Identifier: GPL-2.0-only package aa import ( "reflect" "testing" ) func TestRules_FromLog(t *testing.T) { for _, tt := range testRule { if tt.fromLog == nil { continue } t.Run(tt.name, func(t *testing.T) { if got := tt.fromLog(tt.log); !reflect.DeepEqual(got, tt.rule) { t.Errorf("RuleFromLog() = %v, want %v", got, tt.rule) } }) } } func TestRules_Validate(t *testing.T) { for _, tt := range testRule { t.Run(tt.name, func(t *testing.T) { if err := tt.rule.Validate(); (err != nil) != tt.wValidErr { t.Errorf("Rules.Validate() error = %v, wantErr %v", err, tt.wValidErr) } }) } } func TestRules_Less(t *testing.T) { for _, tt := range testRule { if tt.oLess == nil { continue } t.Run(tt.name, func(t *testing.T) { if got := tt.rule.Less(tt.oLess); got != tt.wLessErr { t.Errorf("Rule.Less() = %v, want %v", got, tt.wLessErr) } }) } } func TestRules_Equals(t *testing.T) { for _, tt := range testRule { if tt.oEqual == nil { continue } t.Run(tt.name, func(t *testing.T) { r := tt.rule if got := r.Equals(tt.oEqual); got != tt.wEqualErr { t.Errorf("Rule.Equals() = %v, want %v", got, tt.wEqualErr) } }) } } func TestRules_String(t *testing.T) { for _, tt := range testRule { t.Run(tt.name, func(t *testing.T) { if got := tt.rule.String(); got != tt.wString { t.Errorf("Rule.String() = %v, want %v", got, tt.wString) } }) } } var ( // Test cases for the Rule interface testRule = []struct { name string fromLog func(map[string]string) Rule log map[string]string rule Rule wValidErr bool oLess Rule wLessErr bool oEqual Rule wEqualErr bool wString string }{ { name: "comment", rule: comment1, oLess: comment2, wLessErr: false, oEqual: comment2, wEqualErr: false, wString: "#comment", }, { name: "abi", rule: abi1, oLess: abi2, wLessErr: false, oEqual: abi1, wEqualErr: true, wString: "abi ,", }, { name: "alias", rule: alias1, oLess: alias2, wLessErr: true, oEqual: alias2, wEqualErr: false, wString: "alias /mnt/usr -> /usr,", }, { name: "include1", rule: include1, oLess: includeLocal1, wLessErr: false, oEqual: includeLocal1, wEqualErr: false, wString: "include ", }, { name: "include2", rule: include1, oLess: include2, wLessErr: false, wString: "include ", }, { name: "include-local", rule: includeLocal1, oLess: include1, wLessErr: true, wString: "include if exists ", }, { name: "include/abs", rule: &Include{Path: "/usr/share/apparmor.d/", IsMagic: false}, wString: `include "/usr/share/apparmor.d/"`, }, { name: "variable", rule: variable1, oLess: variable2, wLessErr: true, oEqual: variable1, wEqualErr: true, wString: "@{bin} = /{,usr/}{,s}bin", }, { name: "all", rule: all1, oLess: all2, wLessErr: false, oEqual: all2, wEqualErr: false, wString: "all,", }, { name: "rlimit", rule: rlimit1, oLess: rlimit2, wLessErr: false, oEqual: rlimit1, wEqualErr: true, wString: "set rlimit nproc <= 200,", }, { name: "rlimit2", rule: rlimit2, oLess: rlimit2, wLessErr: false, wString: "set rlimit cpu <= 2,", }, { name: "rlimit3", rule: rlimit3, oLess: rlimit1, wLessErr: true, wString: "set rlimit nproc < 2,", }, { name: "userns", rule: userns1, oLess: userns2, wLessErr: true, oEqual: userns1, wEqualErr: true, wString: "userns,", }, { name: "capbability", fromLog: newCapabilityFromLog, log: capability1Log, rule: capability1, oLess: capability2, wLessErr: true, oEqual: capability1, wEqualErr: true, wString: "capability net_admin,", }, { name: "capability/multi", rule: &Capability{Names: []string{"dac_override", "dac_read_search"}}, wString: "capability dac_override dac_read_search,", }, { name: "capability/all", rule: &Capability{}, wString: "capability,", }, { name: "network", fromLog: newNetworkFromLog, log: network1Log, rule: network1, wValidErr: true, oLess: network2, wLessErr: false, oEqual: network1, wEqualErr: true, wString: "network netlink raw,", }, { name: "mount", fromLog: newMountFromLog, log: mount1Log, rule: mount1, oEqual: mount2, wEqualErr: false, wString: "mount fstype=overlay overlay -> /var/lib/docker/overlay2/opaque-bug-check1209538631/merged/, # failed perms check", }, { name: "remount", rule: remount1, oLess: remount2, wLessErr: true, oEqual: remount1, wEqualErr: true, wString: "remount /,", }, { name: "umount", fromLog: newUmountFromLog, log: umount1Log, rule: umount1, oLess: umount2, wLessErr: true, oEqual: umount1, wEqualErr: true, wString: "umount /,", }, { name: "pivot_root1", fromLog: newPivotRootFromLog, log: pivotroot1Log, rule: pivotroot1, oLess: pivotroot2, wLessErr: false, oEqual: pivotroot2, wEqualErr: false, wString: "pivot_root oldroot=@{run}/systemd/mount-rootfs/ @{run}/systemd/mount-rootfs/,", }, { name: "pivot_root2", rule: pivotroot1, oLess: pivotroot3, wLessErr: false, wString: "pivot_root oldroot=@{run}/systemd/mount-rootfs/ @{run}/systemd/mount-rootfs/,", }, { name: "change_profile1", fromLog: newChangeProfileFromLog, log: changeprofile1Log, rule: changeprofile1, oLess: changeprofile2, wLessErr: false, wString: "change_profile -> systemd-user,", }, { name: "change_profile2", rule: changeprofile2, oLess: changeprofile3, wLessErr: true, oEqual: changeprofile1, wEqualErr: false, wString: "change_profile -> brwap,", }, { name: "mqueue", rule: mqueue1, oLess: mqueue2, wLessErr: true, oEqual: mqueue1, wEqualErr: true, wString: "mqueue r type=posix /,", }, { name: "iouring", rule: iouring1, oLess: iouring2, wLessErr: false, oEqual: iouring2, wEqualErr: false, wString: "io_uring sqpoll label=foo,", }, { name: "signal", fromLog: newSignalFromLog, log: signal1Log, rule: signal1, oLess: signal2, wLessErr: false, oEqual: signal1, wEqualErr: true, wString: "signal receive set=kill peer=firefox//&firejail-default,", }, { name: "ptrace/xdg-document-portal", fromLog: newPtraceFromLog, log: ptrace1Log, rule: ptrace1, oLess: ptrace2, wLessErr: false, oEqual: ptrace1, wEqualErr: true, wString: "ptrace read peer=nautilus,", }, { name: "ptrace/snap-update-ns.firefox", fromLog: newPtraceFromLog, log: ptrace2Log, rule: ptrace2, oLess: ptrace1, wLessErr: false, oEqual: ptrace1, wEqualErr: false, wString: "ptrace readby peer=systemd-journald,", }, { name: "unix", fromLog: newUnixFromLog, log: unix1Log, rule: unix1, oLess: unix1, wLessErr: false, oEqual: unix1, wEqualErr: true, wString: "unix (send receive) type=stream protocol=0 addr=none peer=(label=dbus-daemon, addr=@/tmp/dbus-AaKMpxzC4k),", }, { name: "dbus", fromLog: newDbusFromLog, log: dbus1Log, rule: dbus1, oLess: dbus1, wLessErr: false, oEqual: dbus2, wEqualErr: false, wString: "dbus receive bus=session path=/org/gtk/vfs/metadata\n interface=org.gtk.vfs.Metadata\n member=Remove\n peer=(name=:1.15, label=tracker-extract),", }, { name: "dbus2", rule: dbus2, oLess: dbus3, wLessErr: false, wString: "dbus bind bus=session name=org.gnome.evolution.dataserver.Sources5,", }, { name: "dbus/bind", rule: &Dbus{Access: []string{"bind"}, Bus: "session", Name: "org.gnome.*"}, wString: `dbus bind bus=session name=org.gnome.*,`, }, { name: "dbus/full", rule: &Dbus{Bus: "accessibility"}, wString: `dbus bus=accessibility,`, }, { name: "file", fromLog: newFileFromLog, log: file1Log, rule: file1, oLess: file2, wLessErr: true, oEqual: file2, wEqualErr: false, wString: "/usr/share/poppler/cMap/Identity-H r,", }, { name: "file/empty", rule: &File{}, oLess: &File{}, wLessErr: false, wString: " ,", }, { name: "file/equal", rule: &File{Path: "/usr/share/poppler/cMap/Identity-H"}, oLess: &File{Path: "/usr/share/poppler/cMap/Identity-H"}, wLessErr: false, wString: "/usr/share/poppler/cMap/Identity-H ,", }, { name: "file/owner", rule: &File{Path: "/usr/share/poppler/cMap/Identity-H", Owner: true}, oLess: &File{Path: "/usr/share/poppler/cMap/Identity-H"}, wLessErr: true, wString: "owner /usr/share/poppler/cMap/Identity-H ,", }, { name: "file/access", rule: &File{Path: "/usr/share/poppler/cMap/Identity-H", Access: []string{"r"}}, oLess: &File{Path: "/usr/share/poppler/cMap/Identity-H", Access: []string{"w"}}, wLessErr: false, wString: "/usr/share/poppler/cMap/Identity-H r,", }, { name: "file/close", rule: &File{Path: "/usr/share/poppler/cMap/"}, oLess: &File{Path: "/usr/share/poppler/cMap/Identity-H"}, wLessErr: true, wString: "/usr/share/poppler/cMap/ ,", }, { name: "link", fromLog: newLinkFromLog, log: link1Log, rule: link1, oLess: link2, wLessErr: true, oEqual: link3, wEqualErr: false, wString: "link /tmp/mkinitcpio.QDWtza/early@{lib}/firmware/i915/dg1_dmc_ver2_02.bin.zst -> /tmp/mkinitcpio.QDWtza/root@{lib}/firmware/i915/dg1_dmc_ver2_02.bin.zst,", }, { name: "link", fromLog: newFileFromLog, log: link3Log, rule: link3, wString: "owner link @{user_config_dirs}/kiorc -> @{user_config_dirs}/#3954,", }, { name: "profile", rule: profile1, oLess: profile2, wLessErr: true, oEqual: profile1, wEqualErr: true, wString: "profile sudo {\n}", }, { name: "hat", rule: hat1, oLess: hat2, wLessErr: false, oEqual: hat1, wEqualErr: true, wString: "hat user {\n}", }, } )