Files
AetherForge/agent/client/client_upload_test.go
AetherForge 7b2d41cda8
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Expand P2 test coverage: mining chain, spread lanes, path forge, WS/beacon, E2E onion, file handling
2026-06-07 04:58:55 -07:00

169 lines
4.7 KiB
Go

package client
import (
"encoding/base64"
"os"
"path/filepath"
"strings"
"sync"
"testing"
"crypto-miner-agent/deploy"
)
type commandResult struct {
action string
success bool
message string
}
func captureCommandResult(t *testing.T, c *AgentClient) (done <-chan struct{}, result *commandResult) {
t.Helper()
ch := make(chan struct{})
var mu sync.Mutex
out := &commandResult{}
c.commandResultHook = func(action string, success bool, message string) {
mu.Lock()
out.action = action
out.success = success
out.message = message
mu.Unlock()
close(ch)
}
t.Cleanup(func() { c.commandResultHook = nil })
return ch, out
}
// traversalPaths lists every variant that must be rejected by upload/download.
var traversalPaths = []struct {
name string
path string
}{
{name: "unix_relative", path: "../../etc/passwd"},
{name: "windows_relative", path: `..\..\Windows\System32\config\sam`},
{name: "embedded_traversal", path: "uploads/../../outside.txt"},
{name: "absolute_with_traversal", path: "/var/log/../../etc/shadow"},
{name: "dotdot_only", path: ".."},
{name: "mixed_separators", path: `foo\..\bar\..\..\secret`},
{name: "tilde_traversal", path: "~/../../etc/passwd"},
{name: "desktop_prefix_traversal", path: "@desktop/../../outside.txt"},
{name: "desktop_colon_traversal", path: "desktop:../../payload.bin"},
{name: "midpath_dotdot", path: "safe/inner/../../../etc/shadow"},
}
func TestUploadCommandRejectsPathTraversal(t *testing.T) {
data := base64.StdEncoding.EncodeToString([]byte("payload"))
cases := traversalPaths
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
_, err := deploy.ResolveRemotePath(tc.path)
if err == nil {
t.Fatalf("ResolveRemotePath(%q) should reject traversal", tc.path)
}
if !strings.Contains(err.Error(), "path traversal") {
t.Fatalf("ResolveRemotePath(%q) error = %q, want path traversal rejection", tc.path, err.Error())
}
c := newTestClient(t)
done, got := captureCommandResult(t, c)
c.handleCommand("upload", 0, "", tc.path, data, "")
<-done
if got.action != "upload" {
t.Fatalf("action = %q, want upload", got.action)
}
if got.success {
t.Fatalf("upload with %q should fail (success=true, message=%q)", tc.path, got.message)
}
if !strings.Contains(got.message, "path traversal") {
t.Fatalf("message = %q, want path traversal error from ResolveRemotePath", got.message)
}
})
}
}
func TestDownloadCommandRejectsPathTraversal(t *testing.T) {
cases := traversalPaths
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
_, err := deploy.ResolveRemotePath(tc.path)
if err == nil {
t.Fatalf("ResolveRemotePath(%q) should reject traversal", tc.path)
}
if !strings.Contains(err.Error(), "path traversal") {
t.Fatalf("ResolveRemotePath(%q) error = %q, want path traversal rejection", tc.path, err.Error())
}
c := newTestClient(t)
done, got := captureCommandResult(t, c)
c.handleCommand("download", 0, "", tc.path, "", "")
<-done
if got.action != "download" {
t.Fatalf("action = %q, want download", got.action)
}
if got.success {
t.Fatalf("download with %q should fail (success=true, message=%q)", tc.path, got.message)
}
if !strings.Contains(got.message, "path traversal") {
t.Fatalf("message = %q, want path traversal error from ResolveRemotePath", got.message)
}
})
}
}
func TestUploadCommandRejectsInvalidBase64(t *testing.T) {
c := newTestClient(t)
done, got := captureCommandResult(t, c)
c.handleCommand("upload", 0, "", "notes.txt", "not-valid-base64!!!", "")
<-done
if got.success {
t.Fatalf("expected base64 failure, got %q", got.message)
}
if !strings.Contains(got.message, "invalid base64") {
t.Fatalf("message=%q", got.message)
}
}
func TestDownloadCommandReadsSafeFile(t *testing.T) {
dir := t.TempDir()
src := filepath.Join(dir, "payload.bin")
content := []byte("download-me")
if err := os.WriteFile(src, content, 0644); err != nil {
t.Fatal(err)
}
data := base64.StdEncoding.EncodeToString(content)
c := newTestClient(t)
done, got := captureCommandResult(t, c)
c.handleCommand("download", 0, "", src, "", "")
<-done
if !got.success {
t.Fatalf("download failed: %s", got.message)
}
if got.message != data {
t.Fatalf("encoded mismatch: got len=%d want len=%d", len(got.message), len(data))
}
}
func TestUploadCommandAcceptsSafePath(t *testing.T) {
dir := t.TempDir()
dest := dir + "/notes.txt"
data := base64.StdEncoding.EncodeToString([]byte("ok"))
c := newTestClient(t)
done, got := captureCommandResult(t, c)
c.handleCommand("upload", 0, "", dest, data, "")
<-done
if !got.success {
t.Fatalf("safe upload failed: %s", got.message)
}
if got.action != "upload" {
t.Fatalf("action = %q, want upload", got.action)
}
}