fix(brainfs): refuse a file behind a symlinked directory for reads and writes (#173)
os.Root follows an intermediate directory symlink that stays inside the brain. A dir symlink inside wiki/ pointing elsewhere in the brain let brain_update and brain_tunnel rewrite an existing .md there (e.g. under raw/), and session_log followed a symlinked sessions dir. - WriteRegular, ReadRegular and ReadRegularMax require every directory above the file to be a real directory (realDir). Reads too: brain_tunnel read the redirected target, then rewrote the source before the target's write was refused; and brain_read/brain_get could read another part of the brain through such a path. - session.Append refuses a sessions dir that is not a real directory. - A directory swapped in after the check remains the #167 residual. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -68,6 +68,13 @@ func Append(sessionsDir, sessionID string, entry Entry) error {
|
||||
if err := os.MkdirAll(sessionsDir, 0o755); err != nil {
|
||||
return fmt.Errorf("create sessions dir: %w", err)
|
||||
}
|
||||
// The sessions dir itself must be a real directory, not a symlink that
|
||||
// redirects the log elsewhere (hyperguild#173).
|
||||
if fi, err := os.Lstat(sessionsDir); err != nil {
|
||||
return fmt.Errorf("stat sessions dir: %w", err)
|
||||
} else if !fi.IsDir() {
|
||||
return fmt.Errorf("sessions dir %s is not a real directory", sessionsDir)
|
||||
}
|
||||
path := filepath.Join(sessionsDir, sessionID+".jsonl")
|
||||
f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY|syscall.O_NOFOLLOW|syscall.O_NONBLOCK, 0o644)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user