package main import ( "os" "path/filepath" "strings" "testing" ) func manifest(t *testing.T, root, module, body string) { t.Helper() dir := filepath.Join(root, "modules", module) if err := os.MkdirAll(dir, 0o755); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(dir, "module.json"), []byte(body), 0o644); err != nil { t.Fatal(err) } } func check(t *testing.T, root string) []Finding { t.Helper() if _, err := os.Stat(filepath.Join(root, SystemList)); os.IsNotExist(err) { lists(t, root, "/var/lib/pacman pacman: pacman -Qo\n", "") } found, err := Check(root, nil) if err != nil { t.Fatal(err) } return found } // only asserts exactly one finding, of the rule, naming the module and the resource (issue 1024). func only(t *testing.T, found []Finding, rule int, module, id string) { t.Helper() if len(found) != 1 || found[0].Rule != rule || found[0].Module != module || found[0].ID != id || found[0].Why == "" { t.Fatalf("found %+v; want one finding of rule %d for %s/%s with a reason", found, rule, module, id) } } // lists writes the catalogue's list of system directories and its known exceptions. func lists(t *testing.T, root, system, known string) { t.Helper() if err := os.MkdirAll(filepath.Join(root, "checks", "owned"), 0o755); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(root, SystemList), []byte(system), 0o644); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(root, SystemKnown), []byte(known), 0o644); err != nil { t.Fatal(err) } } func dir(id, path, rest string) string { return `{"id":"` + id + `","type":"directory","path":"` + path + `"` + rest + `}` } func mod(name string, resources ...string) string { return `{"module":"` + name + `","resources":[` + strings.Join(resources, ",") + `]}` } func TestMissingOrUnknownValueIsRefused(t *testing.T) { root := t.TempDir() manifest(t, root, "a", mod("a", dir("d", "/var/lib/a", ""))) only(t, check(t, root), 1, "a", "d") root = t.TempDir() manifest(t, root, "a", mod("a", `{"id":"z","type":"archive","path":"/opt/a","unknown":"delete"}`)) only(t, check(t, root), 1, "a", "z") } func TestMoveOutOutsideAHomeIsRefused(t *testing.T) { root := t.TempDir() manifest(t, root, "a", mod("a", dir("d", "/etc/a.d", `,"unknown":"move-out"`))) only(t, check(t, root), 2, "a", "d") } func TestMoveOutUnderAHomeIsAllowed(t *testing.T) { root := t.TempDir() manifest(t, root, "a", mod("a", dir("d", "${machine:account-home}/.config/a", `,"unknown":"move-out"`), dir("e", "${dir:d}/sub", `,"unknown":"move-out"`), dir("f", "${machine:agent-home}/x", `,"unknown":"move-out"`), dir("g", "/var/lib/a", `,"unknown":"program"`), dir("h", "${machine:account-home}/.ssh", `,"unknown":"module"`))) if found := check(t, root); len(found) != 0 { t.Fatalf("refused %+v", found) } } func TestBadKnowsIsRefused(t *testing.T) { for _, knows := range []string{`[""]`, `["/abs"]`, `["a/../b"]`, `["./a"]`, `["a//b"]`, `["a","a"]`, `["a","a/"]`} { root := t.TempDir() manifest(t, root, "a", mod("a", dir("d", "${machine:account-home}/.config/a", `,"unknown":"move-out","knows":`+knows))) only(t, check(t, root), 3, "a", "d") } root := t.TempDir() manifest(t, root, "a", mod("a", dir("d", "${machine:account-home}/.config/a", `,"unknown":"move-out","knows":["forge.credentials","kept/"]`))) if found := check(t, root); len(found) != 0 { t.Fatalf("refused %+v", found) } } func TestAnotherModulesPathInsideADirectoryIsRefused(t *testing.T) { root := t.TempDir() manifest(t, root, "owner", mod("owner", dir("ssh", "${machine:account-home}/.ssh", `,"unknown":"module"`))) manifest(t, root, "intruder", mod("intruder", `{"id":"f","type":"file","path":"${machine:account-home}/.ssh/config.d/20"}`)) manifest(t, root, "viahome", mod("viahome", `{"id":"g","type":"file","path":"${machine:agent-home}/.ssh/x"}`)) manifest(t, root, "facty", `{"module":"facty","facts":{"hosts":{"path":".ssh/config.d/00","home":true}}}`) manifest(t, root, "outside", mod("outside", `{"id":"o","type":"file","path":"${machine:account-home}/.sshrc"}`)) got := map[string]int{} for _, f := range check(t, root) { if f.Rule != 4 || f.Why == "" { t.Fatalf("finding %+v", f) } got[f.Module]++ } if len(got) != 3 || got["intruder"] != 1 || got["viahome"] != 1 || got["facty"] != 1 { t.Fatalf("refused %v; want intruder, viahome, facty", got) } } func TestDirReferencesAreResolvedPerModule(t *testing.T) { root := t.TempDir() manifest(t, root, "owner", mod("owner", dir("base", "${machine:account-home}/.config/o", `,"unknown":"move-out"`))) manifest(t, root, "other", mod("other", dir("base", "${machine:account-home}/.config/p", `,"unknown":"move-out"`), `{"id":"f","type":"file","path":"${dir:base}/x"}`)) if found := check(t, root); len(found) != 0 { t.Fatalf("refused %+v", found) } manifest(t, root, "third", mod("third", dir("d", "${machine:account-home}/.config/t", `,"unknown":"move-out"`), `{"id":"f","type":"file","path":"${dir:d}/../o/x"}`)) only(t, check(t, root), 4, "third", "f") } func TestAnArchiveIsNotAContainerAndSameModuleIsFine(t *testing.T) { root := t.TempDir() manifest(t, root, "a", mod("a", `{"id":"arc","type":"archive","path":"/opt/a","unknown":"program"}`, dir("d", "/opt/a2", `,"unknown":"program"`), `{"id":"f","type":"file","path":"/opt/a2/x"}`)) manifest(t, root, "b", mod("b", `{"id":"f","type":"file","path":"/opt/a/x"}`)) if found := check(t, root); len(found) != 0 { t.Fatalf("refused %+v", found) } } // Issue 1484: a directory the mesh places for a module (no path, `place`) is that module's own: another module's // directory of the same id, and a path under it, are not inside it. func TestPlacedDirectoriesOfTwoModulesAreTheirOwn(t *testing.T) { root := t.TempDir() for _, name := range []string{"first", "second"} { manifest(t, root, name, mod(name, `{"id":"state","type":"directory","place":".","unknown":"program"}`, dir("ssh", "${dir:state}/.ssh", `,"unknown":"program"`), `{"id":"conf","type":"file","path":"${dir:ssh}/config"}`)) } if found := check(t, root); len(found) != 0 { t.Fatalf("refused %+v", found) } } // 1841/AC18: a module's directory that resolves to a /var/lib directory a system package owns, lies inside one or // holds one, is refused, whether placed (place ".", or by default beneath the module's own) or stated; a placed // directory saying place "mesh" lies under /var/lib/mesh and passes (issue 1841: pacman's state was /var/lib/pacman). func TestADirectoryOfASystemPackageIsRefused(t *testing.T) { system := "/var/lib/pacman pacman: pacman -Qo\n/var/lib/systemd/coredump systemd: tmpfiles.d/systemd.conf\n" for name, resources := range map[string][]string{ "placed-own": {`{"id":"state","type":"directory","place":".","unknown":"program"}`}, "placed-beneath": {`{"id":"coredump","type":"directory","unknown":"program"}`}, "stated-inside": {dir("d", "/var/lib/pacman/keeper", `,"unknown":"program"`)}, "stated-holding": {dir("d", "/var/lib/systemd", `,"unknown":"program"`)}, "file-inside": {`{"id":"state","type":"directory","place":"mesh","unknown":"program"}`, `{"id":"f","type":"file","path":"/var/lib/pacman/keeper.json"}`}, } { module := map[string]string{"placed-own": "pacman", "placed-beneath": "systemd"}[name] if module == "" { module = "keeper" } root := t.TempDir() lists(t, root, system, "") manifest(t, root, module, mod(module, resources...)) found := check(t, root) if len(found) != 1 || found[0].Rule != 5 || found[0].Module != module || !strings.Contains(found[0].Why, "/var/lib/") || !strings.Contains(found[0].Why, "package") { t.Errorf("%s: found %+v; want one finding of rule 5 for %s naming the path and the package", name, found, module) } } root := t.TempDir() lists(t, root, system, "") manifest(t, root, "pacman", mod("pacman", `{"id":"state","type":"directory","place":"mesh","unknown":"program"}`, `{"id":"f","type":"file","path":"${dir:state}/keeper.json"}`, dir("other", "/var/lib/pacman-other", `,"unknown":"program"`))) if found := check(t, root); len(found) != 0 { t.Fatalf("a directory under /var/lib/mesh/pacman was refused: %+v", found) } } // 1841/AC18: a known exception passes; one that no longer collides is refused, so the list only shrinks; one the trunk's // copy does not hold is refused; and an absent list of system directories fails the check rather than passing it. func TestKnownSystemExceptionsOnlyShrink(t *testing.T) { system := "/var/lib/systemd systemd: tmpfiles.d/systemd.conf\n" known := "# comment\nsystemd state issue 1873\n" root := t.TempDir() lists(t, root, system, known) manifest(t, root, "systemd", mod("systemd", `{"id":"state","type":"directory","place":".","unknown":"program"}`)) if found := check(t, root); len(found) != 0 { t.Fatalf("a known exception was refused: %+v", found) } if found, err := Check(root, []string{"systemd state issue 1873"}); err != nil || len(found) != 0 { t.Fatalf("a known exception held by the trunk was refused: %+v %v", found, err) } if found, err := Check(root, []string{}); err != nil || len(found) != 1 || found[0].Rule != 5 { t.Fatalf("an exception the trunk does not hold passed: %+v %v", found, err) } manifest(t, root, "systemd", mod("systemd", `{"id":"state","type":"directory","place":"mesh","unknown":"program"}`)) only(t, check(t, root), 5, "systemd", "state") root = t.TempDir() manifest(t, root, "a", mod("a")) if err := os.MkdirAll(filepath.Join(root, "checks", "owned"), 0o755); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(root, SystemList), []byte("# nothing\n"), 0o644); err != nil { t.Fatal(err) } if _, err := Check(root, nil); err == nil { t.Fatal("an empty list of system directories passed") } } // 1841/AC18: the catalogue as it stands passes rule 5 with its own lists. func TestTheCatalogueKeepsOutOfSystemDirectories(t *testing.T) { found, err := Check("../..", nil) if err != nil { t.Fatal(err) } for _, f := range found { if f.Rule == 5 { t.Errorf("%s: %s: %s", f.Module, f.ID, f.Why) } } }