fix(ingestion): close the PR #156 review findings on raw/ reads
CI / Lint / Test / Vet (pull_request) Successful in 34s
CI / Mirror to GitHub (pull_request) Skipped

- Reverting the ingest call sites to extract.Text(path) stayed green;
  a test now fails if anything outside internal/extract calls
  extract.Text( (only TextIn, through an os.Root, may be used).
- The watcher symlink test passed for a watcher that ingested nothing,
  or stopped at the first symlink (SkipAll: one synced link halting all
  ingestion). It now plants a real note after the symlink and asserts
  exactly one ingest, and the symlinked-raw refusal has its own test.
- A FIFO swapped in between Lstat and Open hung open(2) forever.
  extract.OpenRegular opens non-blocking and checks the type on the
  handle; readRawNote, TextIn and the watcher's archive copy use it.
- The symlinked-root test covers pending, promote, reject, resolve and
  ReadIngestFile, and is named for exactly those.
- The hard-link residual is documented where it is accepted.

Out of scope, filed: #157 (symlinks elsewhere in the Syncthing-shared
brain dir reach search and other readers), #158 (writes under raw/
follow symlinks).

Refs #153

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-03 16:59:08 +02:00
co-authored by Claude Opus 5.5
parent 8a91feb02b
commit 87c80e4e82
7 changed files with 138 additions and 8 deletions
@@ -0,0 +1,43 @@
package extract
import (
"os"
"path/filepath"
"strings"
"testing"
)
// PR #156 review: putting extract.Text(path) back at the ingest call sites
// stayed green, because only TextIn itself was tested. Reading a caller-chosen
// path by name reopens it after the containment check (hyperguild#153 item 3),
// so outside this package only TextIn may be used.
func TestNothingOutsideExtractCallsTextByPath(t *testing.T) {
root := filepath.Join("..", "..")
var offenders []string
err := filepath.WalkDir(root, func(p string, d os.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() && (d.Name() == "vendor" || d.Name() == ".git") {
return filepath.SkipDir
}
if d.IsDir() || !strings.HasSuffix(p, ".go") || strings.HasSuffix(p, "_test.go") ||
strings.Contains(filepath.ToSlash(p), "internal/extract/") {
return nil
}
b, err := os.ReadFile(p)
if err != nil {
return err
}
if strings.Contains(string(b), "extract.Text(") {
offenders = append(offenders, p)
}
return nil
})
if err != nil {
t.Fatal(err)
}
if len(offenders) > 0 {
t.Fatalf("extract.Text( called outside internal/extract (use TextIn through an os.Root): %v", offenders)
}
}