// apparmor.d - Full set of apparmor profiles // Copyright (C) 2023 Alexandre Pujol // SPDX-License-Identifier: GPL-2.0-only package main import ( "flag" "fmt" "os" "path/filepath" "strings" "github.com/arduino/go-paths-helper" intg "github.com/roddhjav/apparmor.d/pkg/integration" "github.com/roddhjav/apparmor.d/pkg/logging" ) const usage = `aa-test [-h] [--bootstrap | --run | --list] Integration tests manager tool for apparmor.d Options: -h, --help Show this help message and exit. -b, --bootstrap Bootstrap test scenarios using tldr pages. -r, --run Run a predefined list of tests. -l, --list List the configured tests. -f, --file FILE Set a scenario test file. Default: tests/scenarios.yml ` const defaultScenariosFile = "scenarios.yml" var ( help bool bootstrap bool run bool list bool path string Cfg Config ) type Config struct { TldrDir *paths.Path // Default: tests/tldr ScenariosDir *paths.Path // Default: tests ScenariosFile *paths.Path // Default: tests/tldr.yml ProfilesDir *paths.Path // Default: /etc/apparmor.d Profiles paths.PathList } func NewConfig() Config { cfg := Config{ TldrDir: paths.New("tests/tldr"), ScenariosDir: paths.New("tests/"), ProfilesDir: paths.New("/etc/apparmor.d"), Profiles: paths.PathList{}, } cfg.ScenariosFile = cfg.ScenariosDir.Join("tldr.yml") return cfg } func init() { Cfg = NewConfig() files, _ := Cfg.ProfilesDir.ReadDir(paths.FilterOutDirectories()) for _, path := range files { Cfg.Profiles.Add(path) } flag.BoolVar(&help, "h", false, "Show this help message and exit.") flag.BoolVar(&help, "help", false, "Show this help message and exit.") flag.BoolVar(&bootstrap, "b", false, "Bootstrap test scenarios using tldr pages.") flag.BoolVar(&bootstrap, "bootstrap", false, "Bootstrap test scenarios using tldr pages.") flag.BoolVar(&run, "r", false, "Run a predefined list of tests.") flag.BoolVar(&run, "run", false, "Run a predefined list of tests.") flag.BoolVar(&list, "l", false, "List the test to run.") flag.BoolVar(&list, "list", false, "List the test to run.") flag.StringVar(&path, "f", defaultScenariosFile, "Set a scenario test file.") flag.StringVar(&path, "file", defaultScenariosFile, "Set a scenario test file.") } func apparmorTestBootstrap() error { tldr := intg.NewTldr(Cfg.TldrDir) if err := tldr.Download(); err != nil { return err } tSuite, err := tldr.Parse(Cfg.Profiles) if err != nil { return err } // Default bootstraped scenarios file if err := tSuite.Write(Cfg.ScenariosFile); err != nil { return err } logging.Bullet("Default scenarios saved: %s", Cfg.ScenariosFile) // Scenarios file with only profiled programs tSuiteWithProfile := intg.NewTestSuite() for _, s := range tSuite.Scenarios { if s.Profiled { tSuiteWithProfile.Scenarios = append(tSuiteWithProfile.Scenarios, s) } } scnPath := Cfg.ScenariosDir.Join(strings.TrimSuffix(path, filepath.Ext(path)) + ".new.yml") if err := tSuiteWithProfile.Write(scnPath); err != nil { return err } logging.Bullet("Scenarios with profile saved: %s", scnPath) logging.Bullet("Number of scenarios found %d", len(tSuite.Scenarios)) logging.Bullet("Number of scenarios with profiles in apparmor.d %d", len(tSuiteWithProfile.Scenarios)) return nil } func apparmorTestRun(dryRun bool) error { // FIXME: Safety settings. For default scenario set with sudo enabled the apparmor.d folder gets removed. dryRun = true if dryRun { logging.Step("List tests") } else { logging.Step("Run tests") } tSuite := intg.NewTestSuite() scnPath := Cfg.ScenariosDir.Join(path) if err := tSuite.ReadScenarios(scnPath); err != nil { return err } cfgPath := Cfg.ScenariosDir.Join("integration.yml") if err := tSuite.ReadSettings(cfgPath); err != nil { return err } intg.Arguments = tSuite.Arguments intg.Ignore = tSuite.Ignore nbTest := 0 nbScn := 0 for _, scn := range tSuite.Scenarios { ran, nb, err := scn.Run(dryRun) nbScn += ran nbTest += nb if err != nil { return err } } if dryRun { logging.Bullet("Number of scenarios to run %d", nbScn) logging.Bullet("Number of tests to run %d", nbTest) } else { logging.Success("Number of scenarios ran %d", nbScn) logging.Success("Number of tests to ran %d", nbTest) } return nil } func main() { flag.Usage = func() { fmt.Print(usage) } flag.Parse() if help { flag.Usage() os.Exit(0) } var err error if bootstrap { logging.Step("Bootstraping tests") err = apparmorTestBootstrap() } else if run || list { err = apparmorTestRun(list) } else { flag.Usage() os.Exit(1) } if err != nil { logging.Fatal(err.Error()) } }