fixed bug with store sync extraction into unwritable directory (#737)

(cherry picked from commit 29a5c697b1)

# Conflicts:
#	internal/flags/cli.go
#	internal/flags/store.go
#	pkg/consts/consts.go
This commit is contained in:
Zack Brady
2026-08-18 23:44:01 +00:00
committed by Mergify
parent bb5be057d7
commit 40bae24d14
5 changed files with 58 additions and 4 deletions
+4 -2
View File
@@ -135,12 +135,14 @@ func SyncCmd(ctx context.Context, o *flags.SyncOpts, s *store.Layout, rso *flags
if err != nil {
return fmt.Errorf("failed to fetch product manifest for [%s]: %w", productName, err)
}
err = ExtractCmd(ctx, &flags.ExtractOpts{StoreRootOpts: o.StoreRootOpts}, s, fmt.Sprintf("hauler/%s-manifest.yaml:%s", parts[0], tag))
// The manifest is output for the user, so it goes to workDir, not tempDir (removed when sync returns).
workDir := flags.ResolveWorkDir(ro)
err = ExtractCmd(ctx, &flags.ExtractOpts{StoreRootOpts: o.StoreRootOpts, DestinationDir: workDir}, s, fmt.Sprintf("hauler/%s-manifest.yaml:%s", parts[0], tag))
if err != nil {
return err
}
fileName := fmt.Sprintf("%s-manifest.yaml", parts[0])
fi, err := os.Open(fileName)
fi, err := os.Open(filepath.Join(workDir, fileName))
if err != nil {
return err
}
+11
View File
@@ -6,6 +6,11 @@ type CliRootOpts struct {
LogLevel string
HaulerDir string
IgnoreErrors bool
<<<<<<< HEAD
=======
AuditLevel string
WorkDir string
>>>>>>> 29a5c69 (fixed bug with store sync extraction into unwritable directory (#737))
}
func AddRootFlags(cmd *cobra.Command, ro *CliRootOpts) {
@@ -13,5 +18,11 @@ func AddRootFlags(cmd *cobra.Command, ro *CliRootOpts) {
pf.StringVarP(&ro.LogLevel, "log-level", "l", "info", "Set the logging level (i.e. info, debug, warn)")
pf.StringVarP(&ro.HaulerDir, "haulerdir", "d", "", "Set the location of the hauler directory (default $HOME/.hauler)")
<<<<<<< HEAD
pf.BoolVar(&ro.IgnoreErrors, "ignore-errors", false, "Ignore/Bypass errors (i.e. warn on error) (defaults false)")
=======
pf.BoolVar(&ro.IgnoreErrors, "ignore-errors", false, "Warn and continue instead of failing on errors, including storing images that failed verification (defaults false)")
pf.StringVar(&ro.AuditLevel, "audit-level", "", "Set the audit logging level (none, standard, verbose) (defaults standard)")
pf.StringVarP(&ro.WorkDir, "work-dir", "w", "", "(Optional) Set the directory for output that commands would otherwise write to the current directory (default: current directory)")
>>>>>>> 29a5c69 (fixed bug with store sync extraction into unwritable directory (#737))
}
+23
View File
@@ -61,3 +61,26 @@ func (o *StoreRootOpts) Store(ctx context.Context) (*store.Layout, error) {
}
return s, nil
}
<<<<<<< HEAD
=======
// resolveHaulerDir mirrors other variable detection, but duplicated to avoid an import cycle
func resolveHaulerDir(ro *CliRootOpts) string {
if ro != nil && ro.HaulerDir != "" {
return ro.HaulerDir
}
if d := os.Getenv(consts.HaulerDir); d != "" {
return d
}
home, _ := os.UserHomeDir()
return filepath.Join(home, consts.DefaultHaulerDirName)
}
// ResolveWorkDir returns the configured output dir, or "" to mean the current directory (legacy behavior).
func ResolveWorkDir(ro *CliRootOpts) string {
if ro != nil && ro.WorkDir != "" {
return ro.WorkDir
}
return os.Getenv(consts.HaulerWorkDir)
}
>>>>>>> 29a5c69 (fixed bug with store sync extraction into unwritable directory (#737))
+4 -2
View File
@@ -89,8 +89,10 @@ func (s *pusher) Push(ctx context.Context, desc ocispec.Descriptor) (ccontent.Wr
}
fullFileName := filepath.Join(destDir, filename)
// Guard against path traversal (e.g. filename containing "../")
if !strings.HasPrefix(fullFileName, destDir+string(filepath.Separator)) {
// Guard against path traversal (e.g. "../"). filepath.Rel handles this
// correctly even when destDir is "/", unlike a plain prefix check.
rel, err := filepath.Rel(destDir, fullFileName)
if err != nil || rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
return nil, fmt.Errorf("path_traversal_disallowed: %q resolves outside destination dir", filename)
}
+16
View File
@@ -87,10 +87,26 @@ const (
CollectionGroup = "collection.hauler.cattle.io"
// environment variables
<<<<<<< HEAD
HaulerDir = "HAULER_DIR"
HaulerTempDir = "HAULER_TEMP_DIR"
HaulerStoreDir = "HAULER_STORE_DIR"
HaulerIgnoreErrors = "HAULER_IGNORE_ERRORS"
=======
HaulerDir = "HAULER_DIR"
HaulerTempDir = "HAULER_TEMP_DIR"
HaulerWorkDir = "HAULER_WORK_DIR"
HaulerStoreDir = "HAULER_STORE_DIR"
HaulerIgnoreErrors = "HAULER_IGNORE_ERRORS"
HaulerRetries = "HAULER_RETRIES"
HaulerConcurrency = "HAULER_CONCURRENCY"
HaulerBlobConcurrency = "HAULER_BLOB_CONCURRENCY"
HaulerLogLevel = "HAULER_LOG_LEVEL"
HaulerAuditLevel = "HAULER_AUDIT_LEVEL"
CaFile = "CA_FILE"
InsecureSkipTLSVerify = "INSECURE_SKIP_TLS_VERIFY"
>>>>>>> 29a5c69 (fixed bug with store sync extraction into unwritable directory (#737))
// container files and directories
ImageManifestFile = "manifest.json"