diff --git a/cmd/aa-log/main.go b/cmd/aa-log/main.go index f8cf0509..32b9ee8d 100644 --- a/cmd/aa-log/main.go +++ b/cmd/aa-log/main.go @@ -68,8 +68,9 @@ func aaLog(logger string, path string, profile string) error { if rules { profiles := aaLogs.ParseToProfiles() for _, profile := range profiles { - profile.Sort() profile.MergeRules() + profile.Sort() + profile.Format() fmt.Print(profile.String() + "\n") } } else { diff --git a/pkg/aa/file.go b/pkg/aa/file.go index e304d822..13d4dc73 100644 --- a/pkg/aa/file.go +++ b/pkg/aa/file.go @@ -4,10 +4,6 @@ package aa -import ( - "strings" -) - type File struct { Qualifier Path string @@ -26,28 +22,19 @@ func FileFromLog(log map[string]string) ApparmorRule { func (r *File) Less(other any) bool { o, _ := other.(*File) - letterR := "" - letterO := "" - for _, letter := range fileAlphabet { - if strings.HasPrefix(r.Path, letter) { - letterR = letter - } - if strings.HasPrefix(o.Path, letter) { - letterO = letter - } - } - + letterR := getLetterIn(fileAlphabet, r.Path) + letterO := getLetterIn(fileAlphabet, o.Path) if fileWeights[letterR] == fileWeights[letterO] || letterR == "" || letterO == "" { - if r.Path == o.Path { - if r.Qualifier.Equals(o.Qualifier) { + if r.Qualifier.Equals(o.Qualifier) { + if r.Path == o.Path { if r.Access == o.Access { return r.Target < o.Target } return r.Access < o.Access } - return r.Qualifier.Less(o.Qualifier) + return r.Path < o.Path } - return r.Path < o.Path + return r.Qualifier.Less(o.Qualifier) } return fileWeights[letterR] < fileWeights[letterO] } diff --git a/pkg/aa/profile.go b/pkg/aa/profile.go index 68576680..1aa881a2 100644 --- a/pkg/aa/profile.go +++ b/pkg/aa/profile.go @@ -116,11 +116,8 @@ func (p *AppArmorProfile) AddRule(log map[string]string) { } } -func typeToValue(i reflect.Type) string { - return strings.ToLower(strings.TrimPrefix(i.String(), "*aa.")) -} - // Sort the rules in the profile +// Follow: https://apparmor.pujol.io/development/guidelines/#guidelines func (p *AppArmorProfile) Sort() { sort.Slice(p.Rules, func(i, j int) bool { typeOfI := reflect.TypeOf(p.Rules[i]) @@ -163,3 +160,33 @@ func (p *AppArmorProfile) MergeRules() { } } } + +// Format the profile for better readability before printing it +// Follow: https://apparmor.pujol.io/development/guidelines/#the-file-block +func (p *AppArmorProfile) Format() { + hasOwnedRule := false + for i := len(p.Rules) - 1; i > 0; i-- { + j := i - 1 + typeOfI := reflect.TypeOf(p.Rules[i]) + typeOfJ := reflect.TypeOf(p.Rules[j]) + + // File rule + if typeOfI == reflect.TypeOf((*File)(nil)) && typeOfJ == reflect.TypeOf((*File)(nil)) { + letterI := getLetterIn(fileAlphabet, p.Rules[i].(*File).Path) + letterJ := getLetterIn(fileAlphabet, p.Rules[j].(*File).Path) + + // Add prefix before rule path to align with other rule + if p.Rules[i].(*File).Owner { + hasOwnedRule = true + } else if hasOwnedRule { + p.Rules[i].(*File).Prefix = " " + } + + if letterI != letterJ { + // Add a new empty line between Files rule of different type + hasOwnedRule = false + p.Rules = append(p.Rules[:i], append([]ApparmorRule{&Rule{}}, p.Rules[i:]...)...) + } + } + } +} diff --git a/pkg/aa/rules.go b/pkg/aa/rules.go index 977c57e1..92f352bf 100644 --- a/pkg/aa/rules.go +++ b/pkg/aa/rules.go @@ -13,6 +13,8 @@ type Qualifier struct { Owner bool NoNewPrivs bool FileInherit bool + Prefix string + Padding string } func NewQualifierFromLog(log map[string]string) Qualifier { @@ -47,13 +49,13 @@ func NewQualifierFromLog(log map[string]string) Qualifier { } func (r Qualifier) Less(other Qualifier) bool { - if r.Audit == other.Audit { - if r.AccessType == other.AccessType { - return r.Owner + if r.Owner == other.Owner { + if r.Audit == other.Audit { + return r.AccessType < other.AccessType } - return r.AccessType < other.AccessType + return r.Audit } - return r.Audit + return other.Owner } func (r Qualifier) Equals(other Qualifier) bool { diff --git a/pkg/aa/template.go b/pkg/aa/template.go index cbac0461..f11900c7 100644 --- a/pkg/aa/template.go +++ b/pkg/aa/template.go @@ -142,6 +142,10 @@ func typeOf(i any) string { return strings.TrimPrefix(reflect.TypeOf(i).String(), "*aa.") } +func typeToValue(i reflect.Type) string { + return strings.ToLower(strings.TrimPrefix(i.String(), "*aa.")) +} + func indent(s string) string { return indentation + s } @@ -149,3 +153,12 @@ func indent(s string) string { func indentDbus(s string) string { return indentation + " " + s } + +func getLetterIn(alphabet []string, in string) string { + for _, letter := range alphabet { + if strings.HasPrefix(in, letter) { + return letter + } + } + return "" +} diff --git a/pkg/aa/templates/profile.j2 b/pkg/aa/templates/profile.j2 index 47f49055..5a6f816d 100644 --- a/pkg/aa/templates/profile.j2 +++ b/pkg/aa/templates/profile.j2 @@ -19,10 +19,7 @@ {{ end -}} {{- range .Variables -}} - {{ "@{" }}{{ .Name }}{{ "} = " }} - {{- range .Values -}} - {{ . }}{{ " " }} - {{- end }} + {{ "@{" }}{{ .Name }}{{ "} = " }}{{ join .Values }} {{ end -}} {{- "profile" -}} @@ -43,6 +40,10 @@ {{- $oldtype := "" -}} {{- range .Rules -}} {{- $type := typeof . -}} + {{- if eq $type "Rule" -}} + {{- "\n" -}} + {{- continue -}} + {{- end -}} {{- if and (ne $type $oldtype) (ne $oldtype "") -}} {{- "\n" -}} {{- end -}} @@ -217,7 +218,12 @@ {{- if eq $type "File" -}} {{- template "qualifier" . -}} - {{ .Path }}{{ " " }}{{ .Access }} + {{- .Path -}} + {{- " " -}} + {{- with .Padding -}} + {{ . }} + {{- end -}} + {{- .Access -}} {{- with .Target -}} {{ " -> " }}{{ . }} {{- end -}} diff --git a/pkg/aa/templates/qualifier.j2 b/pkg/aa/templates/qualifier.j2 index afe1e0ed..929cc8ed 100644 --- a/pkg/aa/templates/qualifier.j2 +++ b/pkg/aa/templates/qualifier.j2 @@ -1,4 +1,7 @@ {{- define "qualifier" -}} + {{- with .Prefix -}} + {{ . }} + {{- end -}} {{- if .Owner -}} {{- "owner " -}} {{- end -}}