New brainfs PutRegular (create or replace), CreateRegular (create only), AppendRegular, Remove and RealParent: missing parents created one component at a time through the root, every existing component a real directory, new files created with O_EXCL and confirmed before writing. brain_write, promote/reject (O_EXCL create, source kept on refusal), StampProvenanceFile, BuildWingIndex, RebuildIndex, AppendLog and the tunnel candidate log use them; provenance.ApplyFile removed. Fixes #188. Refs #158. Adversarial review: BLOCKED, BLOCKED, PASS. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Mathias <mthbqv@gmail.com>
This commit was merged in pull request #189.
This commit is contained in:
@@ -3,8 +3,6 @@ package wiki
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/mathiasbq/hyperguild/ingestion/internal/brainfs"
|
||||
@@ -50,8 +48,8 @@ func RebuildIndex(brainDir, date string) error {
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
|
||||
dest := filepath.Join(brainDir, "wiki", "index.md")
|
||||
return os.WriteFile(dest, []byte(sb.String()), 0o644)
|
||||
// Written through the brain root, never through a symlink (#158).
|
||||
return bfs.PutRegular("wiki/index.md", []byte(sb.String()))
|
||||
}
|
||||
|
||||
// pageFirstSentence reads the page through the brain root, regular files
|
||||
|
||||
@@ -3,8 +3,7 @@ package wiki
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"github.com/mathiasbq/hyperguild/ingestion/internal/brainfs"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -27,14 +26,14 @@ func AppendLog(brainDir, source string, pages, warnings []string, date string) e
|
||||
}
|
||||
sb.WriteString("\n")
|
||||
|
||||
logPath := filepath.Join(brainDir, "log.md")
|
||||
f, err := os.OpenFile(logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
|
||||
// Appended through the brain root, never through a symlink (#158).
|
||||
bfs, err := brainfs.Open(brainDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("open log: %w", err)
|
||||
return fmt.Errorf("open brain: %w", err)
|
||||
}
|
||||
if _, err = f.WriteString(sb.String()); err != nil {
|
||||
f.Close() //nolint:errcheck
|
||||
defer func() { _ = bfs.Close() }()
|
||||
if err := bfs.AppendRegular("log.md", []byte(sb.String())); err != nil {
|
||||
return fmt.Errorf("write log: %w", err)
|
||||
}
|
||||
return f.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package wiki
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// hyperguild#158: RebuildIndex wrote wiki/index.md and AppendLog appended
|
||||
// log.md with os.WriteFile / os.OpenFile, so a symlinked file was written
|
||||
// through.
|
||||
func TestRebuildIndexAndAppendLogDoNotWriteThroughSymlinks(t *testing.T) {
|
||||
b := t.TempDir()
|
||||
require.NoError(t, os.MkdirAll(filepath.Join(b, "wiki", "concepts"), 0o755))
|
||||
victim := filepath.Join(b, "raw", "v.md")
|
||||
require.NoError(t, os.MkdirAll(filepath.Dir(victim), 0o755))
|
||||
require.NoError(t, os.WriteFile(victim, []byte("untouched"), 0o644))
|
||||
require.NoError(t, os.Symlink(filepath.Join("..", "raw", "v.md"), filepath.Join(b, "wiki", "index.md")))
|
||||
require.NoError(t, os.Symlink(filepath.Join("raw", "v.md"), filepath.Join(b, "log.md")))
|
||||
|
||||
assert.Error(t, RebuildIndex(b, "2026-10-05"))
|
||||
assert.Error(t, AppendLog(b, "src", []string{"p"}, nil, "2026-10-05"))
|
||||
got, _ := os.ReadFile(victim)
|
||||
assert.Equal(t, "untouched", string(got))
|
||||
|
||||
require.NoError(t, os.Remove(filepath.Join(b, "wiki", "index.md")))
|
||||
require.NoError(t, os.Remove(filepath.Join(b, "log.md")))
|
||||
require.NoError(t, RebuildIndex(b, "2026-10-05"), "positive control")
|
||||
require.NoError(t, AppendLog(b, "src", []string{"p"}, nil, "2026-10-05"), "positive control")
|
||||
l, _ := os.ReadFile(filepath.Join(b, "log.md"))
|
||||
assert.Contains(t, string(l), "src")
|
||||
}
|
||||
Reference in New Issue
Block a user