The tripwire looked only for symlinks. A FIFO in the brain passed it: at classification.yaml it made startup exit (classification.Load refuses a non-regular file, #176), and at a note path it could hang a reader that skips the handle check. - Scan reports special files (anything not a regular file, directory or symlink) in Result.Special; Clean() and String() include them, so the startup check refuses with a clear message naming the path. - Watch removes them as it removes symlinks, through an os.Root, after re-checking each is still special (a regular file, directory or symlink swapped in is left alone and reported). The live brain has no special files (checked before this change), so the startup check does not newly refuse. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
77 lines
2.5 KiB
Go
77 lines
2.5 KiB
Go
package main
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"syscall"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// hyperguild#157: the server refuses to start while a symlink exists in the
|
|
// brain dir, or while any part of it cannot be scanned (which could hide
|
|
// one); a missing brain dir is the only thing it lets pass.
|
|
func TestStartupSymlinkCheckRefusesABrainWithASymlink(t *testing.T) {
|
|
brain := t.TempDir()
|
|
require.NoError(t, os.Symlink(t.TempDir(), filepath.Join(brain, "planted")))
|
|
err := startupSymlinkCheck(brain)
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "planted")
|
|
}
|
|
|
|
func TestStartupSymlinkCheckRefusesABrainWithAnUnreadableDir(t *testing.T) {
|
|
if os.Geteuid() == 0 {
|
|
t.Skip("root reads mode-000 directories")
|
|
}
|
|
brain := t.TempDir()
|
|
locked := filepath.Join(brain, "aaa")
|
|
require.NoError(t, os.MkdirAll(locked, 0o755))
|
|
require.NoError(t, os.Chmod(locked, 0o000))
|
|
t.Cleanup(func() { _ = os.Chmod(locked, 0o755) })
|
|
err := startupSymlinkCheck(brain)
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "aaa")
|
|
}
|
|
|
|
func TestStartupSymlinkCheckPassesACleanOrMissingBrain(t *testing.T) {
|
|
brain := t.TempDir()
|
|
require.NoError(t, os.WriteFile(filepath.Join(brain, "n.md"), []byte("x"), 0o644))
|
|
assert.NoError(t, startupSymlinkCheck(brain))
|
|
assert.NoError(t, startupSymlinkCheck(filepath.Join(brain, "missing")))
|
|
}
|
|
|
|
func TestSymlinkScanIntervalFallsBackOnNonPositive(t *testing.T) {
|
|
for _, v := range []string{"0", "-5", "", "junk"} {
|
|
got := symlinkScanInterval(func(string) string { return v })
|
|
assert.Positive(t, got, "env %q", v)
|
|
}
|
|
}
|
|
|
|
func TestAlertingWarningsNamesTheSymlinkTripwire(t *testing.T) {
|
|
w := strings.Join(alertingWarnings(func(string) string { return "" }), " ")
|
|
assert.Contains(t, w, "symlink tripwire")
|
|
}
|
|
|
|
func TestStartupSymlinkCheckRefusesAnUnlistableBrainRoot(t *testing.T) {
|
|
if os.Geteuid() == 0 {
|
|
t.Skip("root lists mode-0100 directories")
|
|
}
|
|
brain := t.TempDir()
|
|
require.NoError(t, os.Chmod(brain, 0o100))
|
|
t.Cleanup(func() { _ = os.Chmod(brain, 0o755) })
|
|
assert.Error(t, startupSymlinkCheck(brain))
|
|
}
|
|
|
|
// hyperguild#179: a FIFO in the brain is refused at startup with a clear
|
|
// message, instead of hanging or exiting later in a reader.
|
|
func TestStartupSymlinkCheckRefusesABrainWithAFIFO(t *testing.T) {
|
|
brain := t.TempDir()
|
|
require.NoError(t, syscall.Mkfifo(filepath.Join(brain, "classification.yaml"), 0o644))
|
|
err := startupSymlinkCheck(brain)
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "classification.yaml")
|
|
}
|