fix(auth): close Task 15 review round two

This commit is contained in:
2026-08-18 07:21:24 +02:00
parent 225ffc8e20
commit fe190e7046
10 changed files with 305 additions and 64 deletions
+42 -3
View File
@@ -82,6 +82,8 @@ type PreflightResult struct {
Manifest Manifest
Entries []ArchiveEntryMetadata
archive *verifiedArchive
stagingRoot string
freeBytes func(string) (uint64, error)
}
type verifiedArchive struct {
@@ -189,7 +191,8 @@ func Preflight(ctx context.Context, installation config.Installation, request Pr
return PreflightResult{}, errors.New("restore staging requirement exceeds supported size")
}
requiredWithStaging := requiredBytes + stagingBytes
freeBytes, err := dependencies.FreeBytes(installation.ProjectDirectory)
stagingCapacityPath := installation.ControlDirectory()
freeBytes, err := dependencies.FreeBytes(stagingCapacityPath)
if err != nil {
return PreflightResult{}, fmt.Errorf("check free disk space: %w", err)
}
@@ -219,7 +222,9 @@ func Preflight(ctx context.Context, installation config.Installation, request Pr
return PreflightResult{
ArchivePath: archivePath, ArchiveSize: openedInfo.Size(), RequiredBytes: requiredBytes,
Manifest: manifest, Entries: metadata,
archive: &verifiedArchive{file: archiveFile, info: openedInfo, digest: finalDigest, limits: limits},
archive: &verifiedArchive{file: archiveFile, info: openedInfo, digest: finalDigest, limits: limits},
stagingRoot: filepath.Join(stagingCapacityPath, "restore-staging"),
freeBytes: dependencies.FreeBytes,
}, nil
}
@@ -259,7 +264,20 @@ func (result PreflightResult) StageArchive(ctx context.Context) (_ *stagedArchiv
if err != nil {
return nil, err
}
directory, err := os.MkdirTemp("", "tht-restore-stage-")
if result.stagingRoot == "" || result.freeBytes == nil {
return nil, errors.New("backup archive has no controlled staging reservation")
}
if err := ensurePrivateStagingRoot(result.stagingRoot); err != nil {
return nil, fmt.Errorf("create private restore staging root: %w", err)
}
freeBytes, err := result.freeBytes(result.stagingRoot)
if err != nil {
return nil, errors.New("check private restore staging capacity")
}
if result.ArchiveSize < 0 || freeBytes < uint64(result.ArchiveSize) {
return nil, errors.New("insufficient free disk space for private restore staging archive")
}
directory, err := os.MkdirTemp(result.stagingRoot, "archive-")
if err != nil {
return nil, errors.New("create private restore staging directory")
}
@@ -324,6 +342,27 @@ func (result PreflightResult) StageArchive(ctx context.Context) (_ *stagedArchiv
return staged, nil
}
func ensurePrivateStagingRoot(root string) error {
if !filepath.IsAbs(root) || filepath.Clean(root) != root {
return errors.New("restore staging root is invalid")
}
if err := os.Mkdir(root, 0o700); err != nil && !errors.Is(err, os.ErrExist) {
return errors.New("restore staging root is unavailable")
}
info, err := os.Lstat(root)
if err != nil || !info.IsDir() || info.Mode()&os.ModeSymlink != 0 {
return errors.New("restore staging root is unsafe")
}
if err := os.Chmod(root, 0o700); err != nil {
return errors.New("restore staging root cannot be protected")
}
info, err = os.Lstat(root)
if err != nil || !info.IsDir() || info.Mode()&os.ModeSymlink != 0 || info.Mode().Perm() != 0o700 {
return errors.New("restore staging root protection is invalid")
}
return nil
}
// Close removes only the staging file and directory created by StageArchive.
func (staged *stagedArchive) Close() error {
if staged == nil {