diff --git a/pkg/prebuild/directive/core.go b/pkg/prebuild/directive/core.go index 59316950..207d8231 100644 --- a/pkg/prebuild/directive/core.go +++ b/pkg/prebuild/directive/core.go @@ -42,30 +42,33 @@ func (d *DirectiveBase) Message() string { // Directive options type Option struct { - Name string - Args map[string]string - File *paths.Path - Raw string + Name string + ArgMap map[string]string + ArgList []string + File *paths.Path + Raw string } func NewOption(file *paths.Path, match []string) *Option { if len(match) != 3 { panic(fmt.Sprintf("Invalid directive: %v", match)) } - args := map[string]string{} - for _, t := range strings.Fields(match[2]) { + argList := strings.Fields(match[2]) + argMap := map[string]string{} + for _, t := range argList { tmp := strings.Split(t, "=") if len(tmp) < 2 { - args[tmp[0]] = "" + argMap[tmp[0]] = "" } else { - args[tmp[0]] = tmp[1] + argMap[tmp[0]] = tmp[1] } } return &Option{ - Name: match[1], - Args: args, - File: file, - Raw: match[0], + Name: match[1], + ArgMap: argMap, + ArgList: argList, + File: file, + Raw: match[0], } } diff --git a/pkg/prebuild/directive/core_test.go b/pkg/prebuild/directive/core_test.go index fe05dc17..28d45d84 100644 --- a/pkg/prebuild/directive/core_test.go +++ b/pkg/prebuild/directive/core_test.go @@ -11,6 +11,32 @@ import ( "github.com/arduino/go-paths-helper" ) +func TestDirective_Usage(t *testing.T) { + tests := []struct { + name string + d Directive + wantMessage string + wantUsage string + }{ + { + name: "empty", + d: Directives["stack"], + wantMessage: "Stack directive applied", + wantUsage: `#aa:stack profiles_name...`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.d.Usage(); got != tt.wantUsage { + t.Errorf("Directive.Usage() = %v, want %v", got, tt.wantUsage) + } + if got := tt.d.Message(); got != tt.wantMessage { + t.Errorf("Directive.Usage() = %v, want %v", got, tt.wantMessage) + } + }) + } +} + func TestNewOption(t *testing.T) { tests := []struct { name string @@ -28,13 +54,14 @@ func TestNewOption(t *testing.T) { }, want: &Option{ Name: "dbus", - Args: map[string]string{ + ArgMap: map[string]string{ "bus": "system", "name": "org.gnome.DisplayManager", "own": "", }, - File: paths.New(""), - Raw: " #aa:dbus own bus=system name=org.gnome.DisplayManager", + ArgList: []string{"own", "bus=system", "name=org.gnome.DisplayManager"}, + File: nil, + Raw: " #aa:dbus own bus=system name=org.gnome.DisplayManager", }, }, { @@ -46,10 +73,11 @@ func TestNewOption(t *testing.T) { "opensuse", }, want: &Option{ - Name: "only", - Args: map[string]string{"opensuse": ""}, - File: paths.New(""), - Raw: " #aa:only opensuse", + Name: "only", + ArgMap: map[string]string{"opensuse": ""}, + ArgList: []string{"opensuse"}, + File: nil, + Raw: " #aa:only opensuse", }, }, } diff --git a/pkg/prebuild/directive/dbus.go b/pkg/prebuild/directive/dbus.go index 3e9c289d..9b107b27 100644 --- a/pkg/prebuild/directive/dbus.go +++ b/pkg/prebuild/directive/dbus.go @@ -50,21 +50,13 @@ func setInterfaces(rules map[string]string) []string { func (d Dbus) Apply(opt *Option, profile string) string { var p *aa.AppArmorProfile - var action string - if _, ok := opt.Args["own"]; ok { - action = "own" - } else if _, ok := opt.Args["talk"]; ok { - action = "talk" - } else { - panic(fmt.Sprintf("Unknown dbus action: %s in %s", opt.Name, opt.File)) - } - d.sanityCheck(action, opt) + action := d.sanityCheck(opt) switch action { case "own": - p = d.own(opt.Args) + p = d.own(opt.ArgMap) case "talk": - p = d.talk(opt.Args) + p = d.talk(opt.ArgMap) } generatedDbus := p.String() @@ -74,22 +66,31 @@ func (d Dbus) Apply(opt *Option, profile string) string { return profile } -func (d Dbus) sanityCheck(action string, opt *Option) { - if _, present := opt.Args["name"]; !present { +func (d Dbus) sanityCheck(opt *Option) string { + if len(opt.ArgList) < 1 { + panic(fmt.Sprintf("Unknown dbus action: %s in %s", opt.Name, opt.File)) + } + action := opt.ArgList[0] + if action != "own" && action != "talk" { + panic(fmt.Sprintf("Unknown dbus action: %s in %s", opt.Name, opt.File)) + } + + if _, present := opt.ArgMap["name"]; !present { panic(fmt.Sprintf("Missing name for 'dbus: %s' in %s", action, opt.File)) } - if _, present := opt.Args["bus"]; !present { - panic(fmt.Sprintf("Missing bus for '%s' in %s", opt.Args["name"], opt.File)) + if _, present := opt.ArgMap["bus"]; !present { + panic(fmt.Sprintf("Missing bus for '%s' in %s", opt.ArgMap["name"], opt.File)) } - if _, present := opt.Args["label"]; !present && action == "talk" { - panic(fmt.Sprintf("Missing label for '%s' in %s", opt.Args["name"], opt.File)) + if _, present := opt.ArgMap["label"]; !present && action == "talk" { + panic(fmt.Sprintf("Missing label for '%s' in %s", opt.ArgMap["name"], opt.File)) } // Set default values - if _, present := opt.Args["path"]; !present { - opt.Args["path"] = "/" + strings.Replace(opt.Args["name"], ".", "/", -1) + "{,/**}" + if _, present := opt.ArgMap["path"]; !present { + opt.ArgMap["path"] = "/" + strings.Replace(opt.ArgMap["name"], ".", "/", -1) + "{,/**}" } - opt.Args["name"] += "{,.*}" + opt.ArgMap["name"] += "{,.*}" + return action } func (d Dbus) own(rules map[string]string) *aa.AppArmorProfile { diff --git a/pkg/prebuild/directive/dbus_test.go b/pkg/prebuild/directive/dbus_test.go index 9d04ecb3..6d7c0594 100644 --- a/pkg/prebuild/directive/dbus_test.go +++ b/pkg/prebuild/directive/dbus_test.go @@ -43,13 +43,14 @@ func TestDbus_Apply(t *testing.T) { name: "own", opt: &Option{ Name: "dbus", - Args: map[string]string{ + ArgMap: map[string]string{ "bus": "system", "name": "org.freedesktop.systemd1", "own": "", }, - File: nil, - Raw: " #aa:dbus own bus=system name=org.freedesktop.systemd1", + ArgList: []string{"own", "bus=system", "name=org.freedesktop.systemd1"}, + File: nil, + Raw: " #aa:dbus own bus=system name=org.freedesktop.systemd1", }, profile: " #aa:dbus own bus=system name=org.freedesktop.systemd1", want: dbusOwnSystemd1, @@ -58,14 +59,15 @@ func TestDbus_Apply(t *testing.T) { name: "own-interface", opt: &Option{ Name: "dbus", - Args: map[string]string{ + ArgMap: map[string]string{ "bus": "session", "name": "com.rastersoft.dingextension", "interface": "org.gtk.Actions", "own": "", }, - File: nil, - Raw: " #aa:dbus own bus=session name=com.rastersoft.dingextension interface=org.gtk.Actions", + ArgList: []string{"own", "bus=session", "name=com.rastersoft.dingextension", "interface=org.gtk.Actions"}, + File: nil, + Raw: " #aa:dbus own bus=session name=com.rastersoft.dingextension interface=org.gtk.Actions", }, profile: " #aa:dbus own bus=session name=com.rastersoft.dingextension interface=org.gtk.Actions", want: ` dbus bind bus=session name=com.rastersoft.dingextension{,.*}, @@ -102,14 +104,15 @@ func TestDbus_Apply(t *testing.T) { name: "talk", opt: &Option{ Name: "dbus", - Args: map[string]string{ + ArgMap: map[string]string{ "bus": "system", "name": "org.freedesktop.Accounts", "label": "accounts-daemon", "talk": "", }, - File: nil, - Raw: " #aa:dbus talk bus=system name=org.freedesktop.Accounts label=accounts-daemon", + ArgList: []string{"talk", "bus=system", "name=org.freedesktop.Accounts", "label=accounts-daemon"}, + File: nil, + Raw: " #aa:dbus talk bus=system name=org.freedesktop.Accounts label=accounts-daemon", }, profile: " #aa:dbus talk bus=system name=org.freedesktop.Accounts label=accounts-daemon", want: ` dbus send bus=system path=/org/freedesktop/Accounts{,/**} diff --git a/pkg/prebuild/directive/exec.go b/pkg/prebuild/directive/exec.go index a792a263..bd1e5d37 100644 --- a/pkg/prebuild/directive/exec.go +++ b/pkg/prebuild/directive/exec.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/roddhjav/apparmor.d/pkg/aa" + "golang.org/x/exp/slices" ) type Exec struct { @@ -26,16 +27,14 @@ func init() { func (d Exec) Apply(opt *Option, profile string) string { transition := "Px" transitions := []string{"P", "U", "p", "u", "PU", "pu"} - for _, t := range transitions { - if _, present := opt.Args[t]; present { - transition = t + "x" - delete(opt.Args, t) - break - } + t := opt.ArgList[0] + if slices.Contains(transitions, t) { + transition = t + "x" + delete(opt.ArgMap, t) } p := &aa.AppArmorProfile{} - for name := range opt.Args { + for name := range opt.ArgMap { content, err := rootApparmord.Join(name).ReadFile() if err != nil { panic(err) diff --git a/pkg/prebuild/directive/exec_test.go b/pkg/prebuild/directive/exec_test.go index 5c2c2534..d5dcc60e 100644 --- a/pkg/prebuild/directive/exec_test.go +++ b/pkg/prebuild/directive/exec_test.go @@ -22,10 +22,11 @@ func TestExec_Apply(t *testing.T) { name: "exec", rootApparmord: paths.New("../../../apparmor.d/groups/kde/"), opt: &Option{ - Name: "exec", - Args: map[string]string{"DiscoverNotifier": ""}, - File: nil, - Raw: " #aa:exec DiscoverNotifier", + Name: "exec", + ArgMap: map[string]string{"DiscoverNotifier": ""}, + ArgList: []string{"DiscoverNotifier"}, + File: nil, + Raw: " #aa:exec DiscoverNotifier", }, profile: ` #aa:exec DiscoverNotifier`, want: ` @{lib}/@{multiarch}/{,libexec/}DiscoverNotifier Px, @@ -35,10 +36,11 @@ func TestExec_Apply(t *testing.T) { name: "exec-unconfined", rootApparmord: paths.New("../../../apparmor.d/groups/freedesktop/"), opt: &Option{ - Name: "exec", - Args: map[string]string{"U": "", "polkit-agent-helper": ""}, - File: nil, - Raw: " #aa:exec U polkit-agent-helper", + Name: "exec", + ArgMap: map[string]string{"U": "", "polkit-agent-helper": ""}, + ArgList: []string{"U", "polkit-agent-helper"}, + File: nil, + Raw: " #aa:exec U polkit-agent-helper", }, profile: ` #aa:exec U polkit-agent-helper`, want: ` @{lib}/polkit-[0-9]/polkit-agent-helper-[0-9] Ux, diff --git a/pkg/prebuild/directive/filter.go b/pkg/prebuild/directive/filter.go index 60249b1b..496358b8 100644 --- a/pkg/prebuild/directive/filter.go +++ b/pkg/prebuild/directive/filter.go @@ -9,7 +9,6 @@ import ( "strings" oss "github.com/roddhjav/apparmor.d/pkg/os" - "golang.org/x/exp/maps" "golang.org/x/exp/slices" ) @@ -37,7 +36,7 @@ func init() { } func filterRuleForUs(opt *Option) bool { - return slices.Contains(maps.Keys(opt.Args), oss.Distribution) || slices.Contains(maps.Keys(opt.Args), oss.Family) + return slices.Contains(opt.ArgList, oss.Distribution) || slices.Contains(opt.ArgList, oss.Family) } func filter(only bool, opt *Option, profile string) string { diff --git a/pkg/prebuild/directive/filter_test.go b/pkg/prebuild/directive/filter_test.go index 5dbd6fc8..2ff756c9 100644 --- a/pkg/prebuild/directive/filter_test.go +++ b/pkg/prebuild/directive/filter_test.go @@ -24,10 +24,11 @@ func TestFilterOnly_Apply(t *testing.T) { dist: "debian", family: "apt", opt: &Option{ - Name: "only", - Args: map[string]string{"apt": ""}, - File: nil, - Raw: " @{bin}/arch-audit rPx, #aa:only apt", + Name: "only", + ArgMap: map[string]string{"apt": ""}, + ArgList: []string{"apt"}, + File: nil, + Raw: " @{bin}/arch-audit rPx, #aa:only apt", }, profile: " @{bin}/arch-audit rPx, #aa:only apt", want: " @{bin}/arch-audit rPx, #aa:only apt", @@ -37,10 +38,11 @@ func TestFilterOnly_Apply(t *testing.T) { dist: "arch", family: "pacman", opt: &Option{ - Name: "only", - Args: map[string]string{"zypper": ""}, - File: nil, - Raw: " #aa:only zypper", + Name: "only", + ArgMap: map[string]string{"zypper": ""}, + ArgList: []string{"zypper"}, + File: nil, + Raw: " #aa:only zypper", }, profile: ` /tmp/apt-changelog-@{rand6}/ w, @@ -98,10 +100,11 @@ func TestFilterExclude_Apply(t *testing.T) { dist: "debian", family: "apt", opt: &Option{ - Name: "exclude", - Args: map[string]string{"debian": ""}, - File: nil, - Raw: " @{bin}/dpkg rPx -> child-dpkg, #aa:exclude debian", + Name: "exclude", + ArgMap: map[string]string{"debian": ""}, + ArgList: []string{"debian"}, + File: nil, + Raw: " @{bin}/dpkg rPx -> child-dpkg, #aa:exclude debian", }, profile: " @{bin}/dpkg rPx -> child-dpkg, #aa:exclude debian", want: "", @@ -111,10 +114,11 @@ func TestFilterExclude_Apply(t *testing.T) { dist: "whonix", family: "apt", opt: &Option{ - Name: "exclude", - Args: map[string]string{"debian": ""}, - File: nil, - Raw: " @{bin}/dpkg rPx -> child-dpkg, #aa:exclude debian", + Name: "exclude", + ArgMap: map[string]string{"debian": ""}, + ArgList: []string{"debian"}, + File: nil, + Raw: " @{bin}/dpkg rPx -> child-dpkg, #aa:exclude debian", }, profile: " @{bin}/dpkg rPx -> child-dpkg, #aa:exclude debian", want: " @{bin}/dpkg rPx -> child-dpkg, #aa:exclude debian", diff --git a/pkg/prebuild/directive/stack.go b/pkg/prebuild/directive/stack.go index 9c79fc80..fc1ac487 100644 --- a/pkg/prebuild/directive/stack.go +++ b/pkg/prebuild/directive/stack.go @@ -41,7 +41,7 @@ func init() { func (s Stack) Apply(opt *Option, profile string) string { res := "" - for name := range opt.Args { + for name := range opt.ArgMap { tmp, err := rootApparmord.Join(name).ReadFile() if err != nil { panic(err) diff --git a/pkg/prebuild/directive/stack_test.go b/pkg/prebuild/directive/stack_test.go index 6b6880ae..07df8fe9 100644 --- a/pkg/prebuild/directive/stack_test.go +++ b/pkg/prebuild/directive/stack_test.go @@ -22,10 +22,11 @@ func TestStack_Apply(t *testing.T) { name: "stack", rootApparmord: paths.New("../../../apparmor.d/groups/freedesktop/"), opt: &Option{ - Name: "stack", - Args: map[string]string{"plymouth": ""}, - File: nil, - Raw: " #aa:stack plymouth", + Name: "stack", + ArgMap: map[string]string{"plymouth": ""}, + ArgList: []string{"plymouth"}, + File: nil, + Raw: " #aa:stack plymouth", }, profile: ` profile parent @{exec_path} {