package archive import ( "bytes" "os" "path/filepath" "testing" ) func TestRoundTripDir(t *testing.T) { src := t.TempDir() mustWrite(t, filepath.Join(src, "a.txt"), "alpha") mustWrite(t, filepath.Join(src, "sub/b.txt"), "bravo") if err := os.MkdirAll(filepath.Join(src, "empty"), 0o755); err != nil { t.Fatal(err) } var buf bytes.Buffer if err := EncodeDir(&buf, src); err != nil { t.Fatalf("EncodeDir: %v", err) } dest := t.TempDir() if err := DecodeDir(&buf, dest); err != nil { t.Fatalf("DecodeDir: %v", err) } for _, c := range []struct{ rel, want string }{ {"a.txt", "alpha"}, {"sub/b.txt", "bravo"}, } { got, err := os.ReadFile(filepath.Join(dest, c.rel)) if err != nil { t.Errorf("read %s: %v", c.rel, err) continue } if string(got) != c.want { t.Errorf("%s = %q, want %q", c.rel, got, c.want) } } if _, err := os.Stat(filepath.Join(dest, "empty")); err != nil { t.Errorf("empty dir missing: %v", err) } } func TestDecodeRejectsPathEscape(t *testing.T) { // Hand-craft a tarball with a "../evil" entry, then zstd it via EncodeDir // indirection isn't feasible (EncodeDir won't emit ..). Instead, write // a tar.zst by hand with one bad header. t.Skip("path-escape rejection covered by safeJoin unit logic; would need a hand-crafted tar to assert at integration level") } func mustWrite(t *testing.T, path, body string) { t.Helper() if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { t.Fatal(err) } if err := os.WriteFile(path, []byte(body), 0o600); err != nil { t.Fatal(err) } } func TestSafeJoinRejectsEscape(t *testing.T) { base := "/tmp/abc" if _, err := safeJoin(base, "../etc/passwd"); err == nil { t.Error("expected escape rejection for ../etc/passwd") } if _, err := safeJoin(base, "sub/ok"); err != nil { t.Errorf("safe entry rejected: %v", err) } }