From 2ca62215bcd358ecd8fe1fc579c5b2b9bc1a2256 Mon Sep 17 00:00:00 2001 From: Alexandre Pujol Date: Thu, 21 Mar 2024 20:36:41 +0000 Subject: [PATCH] build: prepare new structure for directives. --- cmd/prebuild/main.go | 16 ++++-- pkg/prebuild/directive/core.go | 82 +++++++++++++++++++++++++++++ pkg/prebuild/directive/core_test.go | 63 ++++++++++++++++++++++ pkg/prebuild/prebuild.go | 1 + 4 files changed, 159 insertions(+), 3 deletions(-) create mode 100644 pkg/prebuild/directive/core.go create mode 100644 pkg/prebuild/directive/core_test.go diff --git a/cmd/prebuild/main.go b/cmd/prebuild/main.go index d4f376a2..68840387 100644 --- a/cmd/prebuild/main.go +++ b/cmd/prebuild/main.go @@ -12,11 +12,13 @@ import ( "github.com/roddhjav/apparmor.d/pkg/logging" oss "github.com/roddhjav/apparmor.d/pkg/os" "github.com/roddhjav/apparmor.d/pkg/prebuild" + "github.com/roddhjav/apparmor.d/pkg/prebuild/directive" ) -const usage = `prebuild [-h] [--full] [--complain | --enforce] +const usage = `prebuild [-h] [--full] [--complain | --enforce] [profiles...] - Prebuild apparmor.d profiles for a given distribution. + Prebuild apparmor.d profiles for a given distribution and apply + internal built-in directives. Options: -h, --help Show this help message and exit. @@ -24,6 +26,8 @@ Options: -c, --complain Set complain flag on all profiles. -e, --enforce Set enforce flag on all profiles. --abi4 Convert the profiles to Apparmor abi/4.0. + +Directives: ` var ( @@ -73,7 +77,13 @@ func aaPrebuild() error { } func main() { - flag.Usage = func() { fmt.Print(usage) } + flag.Usage = func() { + res := usage + for _, d := range directive.Directives { + res += ` ` + d.Usage() + "\n" + } + fmt.Print(res) + } flag.Parse() if help { flag.Usage() diff --git a/pkg/prebuild/directive/core.go b/pkg/prebuild/directive/core.go new file mode 100644 index 00000000..59316950 --- /dev/null +++ b/pkg/prebuild/directive/core.go @@ -0,0 +1,82 @@ +// apparmor.d - Full set of apparmor profiles +// Copyright (C) 2021-2024 Alexandre Pujol +// SPDX-License-Identifier: GPL-2.0-only + +package directive + +import ( + "fmt" + "regexp" + "strings" + + "github.com/arduino/go-paths-helper" +) + +// Define the directive keyword globally +const Keyword = "#aa:" + +// Build the profiles with the following directive applied +var Directives = map[string]Directive{} + +var regDirective = regexp.MustCompile(`(?m).*` + Keyword + `([a-z]*) (.*)`) + +// Main directive interface +type Directive interface { + Usage() string + Message() string + Apply(opt *Option, profile string) string +} + +type DirectiveBase struct { + message string + usage string +} + +func (d *DirectiveBase) Usage() string { + return d.usage +} + +func (d *DirectiveBase) Message() string { + return d.message +} + +// Directive options +type Option struct { + Name string + Args map[string]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]) { + tmp := strings.Split(t, "=") + if len(tmp) < 2 { + args[tmp[0]] = "" + } else { + args[tmp[0]] = tmp[1] + } + } + return &Option{ + Name: match[1], + Args: args, + File: file, + Raw: match[0], + } +} + +func Run(file *paths.Path, profile string) string { + for _, match := range regDirective.FindAllStringSubmatch(profile, -1) { + opt := NewOption(file, match) + drtv, ok := Directives[opt.Name] + if !ok { + panic(fmt.Sprintf("Unknown directive: %s", opt.Name)) + } + profile = drtv.Apply(opt, profile) + } + return profile +} diff --git a/pkg/prebuild/directive/core_test.go b/pkg/prebuild/directive/core_test.go new file mode 100644 index 00000000..f07e3e33 --- /dev/null +++ b/pkg/prebuild/directive/core_test.go @@ -0,0 +1,63 @@ +// apparmor.d - Full set of apparmor profiles +// Copyright (C) 2021-2024 Alexandre Pujol +// SPDX-License-Identifier: GPL-2.0-only + +package directive + +import ( + "reflect" + "testing" + + "github.com/arduino/go-paths-helper" +) + +func TestNewOption(t *testing.T) { + tests := []struct { + name string + file *paths.Path + match []string + want *Option + }{ + { + name: "dbus", + file: nil, + match: []string{ + " #aa:dbus own bus=system name=org.gnome.DisplayManager", + "dbus", + "own bus=system name=org.gnome.DisplayManager", + }, + want: &Option{ + Name: "dbus", + Args: map[string]string{ + "bus": "system", + "name": "org.gnome.DisplayManager", + "own": "", + }, + File: paths.New(""), + Raw: " #aa:dbus own bus=system name=org.gnome.DisplayManager", + }, + }, + { + name: "only", + file: nil, + match: []string{ + " #aa:only opensuse", + "only", + "opensuse", + }, + want: &Option{ + Name: "only", + Args: map[string]string{"opensuse": ""}, + File: paths.New(""), + Raw: " #aa:only opensuse", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := NewOption(tt.file, tt.match); !reflect.DeepEqual(got, tt.want) { + t.Errorf("NewOption() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/pkg/prebuild/prebuild.go b/pkg/prebuild/prebuild.go index 99d94d03..c87d21cc 100644 --- a/pkg/prebuild/prebuild.go +++ b/pkg/prebuild/prebuild.go @@ -89,6 +89,7 @@ func Build() error { for _, fct := range Directives { profile = fct(file, profile) } + profile = directive.Run(file, profile) if err := file.WriteFile([]byte(profile)); err != nil { return err }