diff --git a/node/bundle.go b/node/bundle.go index 8c48f57..d1da15e 100644 --- a/node/bundle.go +++ b/node/bundle.go @@ -237,15 +237,15 @@ func createTarball(files map[string][]byte) ([]byte, error) { Size: int64(len(content)), } if err := tw.WriteHeader(hdr); err != nil { - return nil, err + return nil, coreerr.E("createTarball", "failed to write tar header", err) } if _, err := tw.Write(content); err != nil { - return nil, err + return nil, coreerr.E("createTarball", "failed to write tar content", err) } } if err := tw.Close(); err != nil { - return nil, err + return nil, coreerr.E("createTarball", "failed to close tar writer", err) } return buf.Bytes(), nil @@ -261,11 +261,11 @@ func extractTarball(tarData []byte, destDir string) (string, error) { absDestDir = filepath.Clean(absDestDir) if err := coreio.Local.EnsureDir(absDestDir); err != nil { - return "", err + return "", coreerr.E("extractTarball", "failed to ensure destination directory", err) } tr := tar.NewReader(bytes.NewReader(tarData)) - var firstExecutable string + var firstExecutablePath string for { hdr, err := tr.Next() @@ -273,7 +273,7 @@ func extractTarball(tarData []byte, destDir string) (string, error) { break } if err != nil { - return "", err + return "", coreerr.E("extractTarball", "failed to read tar entry", err) } // Security: Sanitize the tar entry name to prevent path traversal (Zip Slip) @@ -301,12 +301,12 @@ func extractTarball(tarData []byte, destDir string) (string, error) { switch hdr.Typeflag { case tar.TypeDir: if err := coreio.Local.EnsureDir(fullPath); err != nil { - return "", err + return "", coreerr.E("extractTarball", "failed to create directory "+cleanName, err) } case tar.TypeReg: // Ensure parent directory exists if err := coreio.Local.EnsureDir(filepath.Dir(fullPath)); err != nil { - return "", err + return "", coreerr.E("extractTarball", "failed to create parent directory for "+cleanName, err) } // os.OpenFile is used deliberately here instead of coreio.Local.Create/Write @@ -321,18 +321,24 @@ func extractTarball(tarData []byte, destDir string) (string, error) { const maxFileSize int64 = 100 * 1024 * 1024 limitedReader := io.LimitReader(tr, maxFileSize+1) written, err := io.Copy(f, limitedReader) - f.Close() if err != nil { + _ = f.Close() return "", coreerr.E("extractTarball", "failed to write file "+hdr.Name, err) } + if err := f.Close(); err != nil { + return "", coreerr.E("extractTarball", "failed to close extracted file "+hdr.Name, err) + } + if written > maxFileSize { - coreio.Local.Delete(fullPath) + if err := coreio.Local.Delete(fullPath); err != nil { + return "", coreerr.E("extractTarball", "failed to clean up oversized file "+hdr.Name, err) + } return "", coreerr.E("extractTarball", "file "+hdr.Name+" exceeds maximum size", nil) } // Track first executable - if hdr.Mode&0111 != 0 && firstExecutable == "" { - firstExecutable = fullPath + if hdr.Mode&0111 != 0 && firstExecutablePath == "" { + firstExecutablePath = fullPath } // Explicitly ignore symlinks and hard links to prevent symlink attacks case tar.TypeSymlink, tar.TypeLink: @@ -341,13 +347,17 @@ func extractTarball(tarData []byte, destDir string) (string, error) { } } - return firstExecutable, nil + return firstExecutablePath, nil } // StreamBundle writes a bundle to a writer (for large transfers). func StreamBundle(bundle *Bundle, w io.Writer) error { encoder := json.NewEncoder(w) - return encoder.Encode(bundle) + if err := encoder.Encode(bundle); err != nil { + return coreerr.E("StreamBundle", "failed to encode bundle", err) + } + + return nil } // ReadBundle reads a bundle from a reader. @@ -355,7 +365,7 @@ func ReadBundle(r io.Reader) (*Bundle, error) { var bundle Bundle decoder := json.NewDecoder(r) if err := decoder.Decode(&bundle); err != nil { - return nil, err + return nil, coreerr.E("ReadBundle", "failed to decode bundle", err) } return &bundle, nil }