package graphsync import ( "context" "fmt" "os" "path/filepath" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/mathiasbq/hyperguild/ingestion/internal/brainfs/brainfstest" ) func (f *fakeStore) dump() string { f.mu.Lock() defer f.mu.Unlock() return fmt.Sprintf("%+v %+v", f.upserts, f.replaces) } // hyperguild#157 slice 4: IndexDoc and BackfillFromBrainDir read notes with // os.ReadFile, so a symlinked note put the target's title and wikilinks into // the graph, and a FIFO hung the read. func TestIndexDocRefusesSymlinksAndSurvivesSwapRaces(t *testing.T) { brain := t.TempDir() facts := filepath.Join(brain, "wiki", "w", "facts") require.NoError(t, os.MkdirAll(facts, 0o755)) require.NoError(t, os.WriteFile(filepath.Join(facts, "ok.md"), []byte("---\ntitle: zqinside\n---\nbody [[other]]\n"), 0o644)) require.NoError(t, os.Symlink(brainfstest.Outside(t), filepath.Join(facts, "leak.md"))) store := newFakeStore() require.NoError(t, IndexDoc(context.Background(), store, brain, "wiki/w/facts/ok.md")) assert.Contains(t, store.dump(), "zqinside", "positive control: a regular note is indexed") assert.Error(t, IndexDoc(context.Background(), store, brain, "wiki/w/facts/leak.md")) // A link to a file elsewhere in the brain: os.Root allows it, only the // regular-file check refuses it. inBrain := brainfstest.WriteSentinelAt(t, filepath.Join(brain, "raw", "s.md")) require.NoError(t, os.Symlink(filepath.Join("..", "..", "..", "raw", "s.md"), filepath.Join(facts, "inlink.md"))) assert.Error(t, IndexDoc(context.Background(), store, brain, "wiki/w/facts/inlink.md")) assert.NotContains(t, store.dump(), brainfstest.Sentinel) p := filepath.Join(facts, "r.md") read := func() string { s := newFakeStore() _ = IndexDoc(context.Background(), s, brain, "wiki/w/facts/r.md") return s.dump() } 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 TestBackfillSkipsSymlinkedNotesAndAbortsOnASymlinkedRoot(t *testing.T) { brain := t.TempDir() facts := filepath.Join(brain, "wiki", "w", "facts") require.NoError(t, os.MkdirAll(facts, 0o755)) require.NoError(t, os.WriteFile(filepath.Join(facts, "ok.md"), []byte("---\ntitle: zqinside\n---\n"), 0o644)) require.NoError(t, os.Symlink(brainfstest.Outside(t), filepath.Join(facts, "leak.md"))) 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", "r.md"), filepath.Join(facts, "inlink.md"))) require.NoError(t, os.Symlink(".", filepath.Join(brain, "knowledge"))) store := newFakeStore() _, err := BackfillFromBrainDir(context.Background(), store, brain) assert.Error(t, err, "a symlinked root aborts the backfill") assert.NotContains(t, store.dump(), brainfstest.Sentinel) assert.NotContains(t, store.dump(), "zqrawonly") require.NoError(t, os.Remove(filepath.Join(brain, "knowledge"))) store = newFakeStore() n, err := BackfillFromBrainDir(context.Background(), store, brain) require.NoError(t, err) assert.Equal(t, 1, n, "only the regular note is indexed") assert.Contains(t, store.dump(), "zqinside") assert.NotContains(t, store.dump(), brainfstest.Sentinel) } // IndexDoc gets caller-supplied paths (brain_tunnel, brain_update): only a // clean path under wiki/ or knowledge/ is indexed. func TestIndexDocRefusesPathsOutsideWikiAndKnowledgeAndUncleanPaths(t *testing.T) { brain := t.TempDir() brainfstest.WriteSentinelAt(t, filepath.Join(brain, "raw", "secret.md")) require.NoError(t, os.MkdirAll(filepath.Join(brain, "wiki", "w"), 0o755)) for _, p := range []string{"raw/secret.md", "wiki/w/../../raw/secret.md", "./raw/secret.md", "/raw/secret.md"} { store := newFakeStore() assert.Error(t, IndexDoc(context.Background(), store, brain, p), p) assert.NotContains(t, store.dump(), brainfstest.Sentinel, p) } }