Integrate verified installation flow (T-302)

This commit is contained in:
ila
2026-07-18 17:55:01 +08:00
parent 6575c9ad9b
commit 14589abb31
14 changed files with 1318 additions and 31 deletions
+63 -3
View File
@@ -33,6 +33,24 @@ type endOfCentralDirectory struct {
func (extractor Extractor) openAndScanArchive(
zipPath string,
expectedPackageSize int64,
) (*os.File, int64, error) {
file, size, err := extractor.openArchiveFile(zipPath, expectedPackageSize)
if err != nil {
return nil, 0, err
}
if err := extractor.scanOpenedArchive(file, size); err != nil {
_ = file.Close()
return nil, 0, err
}
return file, size, nil
}
// openArchiveFile opens the completed package once and proves the path still
// names that same ordinary file. Later verification and parsing must retain
// this handle rather than reopening zipPath.
func (extractor Extractor) openArchiveFile(
zipPath string,
expectedPackageSize int64,
) (*os.File, int64, error) {
if err := extractor.limits.validate(); err != nil {
return nil, 0, err
@@ -64,6 +82,17 @@ func (extractor Extractor) openAndScanArchive(
if !info.Mode().IsRegular() {
return closeWithError(fmt.Errorf("%w: opened package is not a regular file", ErrInvalidArchive))
}
if !os.SameFile(pathInfo, info) {
return closeWithError(fmt.Errorf("%w: package changed while opening", ErrInvalidArchive))
}
pathInfoAfterOpen, err := os.Lstat(zipPath)
if err != nil {
return closeWithError(fmt.Errorf("%w: recheck package: %v", ErrInvalidArchive, err))
}
if pathInfoAfterOpen.Mode()&os.ModeSymlink != 0 || !pathInfoAfterOpen.Mode().IsRegular() ||
!os.SameFile(info, pathInfoAfterOpen) {
return closeWithError(fmt.Errorf("%w: package changed while opening", ErrInvalidArchive))
}
size := info.Size()
if size != expectedPackageSize {
return closeWithError(fmt.Errorf(
@@ -81,12 +110,43 @@ func (extractor Extractor) openAndScanArchive(
extractor.limits.MaxArchiveBytes,
))
}
if err := scanCentralDirectory(file, size, extractor.limits); err != nil {
return closeWithError(err)
}
return file, size, nil
}
func (extractor Extractor) scanOpenedArchive(file *os.File, expectedPackageSize int64) error {
if file == nil {
return fmt.Errorf("%w: package handle is nil", ErrInvalidArchive)
}
if err := extractor.limits.validate(); err != nil {
return err
}
info, err := file.Stat()
if err != nil {
return fmt.Errorf("%w: stat package before ZIP scan: %v", ErrInvalidArchive, err)
}
if !info.Mode().IsRegular() {
return fmt.Errorf("%w: opened package is not a regular file", ErrInvalidArchive)
}
size := info.Size()
if size != expectedPackageSize {
return fmt.Errorf(
"%w: got %d, expected %d",
ErrArchiveSizeMismatch,
size,
expectedPackageSize,
)
}
if size > extractor.limits.MaxArchiveBytes {
return fmt.Errorf(
"%w: got %d, limit %d",
ErrArchiveTooLarge,
size,
extractor.limits.MaxArchiveBytes,
)
}
return scanCentralDirectory(file, size, extractor.limits)
}
func scanCentralDirectory(file *os.File, size int64, limits Limits) error {
end, err := findEndOfCentralDirectory(file, size)
if err != nil {