diff --git a/ingestion/internal/notes/read_root_test.go b/ingestion/internal/notes/read_root_test.go index a699fd4..eaa970b 100644 --- a/ingestion/internal/notes/read_root_test.go +++ b/ingestion/internal/notes/read_root_test.go @@ -38,3 +38,31 @@ func TestReadScopedRefusesANoteOverMaxNoteBytes(t *testing.T) { _, err := ReadScoped(brain, "wiki/w/facts/big.md", ScopeNotes) assert.ErrorIs(t, err, ErrTooLarge) } + +// hyperguild#171 (review of #172, finding 1): os.Root alone refuses links that +// leave the brain, so the outside-target race above stays green for a reader +// that skips brainfs's Lstat and SameFile checks. An in-brain target (raw/, +// outside ScopeNotes) is what tells them apart. +func TestReadScopedDoesNotReturnAnInBrainSymlinkTargetStaticallyOrDuringASampledSwapRace(t *testing.T) { + brain := t.TempDir() + facts := filepath.Join(brain, "wiki", "w", "facts") + require.NoError(t, os.MkdirAll(facts, 0o755)) + target := brainfstest.WriteSentinelAt(t, filepath.Join(brain, "raw", "s.md")) + + require.NoError(t, os.Symlink(filepath.Join("..", "..", "..", "raw", "s.md"), filepath.Join(facts, "inlink.md"))) + b, err := ReadScoped(brain, "wiki/w/facts/inlink.md", ScopeNotes) + assert.Error(t, err, "static in-brain link") + assert.NotContains(t, string(b), brainfstest.Sentinel, "static in-brain link") + + p := filepath.Join(facts, "r.md") + require.NoError(t, os.WriteFile(p, []byte("inside"), 0o644)) + b, err = ReadScoped(brain, "wiki/w/facts/r.md", ScopeNotes) + require.NoError(t, err) + assert.Equal(t, "inside", string(b), "positive control: a regular note reads") + + read := func() string { + b, _ := ReadScoped(brain, "wiki/w/facts/r.md", ScopeNotes) + return string(b) + } + brainfstest.AssertNoLeakFrom(t, p, "inside", target, 1500*time.Millisecond, read) +}