feat(build): simplify some internal tooling.

This commit is contained in:
Alexandre Pujol 2024-04-02 17:48:03 +01:00
parent 791459e39a
commit 1915fa5175
No known key found for this signature in database
GPG Key ID: C5469996F0DF68EC
14 changed files with 140 additions and 121 deletions

View File

@ -10,9 +10,13 @@ import (
"sort"
"strings"
"github.com/arduino/go-paths-helper"
"golang.org/x/exp/slices"
)
// Default Apparmor magic directory: /etc/apparmor.d/.
var MagicRoot = paths.New("/etc/apparmor.d")
// AppArmorProfiles represents a full set of apparmor profiles
type AppArmorProfiles map[string]*AppArmorProfile

View File

@ -11,16 +11,12 @@ import (
"regexp"
"strings"
"github.com/arduino/go-paths-helper"
"golang.org/x/exp/slices"
)
var (
regVariablesDef = regexp.MustCompile(`@{(.*)}\s*[+=]+\s*(.*)`)
regVariablesRef = regexp.MustCompile(`@{([^{}]+)}`)
// Default Apparmor magic directory: /etc/apparmor.d/.
MagicRoot = paths.New("/etc/apparmor.d")
)
type Variable struct {

View File

@ -11,6 +11,7 @@ import (
var (
regDev = util.ToRegexRepl([]string{
`Cx`, `cx`,
`PUx`, `pux`,
`Px`, `px`,
`Ux`, `ux`,

View File

@ -6,22 +6,9 @@ package cfg
import (
"strings"
)
// Filter out comments from a text configuration file
func filterComment(line string) (string, bool) {
if strings.HasPrefix(line, "#") || line == "" {
return "", true
}
if strings.Contains(line, "#") {
line = strings.Split(line, "#")[0]
line = strings.TrimSpace(line)
if line == "" {
return "", true
}
}
return line, false
}
"github.com/roddhjav/apparmor.d/pkg/util"
)
type Flagger struct{}
@ -32,12 +19,8 @@ func (f Flagger) Read(name string) map[string][]string {
return res
}
lines, _ := path.ReadFileAsLines()
lines := util.MustReadFileAsLines(path)
for _, line := range lines {
line, next := filterComment(line)
if next {
continue
}
manifest := strings.Split(line, " ")
profile := manifest[0]
flags := []string{}
@ -52,21 +35,11 @@ func (f Flagger) Read(name string) map[string][]string {
type Ignorer struct{}
func (i Ignorer) Read(name string) []string {
res := []string{}
path := IgnoreDir.Join(name + ".ignore")
if !path.Exist() {
return res
return []string{}
}
lines, _ := path.ReadFileAsLines()
for _, line := range lines {
line, next := filterComment(line)
if next {
continue
}
res = append(res, line)
}
return res
return util.MustReadFileAsLines(path)
}
type Overwriter struct {
@ -75,19 +48,11 @@ type Overwriter struct {
// Get the list of upstream profiles to overwrite from dist/overwrite
func (o Overwriter) Get() []string {
res := []string{}
lines, err := DistDir.Join("overwrite").ReadFileAsLines()
if err != nil {
panic(err)
path := DistDir.Join("overwrite")
if !path.Exist() {
return []string{}
}
for _, line := range lines {
line, next := filterComment(line)
if next {
continue
}
res = append(res, line)
}
return res
return util.MustReadFileAsLines(path)
}
// Overwrite upstream profile for APT: rename our profile & hide upstream

View File

@ -11,51 +11,6 @@ import (
"github.com/arduino/go-paths-helper"
)
func Test_filterComment(t *testing.T) {
tests := []struct {
name string
line string
wantLine string
wantNext bool
}{
{
name: "comment",
line: "# comment",
wantLine: "",
wantNext: true,
},
{
name: "comment with space",
line: " # comment",
wantLine: "",
wantNext: true,
},
{
name: "no comment",
line: "no comment",
wantLine: "no comment",
wantNext: false,
},
{
name: "no comment # comment",
line: "no comment # comment",
wantLine: "no comment",
wantNext: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotLine, gotNext := filterComment(tt.line)
if gotLine != tt.wantLine {
t.Errorf("filterComment() got = %v, want %v", gotLine, tt.wantLine)
}
if gotNext != tt.wantNext {
t.Errorf("filterComment() got1 = %v, want %v", gotNext, tt.wantNext)
}
})
}
}
func TestFlagger_Read(t *testing.T) {
tests := []struct {
name string

View File

@ -13,10 +13,10 @@ import (
"github.com/roddhjav/apparmor.d/pkg/prebuild/cfg"
)
// Define the directive keyword globally
const Keyword = "#aa:"
var (
// Define the directive keyword globally
Keyword = "#aa:"
// Build the profiles with the following directive applied
Directives = map[string]Directive{}

View File

@ -9,6 +9,7 @@ import (
"github.com/roddhjav/apparmor.d/pkg/aa"
"github.com/roddhjav/apparmor.d/pkg/prebuild/cfg"
"github.com/roddhjav/apparmor.d/pkg/util"
"golang.org/x/exp/slices"
)
@ -21,7 +22,7 @@ func init() {
Base: cfg.Base{
Keyword: "exec",
Msg: "Exec directive applied",
Help: `#aa:exec [P|U|p|u|PU|pu|] profiles...`,
Help: Keyword + `exec [P|U|p|u|PU|pu|] profiles...`,
},
})
}
@ -37,12 +38,7 @@ func (d Exec) Apply(opt *Option, profile string) string {
p := &aa.AppArmorProfile{}
for name := range opt.ArgMap {
content, err := cfg.RootApparmord.Join(name).ReadFile()
if err != nil {
panic(err)
}
profiletoTransition := string(content)
profiletoTransition := util.MustReadFile(cfg.RootApparmord.Join(name))
dstProfile := aa.DefaultTunables()
dstProfile.ParseVariables(profiletoTransition)
for _, variable := range dstProfile.Variables {

View File

@ -25,14 +25,14 @@ func init() {
Base: cfg.Base{
Keyword: "only",
Msg: "Only directive applied",
Help: `#aa:only filters...`,
Help: Keyword + `only filters...`,
},
})
RegisterDirective(&FilterExclude{
Base: cfg.Base{
Keyword: "exclude",
Msg: "Exclude directive applied",
Help: `#aa:exclude filters...`,
Help: Keyword + `exclude filters...`,
},
})
}

View File

@ -33,7 +33,7 @@ func init() {
Base: cfg.Base{
Keyword: "stack",
Msg: "Stack directive applied",
Help: `#aa:stack profiles...`,
Help: Keyword + `stack profiles...`,
},
})
}
@ -41,12 +41,7 @@ func init() {
func (s Stack) Apply(opt *Option, profile string) string {
res := ""
for name := range opt.ArgMap {
tmp, err := cfg.RootApparmord.Join(name).ReadFile()
if err != nil {
panic(err)
}
stackedProfile := string(tmp)
stackedProfile := util.MustReadFile(cfg.RootApparmord.Join(name))
m := regRules.FindStringSubmatch(stackedProfile)
if len(m) < 2 {
panic(fmt.Sprintf("No profile found in %s", name))

View File

@ -13,6 +13,7 @@ import (
"github.com/roddhjav/apparmor.d/pkg/prebuild/cfg"
"github.com/roddhjav/apparmor.d/pkg/prebuild/directive"
"github.com/roddhjav/apparmor.d/pkg/prebuild/prepare"
"github.com/roddhjav/apparmor.d/pkg/util"
)
func init() {
@ -65,11 +66,10 @@ func Build() error {
if !file.Exist() {
continue
}
content, err := file.ReadFile()
profile, err := util.ReadFile(file)
if err != nil {
return err
}
profile := string(content)
for _, b := range builder.Builds {
profile = b.Apply(profile)
}

View File

@ -10,6 +10,7 @@ import (
"strings"
"github.com/roddhjav/apparmor.d/pkg/prebuild/cfg"
"github.com/roddhjav/apparmor.d/pkg/util"
)
var (
@ -43,13 +44,13 @@ func (p SetFlags) Apply() ([]string, error) {
// Overwrite profile flags
if len(flags) > 0 {
flagsStr := " flags=(" + strings.Join(flags, ",") + ") {"
content, err := file.ReadFile()
out, err := util.ReadFile(file)
if err != nil {
return res, err
}
// Remove all flags definition, then set manifest' flags
out := regFlags.ReplaceAllLiteralString(string(content), "")
out = regFlags.ReplaceAllLiteralString(out, "")
out = regProfileHeader.ReplaceAllLiteralString(out, flagsStr)
if err := file.WriteFile([]byte(out)); err != nil {
return res, err

View File

@ -35,11 +35,11 @@ func (p FullSystemPolicy) Apply() ([]string, error) {
// Set systemd profile name
path := cfg.RootApparmord.Join("tunables/multiarch.d/system")
content, err := path.ReadFile()
out, err := util.ReadFile(path)
if err != nil {
return res, err
}
out := strings.Replace(string(content), "@{p_systemd}=unconfined", "@{p_systemd}=systemd", -1)
out = strings.Replace(out, "@{p_systemd}=unconfined", "@{p_systemd}=systemd", -1)
out = strings.Replace(out, "@{p_systemd_user}=unconfined", "@{p_systemd_user}=systemd-user", -1)
if err := path.WriteFile([]byte(out)); err != nil {
return res, err
@ -47,11 +47,10 @@ func (p FullSystemPolicy) Apply() ([]string, error) {
// Fix conflicting x modifiers in abstractions - FIXME: Temporary solution
path = cfg.RootApparmord.Join("abstractions/gstreamer")
content, err = path.ReadFile()
out, err = util.ReadFile(path)
if err != nil {
return res, err
}
out = string(content)
regFixConflictX := util.ToRegexRepl([]string{`.*gst-plugin-scanner.*`, ``})
out = regFixConflictX.Replace(out)
if err := path.WriteFile([]byte(out)); err != nil {

View File

@ -7,10 +7,20 @@ package util
import (
"encoding/hex"
"regexp"
"slices"
"strings"
"github.com/arduino/go-paths-helper"
)
var (
Comment = `#`
regFilter = ToRegexRepl([]string{
`\s*` + Comment + `.*`, ``,
`(?m)^(?:[\t\s]*(?:\r?\n|\r))+`, ``,
})
)
type RegexReplList []RegexRepl
type RegexRepl struct {
@ -40,7 +50,7 @@ func (rr RegexReplList) Replace(str string) string {
return str
}
// DecodeHexInString decode and replace all hex value in a given string constitued of "key=value".
// DecodeHexInString decode and replace all hex value in a given string of "key=value" format.
func DecodeHexInString(str string) string {
toDecode := []string{"name", "comm", "profile"}
for _, name := range toDecode {
@ -94,3 +104,37 @@ func CopyTo(src *paths.Path, dst *paths.Path) error {
}
return nil
}
// Filter out comments and empty line from a string
func Filter(src string) string {
return regFilter.Replace(src)
}
// ReadFile read a file and return its content as a string.
func ReadFile(path *paths.Path) (string, error) {
content, err := path.ReadFile()
if err != nil {
return "", err
}
return string(content), nil
}
// MustReadFile read a file and return its content as a string. Panic if an error occurs.
func MustReadFile(path *paths.Path) string {
content, err := path.ReadFile()
if err != nil {
panic(err)
}
return string(content)
}
// MustReadFileAsLines read a file and return its content as a slice of string.
// It panics if an error occurs and filter out comments and empty lines.
func MustReadFileAsLines(path *paths.Path) []string {
res := strings.Split(Filter(MustReadFile(path)), "\n")
if slices.Contains(res, "") {
idx := slices.Index(res, "")
res = slices.Delete(res, idx, idx+1)
}
return res
}

View File

@ -151,3 +151,66 @@ func TestCopyTo(t *testing.T) {
})
}
}
func Test_Filter(t *testing.T) {
tests := []struct {
name string
src string
want string
}{
{
name: "comment",
src: "# comment",
want: "",
},
{
name: "comment with space",
src: " # comment",
want: "",
},
{
name: "no comment",
src: "no comment",
want: "no comment",
},
{
name: "no comment # comment",
src: "no comment # comment",
want: "no comment",
},
{
name: "empty",
src: `
`,
want: ``,
},
{
name: "main",
src: `
# Common profile flags definition for all distributions
# File format: one profile by line using the format: '<profile> <flags>'
bwrap attach_disconnected,mediate_deleted,complain
bwrap-app attach_disconnected,complain
akonadi_akonotes_resource complain # Dev
gnome-disks complain
`,
want: `bwrap attach_disconnected,mediate_deleted,complain
bwrap-app attach_disconnected,complain
akonadi_akonotes_resource complain
gnome-disks complain
`,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotLine := Filter(tt.src)
if gotLine != tt.want {
t.Errorf("FilterComment() got = |%v|, want |%v|", gotLine, tt.want)
}
})
}
}