package filestore import ( "bytes" "crypto/sha256" "encoding/hex" "errors" "os" "path/filepath" "testing" ) func TestStoreCommitsAndReusesImmutableFile(t *testing.T) { t.Parallel() store, err := New(t.TempDir()) if err != nil { t.Fatalf("create file store: %v", err) } content := []byte("signed update package") identity := identityOf(content) committed, err := store.Commit("transactions/one/package.zip", bytes.NewReader(content), identity) if err != nil { t.Fatalf("commit file: %v", err) } if committed.Reused { t.Fatal("new file reported as reused") } actual, err := os.ReadFile(committed.Path) if err != nil || !bytes.Equal(actual, content) { t.Fatalf("read committed file: content=%q err=%v", actual, err) } reused, err := store.Commit("transactions/one/package.zip", bytes.NewReader(content), identity) if err != nil { t.Fatalf("reuse committed file: %v", err) } if !reused.Reused || reused.Path != committed.Path { t.Fatalf("unexpected reused file: %+v", reused) } } func TestStoreRejectsMismatchAndNeverPublishesInvalidFile(t *testing.T) { t.Parallel() root := t.TempDir() store, err := New(root) if err != nil { t.Fatalf("create file store: %v", err) } content := []byte("actual") expected := identityOf([]byte("different")) expected.Size = int64(len(content)) _, err = store.Commit("package.zip", bytes.NewReader(content), expected) if err == nil { t.Fatal("SHA-256 mismatch was accepted") } if _, statErr := os.Stat(filepath.Join(root, "package.zip")); !errors.Is(statErr, os.ErrNotExist) { t.Fatalf("invalid destination was published: %v", statErr) } } func TestStoreRejectsNilSource(t *testing.T) { t.Parallel() store, err := New(t.TempDir()) if err != nil { t.Fatalf("create file store: %v", err) } if _, err := store.Commit("package.zip", nil, identityOf(nil)); err == nil { t.Fatal("nil file source was accepted") } } func TestStoreRejectsExistingDifferentContentAndPathEscape(t *testing.T) { t.Parallel() root := t.TempDir() store, err := New(root) if err != nil { t.Fatalf("create file store: %v", err) } if err := os.WriteFile(filepath.Join(root, "package.zip"), []byte("old"), 0o640); err != nil { t.Fatalf("seed destination: %v", err) } _, err = store.Commit("package.zip", bytes.NewReader([]byte("new")), identityOf([]byte("new"))) if !errors.Is(err, ErrDestinationConflict) { t.Fatalf("expected destination conflict, got %v", err) } _, err = store.Commit("../outside", bytes.NewReader(nil), identityOf(nil)) if err == nil { t.Fatal("path traversal was accepted") } } func TestStoreRejectsSymlinkedParentOutsideRoot(t *testing.T) { t.Parallel() root := t.TempDir() outside := t.TempDir() if err := os.Symlink(outside, filepath.Join(root, "linked")); err != nil { t.Fatalf("create parent symlink: %v", err) } store, err := New(root) if err != nil { t.Fatalf("create file store: %v", err) } _, err = store.Commit("linked/package.zip", bytes.NewReader(nil), identityOf(nil)) if err == nil { t.Fatal("symlinked parent outside root was accepted") } } func identityOf(content []byte) Identity { digest := sha256.Sum256(content) return Identity{Size: int64(len(content)), SHA256: hex.EncodeToString(digest[:])} }