readExcerpt, ReadNote and ReadScoped now read through brainfs (os.Root, regular files only, type checked on the opened handle), so a symlink swapped in after a check is not followed and a FIFO does not hang the reader. ReadRegularMax bounds the read itself with io.LimitReader(max+1). notes.Target carries the brain root. Refs #157. Adversarial review round 2: PASS (round 1 BLOCKED: unbounded read, rule-9 test names). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Mathias <mthbqv@gmail.com>
This commit was merged in pull request #165.
This commit is contained in:
@@ -2,6 +2,7 @@ package brainfs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
@@ -90,3 +91,51 @@ func TestWalkRegularYieldsOnlyRegularFiles(t *testing.T) {
|
||||
require.NoError(t, b.WalkRegular(".", func(rel string) error { got = append(got, rel); return nil }, nil))
|
||||
assert.Equal(t, []string{"a/n.md"}, got)
|
||||
}
|
||||
|
||||
func TestReadRegularMaxRefusesAFileOverTheLimit(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
require.NoError(t, os.WriteFile(filepath.Join(dir, "big.md"), make([]byte, 100), 0o644))
|
||||
b, err := Open(dir)
|
||||
require.NoError(t, err)
|
||||
defer func() { _ = b.Close() }()
|
||||
_, err = b.ReadRegularMax("big.md", 99)
|
||||
assert.True(t, errors.Is(err, ErrTooLarge), "got %v", err)
|
||||
got, err := b.ReadRegularMax("big.md", 100)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, got, 100)
|
||||
}
|
||||
|
||||
func TestReadRegularMaxRefusesAndStopsReadingAFileThatGrowsPastTheLimit(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
p := filepath.Join(dir, "grow.md")
|
||||
require.NoError(t, os.WriteFile(p, make([]byte, 10), 0o644))
|
||||
consumed := &countingReader{}
|
||||
wrapAfterStat = func(r io.Reader) io.Reader {
|
||||
f, err := os.OpenFile(p, os.O_WRONLY|os.O_APPEND, 0)
|
||||
require.NoError(t, err)
|
||||
_, err = f.Write(make([]byte, 1000))
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.Close())
|
||||
consumed.r = r
|
||||
return consumed
|
||||
}
|
||||
t.Cleanup(func() { wrapAfterStat = nil })
|
||||
b, err := Open(dir)
|
||||
require.NoError(t, err)
|
||||
defer func() { _ = b.Close() }()
|
||||
got, err := b.ReadRegularMax("grow.md", 100)
|
||||
assert.True(t, errors.Is(err, ErrTooLarge), "got %v", err)
|
||||
assert.Nil(t, got)
|
||||
assert.LessOrEqual(t, consumed.n, 101, "read past max+1 bytes")
|
||||
}
|
||||
|
||||
type countingReader struct {
|
||||
r io.Reader
|
||||
n int
|
||||
}
|
||||
|
||||
func (c *countingReader) Read(p []byte) (int, error) {
|
||||
n, err := c.r.Read(p)
|
||||
c.n += n
|
||||
return n, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user