Validate downloads before installing workspace files
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
@@ -101,10 +102,7 @@ func executeRestoreDownloadAction(
|
||||
return executeRestoreAudioAction(ctx, cfg, safeLocalPath, action, store)
|
||||
}
|
||||
|
||||
if err := fileops.EnsureWorkspaceDirectory(filepath.Dir(safeLocalPath)); err != nil {
|
||||
return fmt.Errorf("create destination directory: %w", err)
|
||||
}
|
||||
temporary, err := fileops.DownloadToSiblingTemp(safeLocalPath, func(destination io.Writer) error {
|
||||
temporaryPath, err := downloadRestoreTemp(safeLocalPath, func(destination io.Writer) error {
|
||||
if len(action.VerifiedContent) > 0 {
|
||||
_, err := io.Copy(destination, bytes.NewReader(action.VerifiedContent))
|
||||
return err
|
||||
@@ -114,13 +112,13 @@ func executeRestoreDownloadAction(
|
||||
if err != nil {
|
||||
return fmt.Errorf("download to temp file: %w", err)
|
||||
}
|
||||
defer func() { _ = temporary.Cleanup() }()
|
||||
if err := verifyRestoredObject(ctx, store, action, temporary); err != nil {
|
||||
defer func() { _ = os.Remove(temporaryPath) }()
|
||||
if err := verifyRestoredObject(ctx, store, action, temporaryPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isRestoreManifest(action.LocalRelativePath) {
|
||||
file, err := temporary.Open()
|
||||
file, err := os.Open(temporaryPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("open restored manifest: %w", err)
|
||||
}
|
||||
@@ -143,7 +141,7 @@ func executeRestoreDownloadAction(
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := temporary.Install(filepath.Base(safeLocalPath), fileops.WorkspaceFileMode); err != nil {
|
||||
if err := fileops.InstallSiblingTempFile(temporaryPath, safeLocalPath, fileops.WorkspaceFileMode); err != nil {
|
||||
return fmt.Errorf("install file atomically: %w", err)
|
||||
}
|
||||
|
||||
@@ -155,14 +153,42 @@ func isRestoreManifest(relativePath string) bool {
|
||||
return clean == config.PathManifestFile || clean == config.PathPreviousDirSegment+"/"+config.PathManifestFile
|
||||
}
|
||||
|
||||
func verifyRestoredObject(ctx context.Context, store storage.ObjectStore, action RestoreAction, temporary *fileops.DownloadedTempFile) error {
|
||||
func downloadRestoreTemp(destination string, download func(io.Writer) error) (string, error) {
|
||||
if strings.TrimSpace(destination) == "" {
|
||||
return "", fmt.Errorf("restore destination path is required")
|
||||
}
|
||||
if download == nil {
|
||||
return "", fmt.Errorf("restore temporary writer is required")
|
||||
}
|
||||
directory := filepath.Dir(destination)
|
||||
if err := fileops.EnsureWorkspaceDirectory(directory); err != nil {
|
||||
return "", fmt.Errorf("create destination directory: %w", err)
|
||||
}
|
||||
temporary, err := os.CreateTemp(directory, "."+filepath.Base(destination)+".tmp-*")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("create sibling temporary file: %w", err)
|
||||
}
|
||||
temporaryPath := temporary.Name()
|
||||
if err := download(temporary); err != nil {
|
||||
_ = temporary.Close()
|
||||
_ = os.Remove(temporaryPath)
|
||||
return "", err
|
||||
}
|
||||
if err := temporary.Close(); err != nil {
|
||||
_ = os.Remove(temporaryPath)
|
||||
return "", fmt.Errorf("close sibling temporary file: %w", err)
|
||||
}
|
||||
return temporaryPath, nil
|
||||
}
|
||||
|
||||
func verifyRestoredObject(ctx context.Context, store storage.ObjectStore, action RestoreAction, temporaryPath string) error {
|
||||
if strings.TrimSpace(action.SHA256) == "" && strings.TrimSpace(action.Generation) == "" {
|
||||
return nil
|
||||
}
|
||||
if strings.TrimSpace(action.SHA256) == "" || strings.TrimSpace(action.Generation) == "" {
|
||||
return fmt.Errorf("committed object identity for %q is incomplete", action.RemoteKey)
|
||||
}
|
||||
file, err := temporary.Open()
|
||||
file, err := os.Open(temporaryPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("open downloaded object for verification: %w", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user