apparmor.d/tests/cmd/tests.go
2024-10-21 14:13:25 +01:00

115 lines
2.3 KiB
Go

// apparmor.d - Full set of apparmor profiles
// Copyright (C) 2023-2024 Alexandre Pujol <alexandre@pujol.io>
// SPDX-License-Identifier: GPL-2.0-only
package main
import (
"html/template"
"os/exec"
"slices"
"strings"
"github.com/roddhjav/apparmor.d/pkg/aa"
"github.com/roddhjav/apparmor.d/pkg/paths"
)
const tmplTest = `#!/usr/bin/env bats
# apparmor.d - Full set of apparmor profiles
# Copyright (C) 2024 Alexandre Pujol <alexandre@pujol.io>
# SPDX-License-Identifier: GPL-2.0-only
load common
setup_file() {
aa_setup
}
{{ $name := .Name -}}
{{ range .Commands }}
# bats test_tags={{ $name }}
@test "{{ $name }}: {{ .Description }}" {
{{ .Cmd }}
aa_check
}
{{ end }}
`
var (
Profiles = getProfiles() // List of profiles in apparmor.d
tmpl = template.Must(template.New("bats").Parse(tmplTest))
)
type Tests []Test
// Filter returns a new list of tests with only the ones that have a profile
func (t Tests) Filter() Tests {
for i := len(t) - 1; i >= 0; i-- {
if !t[i].HasProfile() {
t = slices.Delete(t, i, i+1)
}
}
return t
}
// Test represents of a list of tests for a given program
type Test struct {
Name string
Commands []Command
}
// Command is a command line to run as part of a test
type Command struct {
Description string
Cmd string
}
// HasProfile returns true if the program in the scenario is profiled in apparmor.d
func (t Test) HasProfile() bool {
return slices.Contains(Profiles, t.Name)
}
// IsInstalled returns true if the program in the scenario is installed on the system
func (t Test) IsInstalled() bool {
if _, err := exec.LookPath(t.Name); err != nil {
return false
}
return true
}
func (t Test) Write(dir *paths.Path) error {
if !t.HasProfile() {
return nil
}
path := dir.Join(t.Name + ".bats")
if paths.New("tests/bats").Join(t.Name + ".bats").Exist() {
path = dir.Join("00." + t.Name + ".bats")
}
content := renderBatsFile(t)
if err := path.WriteFile([]byte(content)); err != nil {
return err
}
return nil
}
func renderBatsFile(data any) string {
var res strings.Builder
err := tmpl.Execute(&res, data)
if err != nil {
panic(err)
}
return res.String()
}
func getProfiles() []string {
p := []string{}
files, err := aa.MagicRoot.ReadDir(paths.FilterOutDirectories())
if err != nil {
panic(err)
}
for _, path := range files {
p = append(p, path.Base())
}
return p
}