feat(build): add new filter directives.

This commit is contained in:
Alexandre Pujol 2024-03-21 20:51:42 +00:00
parent 2ca62215bc
commit 83691bbb1f
No known key found for this signature in database
GPG key ID: C5469996F0DF68EC
2 changed files with 207 additions and 0 deletions

View file

@ -0,0 +1,75 @@
// apparmor.d - Full set of apparmor profiles
// Copyright (C) 2021-2024 Alexandre Pujol <alexandre@pujol.io>
// SPDX-License-Identifier: GPL-2.0-only
package directive
import (
"regexp"
"slices"
"strings"
oss "github.com/roddhjav/apparmor.d/pkg/os"
"golang.org/x/exp/maps"
)
type FilterOnly struct {
DirectiveBase
}
type FilterExclude struct {
DirectiveBase
}
func init() {
Directives["only"] = &FilterOnly{
DirectiveBase: DirectiveBase{
message: "Only directive applied",
usage: `#aa:only <dist or familly>`,
},
}
Directives["exclude"] = &FilterExclude{
DirectiveBase: DirectiveBase{
message: "Exclude directive applied",
usage: `#aa:exclude <dist or familly>`,
},
}
}
func filterRuleForUs(opt *Option) bool {
return slices.Contains(maps.Keys(opt.Args), oss.Distribution) || slices.Contains(maps.Keys(opt.Args), oss.Family)
}
func filter(only bool, opt *Option, profile string) string {
if only && filterRuleForUs(opt) {
return profile
}
if !only && !filterRuleForUs(opt) {
return profile
}
inline := true
tmp := strings.Split(opt.Raw, Keyword)
if len(tmp) >= 1 {
left := strings.TrimSpace(tmp[0])
if len(left) == 0 {
inline = false
}
}
if inline {
profile = strings.Replace(profile, opt.Raw, "", -1)
} else {
regRemoveParagraph := regexp.MustCompile(`(?s)` + opt.Raw + `\n.*?\n\n`)
profile = regRemoveParagraph.ReplaceAllString(profile, "")
}
return profile
}
func (d FilterOnly) Apply(opt *Option, profile string) string {
return filter(true, opt, profile)
}
func (d FilterExclude) Apply(opt *Option, profile string) string {
return filter(false, opt, profile)
}

View file

@ -0,0 +1,132 @@
// apparmor.d - Full set of apparmor profiles
// Copyright (C) 2021-2024 Alexandre Pujol <alexandre@pujol.io>
// SPDX-License-Identifier: GPL-2.0-only
package directive
import (
"testing"
oss "github.com/roddhjav/apparmor.d/pkg/os"
)
func TestFilterOnly_Apply(t *testing.T) {
tests := []struct {
name string
dist string
family string
opt *Option
profile string
want string
}{
{
name: "inline",
dist: "debian",
family: "apt",
opt: &Option{
Name: "only",
Args: map[string]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",
},
{
name: "paragraph",
dist: "arch",
family: "pacman",
opt: &Option{
Name: "only",
Args: map[string]string{"zypper": ""},
File: nil,
Raw: " #aa:only zypper",
},
profile: `
/tmp/apt-changelog-@{rand6}/ w,
/tmp/apt-changelog-@{rand6}/*.changelog rw,
owner /tmp/alpm_*/{,**} rw,
owner /tmp/apt-changelog-@{rand6}/.apt-acquire-privs-test.@{rand6} rw,
owner /tmp/packagekit* rw,
@{run}/systemd/inhibit/*.ref rw,
owner @{run}/systemd/users/@{uid} r,
#aa:only zypper
@{run}/zypp.pid rwk,
owner @{run}/zypp-rpm.pid rwk,
owner @{run}/zypp/packages/ r,
owner /dev/shm/AP_0x@{rand6}/{,**} rw,
owner /dev/shm/ r,`,
want: `
/tmp/apt-changelog-@{rand6}/ w,
/tmp/apt-changelog-@{rand6}/*.changelog rw,
owner /tmp/alpm_*/{,**} rw,
owner /tmp/apt-changelog-@{rand6}/.apt-acquire-privs-test.@{rand6} rw,
owner /tmp/packagekit* rw,
@{run}/systemd/inhibit/*.ref rw,
owner @{run}/systemd/users/@{uid} r,
owner /dev/shm/AP_0x@{rand6}/{,**} rw,
owner /dev/shm/ r,`,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
oss.Distribution = tt.dist
oss.Family = tt.family
if got := Directives["only"].Apply(tt.opt, tt.profile); got != tt.want {
t.Errorf("FilterOnly.Apply() = %v, want %v", got, tt.want)
}
})
}
}
func TestFilterExclude_Apply(t *testing.T) {
tests := []struct {
name string
dist string
family string
opt *Option
profile string
want string
}{
{
name: "inline",
dist: "debian",
family: "apt",
opt: &Option{
Name: "exclude",
Args: map[string]string{"debian": ""},
File: nil,
Raw: " @{bin}/dpkg rPx -> child-dpkg, #aa:exclude debian",
},
profile: " @{bin}/dpkg rPx -> child-dpkg, #aa:exclude debian",
want: "",
},
{
name: "inline-keep",
dist: "whonix",
family: "apt",
opt: &Option{
Name: "exclude",
Args: map[string]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",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
oss.Distribution = tt.dist
oss.Family = tt.family
if got := Directives["exclude"].Apply(tt.opt, tt.profile); got != tt.want {
t.Errorf("FilterExclude.Apply() = %v, want %v", got, tt.want)
}
})
}
}