build(directive): support both liust & map.

This commit is contained in:
Alexandre Pujol 2024-03-23 17:41:10 +00:00
parent f81ceb9185
commit 88fcdd8c8e
Failed to generate hash of commit
10 changed files with 126 additions and 86 deletions

View file

@ -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],
}
}

View file

@ -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",
},
},
}

View file

@ -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 {

View file

@ -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{,/**}

View file

@ -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)

View file

@ -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,

View file

@ -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 {

View file

@ -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",

View file

@ -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)

View file

@ -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} {