graphsync, wiki inventory/index read through brainfs; brainfs refuses an in-brain file swapped in behind a symlink (os.SameFile). Gatekeeper round 2 PASS on d90ce66, CI run 9180 green.
Refs #157
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Mathias <mthbqv@gmail.com>
80 lines
3.6 KiB
Go
80 lines
3.6 KiB
Go
package wiki
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/mathiasbq/hyperguild/ingestion/internal/brainfs"
|
|
"github.com/mathiasbq/hyperguild/ingestion/internal/brainfs/brainfstest"
|
|
)
|
|
|
|
// hyperguild#157 slice 4: LoadInventory listed wiki/<type>/ with os.ReadDir
|
|
// and opened each .md by path, so a symlinked page put the target's title and
|
|
// aliases into the inventory (and the LLM prompts built from it), and a FIFO
|
|
// hung it. RebuildIndex read first sentences the same way.
|
|
func TestLoadInventoryRefusesSymlinksAndSurvivesSwapRaces(t *testing.T) {
|
|
brain := t.TempDir()
|
|
dir := filepath.Join(brain, "wiki", "concepts")
|
|
require.NoError(t, os.MkdirAll(dir, 0o755))
|
|
require.NoError(t, os.WriteFile(filepath.Join(dir, "ok.md"), []byte("---\ntitle: zqinside\n---\n"), 0o644))
|
|
require.NoError(t, os.Symlink(brainfstest.Outside(t), filepath.Join(dir, "leak.md")))
|
|
inBrain := brainfstest.WriteSentinelAt(t, filepath.Join(brain, "raw", "s.md"))
|
|
require.NoError(t, os.Symlink(filepath.Join("..", "..", "raw", "s.md"), filepath.Join(dir, "inlink.md")))
|
|
|
|
inv, err := LoadInventory(brain)
|
|
require.NoError(t, err)
|
|
got := fmt.Sprintf("%+v", inv)
|
|
assert.Contains(t, got, "zqinside", "positive control")
|
|
assert.NotContains(t, got, brainfstest.Sentinel)
|
|
assert.NotContains(t, got, "leak", "a symlinked page is not listed")
|
|
assert.NotContains(t, got, "inlink", "a page linked to another brain file is not listed")
|
|
|
|
p := filepath.Join(dir, "r.md")
|
|
read := func() string {
|
|
inv, _ := LoadInventory(brain)
|
|
return fmt.Sprintf("%+v", inv)
|
|
}
|
|
brainfstest.AssertNoLeak(t, p, "---\ntitle: zqinside\n---\n", 1500*time.Millisecond, read)
|
|
brainfstest.AssertNoLeakFrom(t, p, "---\ntitle: zqinside\n---\n", inBrain, 1500*time.Millisecond, read)
|
|
brainfstest.AssertNoHang(t, p, "---\ntitle: zqinside\n---\n", 1500*time.Millisecond, 6*time.Second, func() { _ = read() })
|
|
}
|
|
|
|
func TestLoadInventoryReturnsErrNotRegularForASymlinkedTypeDir(t *testing.T) {
|
|
brain := t.TempDir()
|
|
require.NoError(t, os.MkdirAll(filepath.Join(brain, "wiki"), 0o755))
|
|
require.NoError(t, os.MkdirAll(filepath.Join(brain, "raw"), 0o755))
|
|
require.NoError(t, os.WriteFile(filepath.Join(brain, "raw", "r.md"), []byte("---\ntitle: zqrawonly\n---\n"), 0o644))
|
|
require.NoError(t, os.Symlink(filepath.Join("..", "raw"), filepath.Join(brain, "wiki", "concepts")))
|
|
inv, err := LoadInventory(brain)
|
|
require.ErrorIs(t, err, brainfs.ErrNotRegular)
|
|
assert.NotContains(t, fmt.Sprintf("%+v", inv), "zqrawonly")
|
|
}
|
|
|
|
func TestRebuildIndexDoesNotReadAPageSwappedForASymlink(t *testing.T) {
|
|
brain := t.TempDir()
|
|
dir := filepath.Join(brain, "wiki", "concepts")
|
|
require.NoError(t, os.MkdirAll(dir, 0o755))
|
|
p := filepath.Join(dir, "r.md")
|
|
require.NoError(t, os.WriteFile(p, []byte("---\ntitle: R\n---\nzqinside sentence.\n"), 0o644))
|
|
require.NoError(t, RebuildIndex(brain, "2026-10-04"))
|
|
idx, err := os.ReadFile(filepath.Join(brain, "wiki", "index.md"))
|
|
require.NoError(t, err)
|
|
require.Contains(t, string(idx), "zqinside", "positive control: the summary is read")
|
|
|
|
read := func() string {
|
|
_ = RebuildIndex(brain, "2026-10-04")
|
|
b, _ := os.ReadFile(filepath.Join(brain, "wiki", "index.md"))
|
|
return string(b)
|
|
}
|
|
brainfstest.AssertNoLeak(t, p, "---\ntitle: R\n---\nzqinside sentence.\n", 1500*time.Millisecond, read)
|
|
inBrain := brainfstest.WriteSentinelAt(t, filepath.Join(brain, "raw", "s.md"))
|
|
brainfstest.AssertNoLeakFrom(t, p, "---\ntitle: R\n---\nzqinside sentence.\n", inBrain, 1500*time.Millisecond, read)
|
|
brainfstest.AssertNoHang(t, p, "---\ntitle: R\n---\nzqinside sentence.\n", 1500*time.Millisecond, 6*time.Second, func() { _ = read() })
|
|
}
|