Add install preflight safeguards (T-303)

This commit is contained in:
ila
2026-07-18 18:14:25 +08:00
parent 449b183ca3
commit ae3f64c407
8 changed files with 655 additions and 44 deletions
+39 -3
View File
@@ -34,9 +34,10 @@ var packageIDPattern = regexp.MustCompile(`^[a-z0-9-]+$`)
type PackageStage string
const (
PackageStageVerify PackageStage = "verify"
PackageStageManifest PackageStage = "manifest"
PackageStageExtract PackageStage = "extract"
PackageStageVerify PackageStage = "verify"
PackageStageManifest PackageStage = "manifest"
PackageStagePreflight PackageStage = "preflight"
PackageStageExtract PackageStage = "extract"
)
// PackageError preserves the underlying safe failure while making the package
@@ -74,6 +75,20 @@ type PackageExpectation struct {
App AppExpectation
}
// VerifiedPackage describes the payload plan after the download, ZIP layout,
// and app manifest have all been verified. It intentionally contains no ZIP
// handles or destination paths, so callers cannot bypass safe extraction.
type VerifiedPackage struct {
PayloadBytes int64
PayloadFiles int
Entrypoint string
}
// PreExtractCheck runs after package verification but before the extraction
// destination is created. It lets application code enforce environment
// preconditions without introducing application or platform dependencies here.
type PreExtractCheck func(VerifiedPackage) error
type packageAppManifest struct {
SchemaVersion int `json:"schema_version"`
ID string `json:"id"`
@@ -116,6 +131,18 @@ func (extractor Extractor) ExtractVerifiedFile(
zipPath string,
destination string,
expectation PackageExpectation,
) (ExtractResult, error) {
return extractor.ExtractVerifiedFileWithCheck(zipPath, destination, expectation, nil)
}
// ExtractVerifiedFileWithCheck preserves one file handle from Catalog
// size/SHA-256 verification through ZIP scanning, manifest comparison, an
// optional environment precheck, and safe extraction.
func (extractor Extractor) ExtractVerifiedFileWithCheck(
zipPath string,
destination string,
expectation PackageExpectation,
beforeExtract PreExtractCheck,
) (ExtractResult, error) {
expectedHash, err := expectation.validate()
if err != nil {
@@ -157,6 +184,15 @@ func (extractor Extractor) ExtractVerifiedFile(
if err := manifest.matches(expectation.App); err != nil {
return ExtractResult{}, packageError(PackageStageManifest, err)
}
if beforeExtract != nil {
verified, err := verifiedPackageFromPlan(plan, normalizedEntrypoint)
if err != nil {
return ExtractResult{}, packageError(PackageStageVerify, err)
}
if err := beforeExtract(verified); err != nil {
return ExtractResult{}, packageError(PackageStagePreflight, err)
}
}
result, err := extractor.extractPlan(destination, normalizedEntrypoint, plan)
if err != nil {
return ExtractResult{}, packageError(PackageStageExtract, err)