mirror of
https://github.com/roddhjav/apparmor.d.git
synced 2024-11-14 23:43:56 +01:00
feat(build): simplify some internal tooling.
This commit is contained in:
parent
791459e39a
commit
1915fa5175
@ -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
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
|
||||
var (
|
||||
regDev = util.ToRegexRepl([]string{
|
||||
`Cx`, `cx`,
|
||||
`PUx`, `pux`,
|
||||
`Px`, `px`,
|
||||
`Ux`, `ux`,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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{}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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...`,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@ -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))
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user