feat(aa): ensure accesses are slice of string.

This commit is contained in:
Alexandre Pujol 2024-04-23 21:17:25 +01:00
parent a2910122d2
commit c719a0a109
No known key found for this signature in database
GPG Key ID: C5469996F0DF68EC
16 changed files with 240 additions and 210 deletions

View File

@ -61,77 +61,6 @@ func (f *AppArmorProfileFile) GetDefaultProfile() *Profile {
return f.Profiles[0]
}
// AddRule adds a new rule to the profile from a log map
// See utils/apparmor/logparser.py for the format of the log map
func (f *AppArmorProfileFile) AddRule(log map[string]string) {
p := f.GetDefaultProfile()
// Generate profile flags and extra rules
switch log["error"] {
case "-2":
if !slices.Contains(p.Flags, "mediate_deleted") {
p.Flags = append(p.Flags, "mediate_deleted")
}
case "-13":
if strings.Contains(log["info"], "namespace creation restricted") {
p.Rules = append(p.Rules, newUsernsFromLog(log))
} else if strings.Contains(log["info"], "disconnected path") && !slices.Contains(p.Flags, "attach_disconnected") {
p.Flags = append(p.Flags, "attach_disconnected")
}
default:
}
switch log["class"] {
case "cap":
p.Rules = append(p.Rules, newCapabilityFromLog(log))
case "net":
if log["family"] == "unix" {
p.Rules = append(p.Rules, newUnixFromLog(log))
} else {
p.Rules = append(p.Rules, newNetworkFromLog(log))
}
case "mount":
if strings.Contains(log["flags"], "remount") {
p.Rules = append(p.Rules, newRemountFromLog(log))
} else {
switch log["operation"] {
case "mount":
p.Rules = append(p.Rules, newMountFromLog(log))
case "umount":
p.Rules = append(p.Rules, newUmountFromLog(log))
case "remount":
p.Rules = append(p.Rules, newRemountFromLog(log))
case "pivotroot":
p.Rules = append(p.Rules, newPivotRootFromLog(log))
}
}
case "posix_mqueue", "sysv_mqueue":
p.Rules = append(p.Rules, newMqueueFromLog(log))
case "signal":
p.Rules = append(p.Rules, newSignalFromLog(log))
case "ptrace":
p.Rules = append(p.Rules, newPtraceFromLog(log))
case "namespace":
p.Rules = append(p.Rules, newUsernsFromLog(log))
case "unix":
p.Rules = append(p.Rules, newUnixFromLog(log))
case "dbus":
p.Rules = append(p.Rules, newDbusFromLog(log))
case "file":
if log["operation"] == "change_onexec" {
p.Rules = append(p.Rules, newChangeProfileFromLog(log))
} else {
p.Rules = append(p.Rules, newFileFromLog(log))
}
default:
if strings.Contains(log["operation"], "dbus") {
p.Rules = append(p.Rules, newDbusFromLog(log))
} else if log["family"] == "unix" {
p.Rules = append(p.Rules, newUnixFromLog(log))
}
}
}
// Sort the rules in the profile
// Follow: https://apparmor.pujol.io/development/guidelines/#guidelines
func (f *AppArmorProfileFile) Sort() {

View File

@ -62,8 +62,8 @@ func TestAppArmorProfile_String(t *testing.T) {
&Include{IsMagic: true, Path: "abstractions/base"},
&Include{IsMagic: true, Path: "abstractions/nameservice-strict"},
rlimit1,
&Capability{Name: "dac_read_search"},
&Capability{Name: "dac_override"},
&Capability{Names: []string{"dac_read_search"}},
&Capability{Names: []string{"dac_override"}},
&Network{Domain: "inet", Type: "stream"},
&Network{Domain: "inet6", Type: "stream"},
&Mount{
@ -79,25 +79,25 @@ func TestAppArmorProfile_String(t *testing.T) {
MountPoint: "@{run}/user/@{uid}/",
},
&Signal{
Access: "receive",
Set: "term",
Access: []string{"receive"},
Set: []string{"term"},
Peer: "at-spi-bus-launcher",
},
&Ptrace{Access: "read", Peer: "nautilus"},
&Ptrace{Access: []string{"read"}, Peer: "nautilus"},
&Unix{
Access: "send receive",
Access: []string{"send", "receive"},
Type: "stream",
Address: "@/tmp/.ICE-unix/1995",
PeerLabel: "gnome-shell",
PeerAddr: "none",
},
&Dbus{
Access: "bind",
Access: []string{"bind"},
Bus: "session",
Name: "org.gnome.*",
},
&Dbus{
Access: "receive",
Access: []string{"receive"},
Bus: "system",
Path: "/org/freedesktop/DBus",
Interface: "org.freedesktop.DBus",
@ -105,9 +105,9 @@ func TestAppArmorProfile_String(t *testing.T) {
PeerName: ":1.3",
PeerLabel: "power-profiles-daemon",
},
&File{Path: "/opt/intel/oneapi/compiler/*/linux/lib/*.so./*", Access: "rm"},
&File{Path: "@{PROC}/@{pid}/task/@{tid}/comm", Access: "rw"},
&File{Path: "@{sys}/devices/@{pci}/class", Access: "r"},
&File{Path: "/opt/intel/oneapi/compiler/*/linux/lib/*.so./*", Access: []string{"r", "m"}},
&File{Path: "@{PROC}/@{pid}/task/@{tid}/comm", Access: []string{"r", "w"}},
&File{Path: "@{sys}/devices/@{pci}/class", Access: []string{"r"}},
includeLocal1,
},
}},
@ -306,19 +306,19 @@ func TestAppArmorProfile_Integration(t *testing.T) {
},
Rules: Rules{
&Include{IfExists: true, IsMagic: true, Path: "local/aa-status"},
&Capability{Name: "dac_read_search"},
&File{Path: "@{exec_path}", Access: "mr"},
&File{Path: "@{PROC}/@{pids}/attr/apparmor/current", Access: "r"},
&File{Path: "@{PROC}/", Access: "r"},
&File{Path: "@{sys}/module/apparmor/parameters/enabled", Access: "r"},
&File{Path: "@{sys}/kernel/security/apparmor/profiles", Access: "r"},
&File{Path: "@{PROC}/@{pids}/attr/current", Access: "r"},
&Capability{Names: []string{"dac_read_search"}},
&File{Path: "@{exec_path}", Access: []string{"m", "r"}},
&File{Path: "@{PROC}/@{pids}/attr/apparmor/current", Access: []string{"r"}},
&File{Path: "@{PROC}/", Access: []string{"r"}},
&File{Path: "@{sys}/module/apparmor/parameters/enabled", Access: []string{"r"}},
&File{Path: "@{sys}/kernel/security/apparmor/profiles", Access: []string{"r"}},
&File{Path: "@{PROC}/@{pids}/attr/current", Access: []string{"r"}},
&Include{IsMagic: true, Path: "abstractions/consoles"},
&File{Owner: true, Path: "@{PROC}/@{pid}/mounts", Access: "r"},
&File{Owner: true, Path: "@{PROC}/@{pid}/mounts", Access: []string{"r"}},
&Include{IsMagic: true, Path: "abstractions/base"},
&File{Path: "/dev/tty@{int}", Access: "rw"},
&Capability{Name: "sys_ptrace"},
&Ptrace{Access: "read"},
&File{Path: "/dev/tty@{int}", Access: []string{"r", "w"}},
&Capability{Names: []string{"sys_ptrace"}},
&Ptrace{Access: []string{"read"}},
},
}},
},

View File

@ -4,29 +4,36 @@
package aa
type Capability struct {
RuleBase
Qualifier
Name string
Names []string
}
}
func newCapabilityFromLog(log map[string]string) Rule {
return &Capability{
RuleBase: newRuleFromLog(log),
Qualifier: newQualifierFromLog(log),
Name: log["capname"],
Names: []string{log["capname"]},
}
}
func (r *Capability) Less(other any) bool {
o, _ := other.(*Capability)
if r.Name != o.Name {
return r.Name < o.Name
for i := 0; i < len(r.Names) && i < len(o.Names); i++ {
if r.Names[i] != o.Names[i] {
return r.Names[i] < o.Names[i]
}
}
return r.Qualifier.Less(o.Qualifier)
}
func (r *Capability) Equals(other any) bool {
o, _ := other.(*Capability)
return r.Name == o.Name && r.Qualifier.Equals(o.Qualifier)
return slices.Equal(r.Names, o.Names) && r.Qualifier.Equals(o.Qualifier)
}
}

View File

@ -26,8 +26,8 @@ var (
"profile": "pkexec",
"comm": "pkexec",
}
capability1 = &Capability{Name: "net_admin"}
capability2 = &Capability{Name: "sys_ptrace"}
capability1 = &Capability{Names: []string{"net_admin"}}
capability2 = &Capability{Names: []string{"sys_ptrace"}}
// Network
network1Log = map[string]string{
@ -147,13 +147,13 @@ var (
"peer": "firefox//&firejail-default",
}
signal1 = &Signal{
Access: "receive",
Set: "kill",
Access: []string{"receive"},
Set: []string{"kill"},
Peer: "firefox//&firejail-default",
}
signal2 = &Signal{
Access: "receive",
Set: "up",
Access: []string{"receive"},
Set: []string{"up"},
Peer: "firefox//&firejail-default",
}
@ -177,8 +177,8 @@ var (
"denied_mask": "readby",
"peer": "systemd-journald",
}
ptrace1 = &Ptrace{Access: "read", Peer: "nautilus"}
ptrace2 = &Ptrace{Access: "readby", Peer: "systemd-journald"}
ptrace1 = &Ptrace{Access: []string{"read"}, Peer: "nautilus"}
ptrace2 = &Ptrace{Access: []string{"readby"}, Peer: "systemd-journald"}
// Unix
unix1Log = map[string]string{
@ -197,7 +197,7 @@ var (
"protocol": "0",
}
unix1 = &Unix{
Access: "send receive",
Access: []string{"receive", "send"},
Type: "stream",
Protocol: "0",
Address: "none",
@ -206,7 +206,7 @@ var (
}
unix2 = &Unix{
RuleBase: RuleBase{FileInherit: true},
Access: "receive",
Access: []string{"receive"},
Type: "stream",
}
@ -234,7 +234,7 @@ var (
"label": "evolution-source-registry",
}
dbus1 = &Dbus{
Access: "receive",
Access: []string{"receive"},
Bus: "session",
Path: "/org/gtk/vfs/metadata",
Interface: "org.gtk.vfs.Metadata",
@ -243,12 +243,12 @@ var (
PeerLabel: "tracker-extract",
}
dbus2 = &Dbus{
Access: "bind",
Access: []string{"bind"},
Bus: "session",
Name: "org.gnome.evolution.dataserver.Sources5",
}
dbus3 = &Dbus{
Access: "bind",
Access: []string{"bind"},
Bus: "session",
Name: "org.gnome.evolution.dataserver",
}
@ -283,11 +283,11 @@ var (
"OUID": "user",
"error": "-1",
}
file1 = &File{Path: "/usr/share/poppler/cMap/Identity-H", Access: "r"}
file1 = &File{Path: "/usr/share/poppler/cMap/Identity-H", Access: []string{"r"}}
file2 = &File{
RuleBase: RuleBase{NoNewPrivs: true},
Owner: true,
Path: "@{PROC}/4163/cgroup",
Access: "r",
Access: []string{"r"},
}
)

View File

@ -7,7 +7,7 @@ package aa
type Dbus struct {
RuleBase
Qualifier
Access string
Access []string
Bus string
Name string
Path string
@ -28,7 +28,7 @@ func newDbusFromLog(log map[string]string) Rule {
return &Dbus{
RuleBase: newRuleFromLog(log),
Qualifier: newQualifierFromLog(log),
Access: log["mask"],
Access: []string{log["mask"]},
Bus: log["bus"],
Name: name,
Path: log["path"],
@ -41,8 +41,10 @@ func newDbusFromLog(log map[string]string) Rule {
func (r *Dbus) Less(other any) bool {
o, _ := other.(*Dbus)
if r.Access != o.Access {
return r.Access < o.Access
for i := 0; i < len(r.Access) && i < len(o.Access); i++ {
if r.Access[i] != o.Access[i] {
return r.Access[i] < o.Access[i]
}
}
if r.Bus != o.Bus {
return r.Bus < o.Bus
@ -70,7 +72,7 @@ func (r *Dbus) Less(other any) bool {
func (r *Dbus) Equals(other any) bool {
o, _ := other.(*Dbus)
return r.Access == o.Access && r.Bus == o.Bus && r.Name == o.Name &&
return slices.Equal(r.Access, o.Access) && r.Bus == o.Bus && r.Name == o.Name &&
r.Path == o.Path && r.Interface == o.Interface &&
r.Member == o.Member && r.PeerName == o.PeerName &&
r.PeerLabel == o.PeerLabel && r.Qualifier.Equals(o.Qualifier)

View File

@ -9,7 +9,7 @@ type File struct {
Qualifier
Owner bool
Path string
Access string
Access []string
Target string
}
@ -26,7 +26,7 @@ func newFileFromLog(log map[string]string) Rule {
Qualifier: newQualifierFromLog(log),
Owner: owner,
Path: log["name"],
Access: toAccess(log["requested_mask"]),
Access: toAccess("file-log", log["requested_mask"]),
Target: log["target"],
}
}
@ -41,8 +41,8 @@ func (r *File) Less(other any) bool {
if r.Path != o.Path {
return r.Path < o.Path
}
if r.Access != o.Access {
return r.Access < o.Access
if len(r.Access) != len(o.Access) {
return len(r.Access) < len(o.Access)
}
if r.Target != o.Target {
return r.Target < o.Target
@ -55,6 +55,9 @@ func (r *File) Less(other any) bool {
func (r *File) Equals(other any) bool {
o, _ := other.(*File)
return r.Path == o.Path && r.Access == o.Access && r.Owner == o.Owner &&
return r.Path == o.Path && slices.Equal(r.Access, o.Access) && r.Owner == o.Owner &&
r.Target == o.Target && r.Qualifier.Equals(o.Qualifier)
}
r.Target == o.Target && r.Qualifier.Equals(o.Qualifier)
}

View File

@ -7,7 +7,7 @@ package aa
type IOUring struct {
RuleBase
Qualifier
Access string
Access []string
Label string
}
@ -15,15 +15,15 @@ func newIOUringFromLog(log map[string]string) Rule {
return &IOUring{
RuleBase: newRuleFromLog(log),
Qualifier: newQualifierFromLog(log),
Access: toAccess(log["requested"]),
Access: toAccess(tokIOURING, log["requested"]),
Label: log["label"],
}
}
func (r *IOUring) Less(other any) bool {
o, _ := other.(*IOUring)
if r.Access != o.Access {
return r.Access < o.Access
if len(r.Access) != len(o.Access) {
return len(r.Access) < len(o.Access)
}
if r.Label != o.Label {
return r.Label < o.Label
@ -33,5 +33,7 @@ func (r *IOUring) Less(other any) bool {
func (r *IOUring) Equals(other any) bool {
o, _ := other.(*IOUring)
return r.Access == o.Access && r.Label == o.Label && r.Qualifier.Equals(o.Qualifier)
return slices.Equal(r.Access, o.Access) && r.Label == o.Label && r.Qualifier.Equals(o.Qualifier)
}
}

View File

@ -11,7 +11,7 @@ import (
type Mqueue struct {
RuleBase
Qualifier
Access string
Access []string
Type string
Label string
Name string
@ -27,7 +27,7 @@ func newMqueueFromLog(log map[string]string) Rule {
return &Mqueue{
RuleBase: newRuleFromLog(log),
Qualifier: newQualifierFromLog(log),
Access: toAccess(log["requested"]),
Access: toAccess(tokMQUEUE, log["requested"]),
Type: mqueueType,
Label: log["label"],
Name: log["name"],
@ -36,8 +36,8 @@ func newMqueueFromLog(log map[string]string) Rule {
func (r *Mqueue) Less(other any) bool {
o, _ := other.(*Mqueue)
if r.Access != o.Access {
return r.Access < o.Access
if len(r.Access) != len(o.Access) {
return len(r.Access) < len(o.Access)
}
if r.Type != o.Type {
return r.Type < o.Type
@ -50,6 +50,6 @@ func (r *Mqueue) Less(other any) bool {
func (r *Mqueue) Equals(other any) bool {
o, _ := other.(*Mqueue)
return r.Access == o.Access && r.Type == o.Type && r.Label == o.Label &&
return slices.Equal(r.Access, o.Access) && r.Type == o.Type && r.Label == o.Label &&
r.Name == o.Name && r.Qualifier.Equals(o.Qualifier)
}

View File

@ -39,3 +39,76 @@ func (p *Profile) Equals(other any) bool {
maps.Equal(p.Attributes, o.Attributes) &&
slices.Equal(p.Flags, o.Flags)
}
// AddRule adds a new rule to the profile from a log map.
func (p *Profile) AddRule(log map[string]string) {
// Generate profile flags and extra rules
switch log["error"] {
case "-2":
if !slices.Contains(p.Flags, "mediate_deleted") {
p.Flags = append(p.Flags, "mediate_deleted")
}
case "-13":
if strings.Contains(log["info"], "namespace creation restricted") {
p.Rules = append(p.Rules, newUsernsFromLog(log))
} else if strings.Contains(log["info"], "disconnected path") && !slices.Contains(p.Flags, "attach_disconnected") {
p.Flags = append(p.Flags, "attach_disconnected")
}
default:
}
switch log["class"] {
case "rlimits":
p.Rules = append(p.Rules, newRlimitFromLog(log))
case "cap":
p.Rules = append(p.Rules, newCapabilityFromLog(log))
case "net":
if log["family"] == "unix" {
p.Rules = append(p.Rules, newUnixFromLog(log))
} else {
p.Rules = append(p.Rules, newNetworkFromLog(log))
}
case "io_uring":
p.Rules = append(p.Rules, newIOUringFromLog(log))
case "mount":
if strings.Contains(log["flags"], "remount") {
p.Rules = append(p.Rules, newRemountFromLog(log))
} else {
switch log["operation"] {
case "mount":
p.Rules = append(p.Rules, newMountFromLog(log))
case "umount":
p.Rules = append(p.Rules, newUmountFromLog(log))
case "remount":
p.Rules = append(p.Rules, newRemountFromLog(log))
case "pivotroot":
p.Rules = append(p.Rules, newPivotRootFromLog(log))
}
}
case "posix_mqueue", "sysv_mqueue":
p.Rules = append(p.Rules, newMqueueFromLog(log))
case "signal":
p.Rules = append(p.Rules, newSignalFromLog(log))
case "ptrace":
p.Rules = append(p.Rules, newPtraceFromLog(log))
case "namespace":
p.Rules = append(p.Rules, newUsernsFromLog(log))
case "unix":
p.Rules = append(p.Rules, newUnixFromLog(log))
case "dbus":
p.Rules = append(p.Rules, newDbusFromLog(log))
case "file":
if log["operation"] == "change_onexec" {
p.Rules = append(p.Rules, newChangeProfileFromLog(log))
} else {
p.Rules = append(p.Rules, newFileFromLog(log))
}
default:
if strings.Contains(log["operation"], "dbus") {
p.Rules = append(p.Rules, newDbusFromLog(log))
} else if log["family"] == "unix" {
p.Rules = append(p.Rules, newUnixFromLog(log))
}
}
}

View File

@ -7,7 +7,7 @@ package aa
type Ptrace struct {
RuleBase
Qualifier
Access string
Access []string
Peer string
}
@ -15,15 +15,15 @@ func newPtraceFromLog(log map[string]string) Rule {
return &Ptrace{
RuleBase: newRuleFromLog(log),
Qualifier: newQualifierFromLog(log),
Access: toAccess(log["requested_mask"]),
Access: toAccess(tokPTRACE, log["requested_mask"]),
Peer: log["peer"],
}
}
func (r *Ptrace) Less(other any) bool {
o, _ := other.(*Ptrace)
if r.Access != o.Access {
return r.Access < o.Access
if len(r.Access) != len(o.Access) {
return len(r.Access) < len(o.Access)
}
if r.Peer != o.Peer {
return r.Peer == o.Peer
@ -33,6 +33,6 @@ func (r *Ptrace) Less(other any) bool {
func (r *Ptrace) Equals(other any) bool {
o, _ := other.(*Ptrace)
return r.Access == o.Access && r.Peer == o.Peer &&
return slices.Equal(r.Access, o.Access) && r.Peer == o.Peer &&
r.Qualifier.Equals(o.Qualifier)
}

View File

@ -251,9 +251,9 @@ func TestRule_Less(t *testing.T) {
},
{
name: "file/access",
rule: &File{Path: "/usr/share/poppler/cMap/Identity-H", Access: "r"},
other: &File{Path: "/usr/share/poppler/cMap/Identity-H", Access: "w"},
want: true,
rule: &File{Path: "/usr/share/poppler/cMap/Identity-H", Access: []string{"r"}},
other: &File{Path: "/usr/share/poppler/cMap/Identity-H", Access: []string{"w"}},
want: false,
},
{
name: "file/close",

View File

@ -7,8 +7,8 @@ package aa
type Signal struct {
RuleBase
Qualifier
Access string
Set string
Access []string
Set []string
Peer string
}
@ -16,19 +16,19 @@ func newSignalFromLog(log map[string]string) Rule {
return &Signal{
RuleBase: newRuleFromLog(log),
Qualifier: newQualifierFromLog(log),
Access: toAccess(log["requested_mask"]),
Set: log["signal"],
Access: toAccess(tokSIGNAL, log["requested_mask"]),
Set: toAccess(tokSIGNAL, log["signal"]),
Peer: log["peer"],
}
}
func (r *Signal) Less(other any) bool {
o, _ := other.(*Signal)
if r.Access != o.Access {
return r.Access < o.Access
if len(r.Access) != len(o.Access) {
return len(r.Access) < len(o.Access)
}
if r.Set != o.Set {
return r.Set < o.Set
if len(r.Set) != len(o.Set) {
return len(r.Set) < len(o.Set)
}
if r.Peer != o.Peer {
return r.Peer < o.Peer
@ -38,6 +38,6 @@ func (r *Signal) Less(other any) bool {
func (r *Signal) Equals(other any) bool {
o, _ := other.(*Signal)
return r.Access == o.Access && r.Set == o.Set &&
return slices.Equal(r.Access, o.Access) && slices.Equal(r.Set, o.Set) &&
r.Peer == o.Peer && r.Qualifier.Equals(o.Qualifier)
}

View File

@ -33,19 +33,10 @@ var (
}
// convert apparmor requested mask to apparmor access mode
requestedMaskToAccess = map[string]string{
"a": "w",
"ac": "w",
"c": "w",
"d": "w",
"m": "rm",
"ra": "rw",
"wc": "w",
"wd": "w",
"wr": "rw",
"wrc": "rw",
"wrd": "rw",
"x": "rix",
maskToAccess = map[string]string{
"a": "w",
"c": "w",
"d": "w",
}
// The order the apparmor rules should be sorted
@ -172,9 +163,38 @@ func getLetterIn(alphabet []string, in string) string {
return ""
}
func toAccess(mask string) string {
if requestedMaskToAccess[mask] != "" {
return requestedMaskToAccess[mask]
// Helper function to convert a access string to slice of access
func toAccess(constraint string, input string) []string {
var res []string
switch constraint {
case "file", "file-log":
raw := strings.Split(input, "")
trans := []string{}
for _, access := range raw {
if slices.Contains(fileAccess, access) {
res = append(res, access)
} else if maskToAccess[access] != "" {
res = append(res, maskToAccess[access])
trans = append(trans, access)
}
}
if constraint != "file-log" {
transition := strings.Join(trans, "")
if len(transition) > 0 {
if slices.Contains(fileExecTransition, transition) {
res = append(res, transition)
} else {
panic("unrecognized pattern: " + transition)
}
}
}
return res
default:
res = strings.Fields(input)
slices.Sort(res)
return slices.Compact(res)
}
return mask
}

View File

@ -7,7 +7,7 @@ package aa
type Unix struct {
RuleBase
Qualifier
Access string
Access []string
Type string
Protocol string
Address string
@ -22,7 +22,7 @@ func newUnixFromLog(log map[string]string) Rule {
return &Unix{
RuleBase: newRuleFromLog(log),
Qualifier: newQualifierFromLog(log),
Access: toAccess(log["requested_mask"]),
Access: toAccess(tokUNIX, log["requested_mask"]),
Type: log["sock_type"],
Protocol: log["protocol"],
Address: log["addr"],
@ -36,8 +36,8 @@ func newUnixFromLog(log map[string]string) Rule {
func (r *Unix) Less(other any) bool {
o, _ := other.(*Unix)
if r.Access != o.Access {
return r.Access < o.Access
if len(r.Access) != len(o.Access) {
return len(r.Access) < len(o.Access)
}
if r.Type != o.Type {
return r.Type < o.Type
@ -68,7 +68,7 @@ func (r *Unix) Less(other any) bool {
func (r *Unix) Equals(other any) bool {
o, _ := other.(*Unix)
return r.Access == o.Access && r.Type == o.Type &&
return slices.Equal(r.Access, o.Access) && r.Type == o.Type &&
r.Protocol == o.Protocol && r.Address == o.Address &&
r.Label == o.Label && r.Attr == o.Attr && r.Opt == o.Opt &&
r.PeerLabel == o.PeerLabel && r.PeerAddr == o.PeerAddr &&

View File

@ -197,8 +197,8 @@ func (aaLogs AppArmorLogs) String() string {
}
// ParseToProfiles convert the log data into a new AppArmorProfiles
func (aaLogs AppArmorLogs) ParseToProfiles() aa.AppArmorProfileFiles {
profiles := make(aa.AppArmorProfileFiles, 0)
func (aaLogs AppArmorLogs) ParseToProfiles() map[string]*aa.Profile {
profiles := make(map[string]*aa.Profile, 0)
for _, log := range aaLogs {
name := ""
if strings.Contains(log["operation"], "dbus") {
@ -208,9 +208,7 @@ func (aaLogs AppArmorLogs) ParseToProfiles() aa.AppArmorProfileFiles {
}
if _, ok := profiles[name]; !ok {
profile := &aa.AppArmorProfileFile{
Profiles: []*aa.Profile{{Header: aa.Header{Name: name}}},
}
profile := &aa.Profile{Header: aa.Header{Name: name}}
profile.AddRule(log)
profiles[name] = profile
} else {

View File

@ -292,46 +292,42 @@ func TestAppArmorLogs_ParseToProfiles(t *testing.T) {
tests := []struct {
name string
aaLogs AppArmorLogs
want aa.AppArmorProfileFiles
want map[string]*aa.Profile
}{
{
name: "",
aaLogs: append(append(refKmod, refPowerProfiles...), refKmod...),
want: aa.AppArmorProfileFiles{
"kmod": &aa.AppArmorProfileFile{
Profiles: []*aa.Profile{{
Header: aa.Header{Name: "kmod"},
Rules: aa.Rules{
&aa.Unix{
RuleBase: aa.RuleBase{FileInherit: true},
Access: "send receive",
Type: "stream",
Protocol: "0",
},
&aa.Unix{
RuleBase: aa.RuleBase{FileInherit: true},
Access: "send receive",
Type: "stream",
Protocol: "0",
},
want: map[string]*aa.Profile{
"kmod": {
Header: aa.Header{Name: "kmod"},
Rules: aa.Rules{
&aa.Unix{
RuleBase: aa.RuleBase{FileInherit: true},
Access: []string{"receive", "send"},
Type: "stream",
Protocol: "0",
},
}},
&aa.Unix{
RuleBase: aa.RuleBase{FileInherit: true},
Access: []string{"receive", "send"},
Type: "stream",
Protocol: "0",
},
},
},
"power-profiles-daemon": &aa.AppArmorProfileFile{
Profiles: []*aa.Profile{{
Header: aa.Header{Name: "power-profiles-daemon"},
Rules: aa.Rules{
&aa.Dbus{
Access: "send",
Bus: "system",
Path: "/org/freedesktop/DBus",
Interface: "org.freedesktop.DBus",
Member: "AddMatch",
PeerName: "org.freedesktop.DBus",
PeerLabel: "dbus-daemon",
},
"power-profiles-daemon": {
Header: aa.Header{Name: "power-profiles-daemon"},
Rules: aa.Rules{
&aa.Dbus{
Access: []string{"send"},
Bus: "system",
Path: "/org/freedesktop/DBus",
Interface: "org.freedesktop.DBus",
Member: "AddMatch",
PeerName: "org.freedesktop.DBus",
PeerLabel: "dbus-daemon",
},
}},
},
},
},
},