package host import ( "golang.org/x/sys/unix" "os" "path/filepath" "testing" "github.com/carlosframework/platform/internal/rootstate" ) func TestPlan14ProofBindingProvenance(t *testing.T) { if !plan14RootFixture(t) { return } for _, attack := range []string{"nonroot owner", "writable parent", "writable file", "symlink", "rename", "erasure"} { t.Run(attack, func(t *testing.T) { e, rt, _, source := plan14ProofFixture(t) path := source.sys.(*fakeUnits).shows[0].FragmentPath makeProofAncestorsPublic(t, path) makeProofAncestorsPublic(t, source.sys.(*fakeUnits).exe[7]) source.openFile = rootstate.OpenTrustedFile baseline, err := source.resolve(t.Context(), rt) if err != nil { t.Fatalf("trusted baseline: %v", err) } switch attack { case "nonroot owner": if err := unix.Mount("/foreign", path, "", unix.MS_BIND, ""); err != nil { t.Fatal(err) } t.Cleanup(func() { _ = unix.Unmount(path, 0) }) case "writable parent": if err := os.Chmod(filepath.Dir(path), 0777); err != nil { t.Fatal(err) } case "writable file": if err := os.Chmod(path, 0666); err != nil { t.Fatal(err) } case "symlink": if err := os.Rename(path, path+".real"); err != nil { t.Fatal(err) } if err := os.Symlink(path+".real", path); err != nil { t.Fatal(err) } case "erasure": if err := os.Remove(path); err != nil { t.Fatal(err) } case "rename": raw, err := os.ReadFile(path) if err != nil { t.Fatal(err) } if err := os.Rename(path, path+".old"); err != nil { t.Fatal(err) } if err := os.WriteFile(path, raw, 0644); err != nil { t.Fatal(err) } } if attack == "rename" { current, err := source.resolve(t.Context(), rt) if err != nil { t.Fatal(err) } if current.Source == baseline.Source { t.Fatal("replacement retained identity") } if _, err := source.prepareRollback(t.Context(), baseline); err == nil { t.Fatal("replacement accepted captured identity") } } else if _, err := source.resolve(t.Context(), rt); err == nil { t.Fatal("mutation accepted") } _ = e }) } }