diff --git a/pkg/node/bundle.go b/pkg/node/bundle.go index bea3c81..17866fd 100644 --- a/pkg/node/bundle.go +++ b/pkg/node/bundle.go @@ -6,7 +6,6 @@ import ( "crypto/sha256" "encoding/hex" "encoding/json" - "fmt" "io" "os" "path/filepath" @@ -51,14 +50,14 @@ func CreateProfileBundle(profileJSON []byte, name string, password string) (*Bun // Create a TIM with just the profile config timContainer, err := tim.New() if err != nil { - return nil, fmt.Errorf("failed to create TIM: %w", err) + return nil, &ProtocolError{Code: ErrCodeOperationFailed, Message: "failed to create TIM: " + err.Error()} } timContainer.Config = profileJSON // Encrypt to STIM format stimData, err := timContainer.ToSigil(password) if err != nil { - return nil, fmt.Errorf("failed to encrypt bundle: %w", err) + return nil, &ProtocolError{Code: ErrCodeOperationFailed, Message: "failed to encrypt bundle: " + err.Error()} } // Calculate checksum @@ -91,7 +90,7 @@ func CreateMinerBundle(minerPath string, profileJSON []byte, name string, passwo // Read miner binary minerData, err := os.ReadFile(minerPath) if err != nil { - return nil, fmt.Errorf("failed to read miner binary: %w", err) + return nil, &ProtocolError{Code: ErrCodeOperationFailed, Message: "failed to read miner binary: " + err.Error()} } // Create a tarball with the miner binary @@ -99,19 +98,19 @@ func CreateMinerBundle(minerPath string, profileJSON []byte, name string, passwo filepath.Base(minerPath): minerData, }) if err != nil { - return nil, fmt.Errorf("failed to create tarball: %w", err) + return nil, &ProtocolError{Code: ErrCodeOperationFailed, Message: "failed to create tarball: " + err.Error()} } // Create DataNode from tarball dataNode, err := datanode.FromTar(tarData) if err != nil { - return nil, fmt.Errorf("failed to create datanode: %w", err) + return nil, &ProtocolError{Code: ErrCodeOperationFailed, Message: "failed to create datanode: " + err.Error()} } // Create TIM from DataNode timContainer, err := tim.FromDataNode(dataNode) if err != nil { - return nil, fmt.Errorf("failed to create TIM: %w", err) + return nil, &ProtocolError{Code: ErrCodeOperationFailed, Message: "failed to create TIM: " + err.Error()} } // Set profile as config if provided @@ -122,7 +121,7 @@ func CreateMinerBundle(minerPath string, profileJSON []byte, name string, passwo // Encrypt to STIM format stimData, err := timContainer.ToSigil(password) if err != nil { - return nil, fmt.Errorf("failed to encrypt bundle: %w", err) + return nil, &ProtocolError{Code: ErrCodeOperationFailed, Message: "failed to encrypt bundle: " + err.Error()} } checksum := calculateChecksum(stimData) @@ -140,7 +139,7 @@ func CreateMinerBundle(minerPath string, profileJSON []byte, name string, passwo func ExtractProfileBundle(bundle *Bundle, password string) ([]byte, error) { // Verify checksum first if calculateChecksum(bundle.Data) != bundle.Checksum { - return nil, fmt.Errorf("checksum mismatch - bundle may be corrupted") + return nil, &ProtocolError{Code: ErrCodeOperationFailed, Message: "checksum mismatch - bundle may be corrupted"} } // If it's unencrypted JSON, just return it @@ -151,7 +150,7 @@ func ExtractProfileBundle(bundle *Bundle, password string) ([]byte, error) { // Decrypt STIM format timContainer, err := tim.FromSigil(bundle.Data, password) if err != nil { - return nil, fmt.Errorf("failed to decrypt bundle: %w", err) + return nil, &ProtocolError{Code: ErrCodeOperationFailed, Message: "failed to decrypt bundle: " + err.Error()} } return timContainer.Config, nil @@ -162,25 +161,25 @@ func ExtractProfileBundle(bundle *Bundle, password string) ([]byte, error) { func ExtractMinerBundle(bundle *Bundle, password string, destDir string) (string, []byte, error) { // Verify checksum if calculateChecksum(bundle.Data) != bundle.Checksum { - return "", nil, fmt.Errorf("checksum mismatch - bundle may be corrupted") + return "", nil, &ProtocolError{Code: ErrCodeOperationFailed, Message: "checksum mismatch - bundle may be corrupted"} } // Decrypt STIM format timContainer, err := tim.FromSigil(bundle.Data, password) if err != nil { - return "", nil, fmt.Errorf("failed to decrypt bundle: %w", err) + return "", nil, &ProtocolError{Code: ErrCodeOperationFailed, Message: "failed to decrypt bundle: " + err.Error()} } // Convert rootfs to tarball and extract tarData, err := timContainer.RootFS.ToTar() if err != nil { - return "", nil, fmt.Errorf("failed to convert rootfs to tar: %w", err) + return "", nil, &ProtocolError{Code: ErrCodeOperationFailed, Message: "failed to convert rootfs to tar: " + err.Error()} } // Extract tarball to destination minerPath, err := extractTarball(tarData, destDir) if err != nil { - return "", nil, fmt.Errorf("failed to extract tarball: %w", err) + return "", nil, &ProtocolError{Code: ErrCodeOperationFailed, Message: "failed to extract tarball: " + err.Error()} } return minerPath, timContainer.Config, nil @@ -264,7 +263,7 @@ func extractTarball(tarData []byte, destDir string) (string, error) { // Ensure destDir is an absolute, clean path for security checks absDestDir, err := filepath.Abs(destDir) if err != nil { - return "", fmt.Errorf("failed to resolve destination directory: %w", err) + return "", &ProtocolError{Code: ErrCodeOperationFailed, Message: "failed to resolve destination directory: " + err.Error()} } absDestDir = filepath.Clean(absDestDir) @@ -289,13 +288,13 @@ func extractTarball(tarData []byte, destDir string) (string, error) { // Reject absolute paths if filepath.IsAbs(cleanName) { - return "", fmt.Errorf("invalid tar entry: absolute path not allowed: %s", header.Name) + return "", &ProtocolError{Code: ErrCodeOperationFailed, Message: "invalid tar entry: absolute path not allowed: " + header.Name} } // Reject paths that escape the destination directory parentPrefix := ".." + string(os.PathSeparator) if bytes.HasPrefix([]byte(cleanName), []byte(parentPrefix)) || cleanName == ".." { - return "", fmt.Errorf("invalid tar entry: path traversal attempt: %s", header.Name) + return "", &ProtocolError{Code: ErrCodeOperationFailed, Message: "invalid tar entry: path traversal attempt: " + header.Name} } // Build the full path and verify it's within destDir @@ -305,7 +304,7 @@ func extractTarball(tarData []byte, destDir string) (string, error) { // Final security check: ensure the path is still within destDir destDirPrefix := absDestDir + string(os.PathSeparator) if !bytes.HasPrefix([]byte(fullPath), []byte(destDirPrefix)) && fullPath != absDestDir { - return "", fmt.Errorf("invalid tar entry: path escape attempt: %s", header.Name) + return "", &ProtocolError{Code: ErrCodeOperationFailed, Message: "invalid tar entry: path escape attempt: " + header.Name} } switch header.Typeflag { @@ -334,7 +333,7 @@ func extractTarball(tarData []byte, destDir string) (string, error) { } if written > maxFileSize { os.Remove(fullPath) - return "", fmt.Errorf("file %s exceeds maximum size of %d bytes", header.Name, maxFileSize) + return "", &ProtocolError{Code: ErrCodeOperationFailed, Message: "file " + header.Name + " exceeds maximum allowed size"} } // Track first executable