Fix review findings: hostile snapshots, stale async results, trash safety

- snapshot: depth limit (stack overflow in exporters), decompressed-size
  limit (gzip bombs), per-node size cap and saturating aggregation
- export: sanitise exclusion patterns from snapshot metadata
- tui: drop duplicate/trash/filter results that belong to a replaced
  tree; cancel duplicate search on rescan
- trash: refuse symlinked or foreign shared-filesystem trash dirs; never
  overwrite an existing trashed item
- scan: no symlink following without inode identity; correct unscanned
  count and keep real read errors after cancellation
This commit is contained in:
Andy Dixon
2026-10-02 08:46:52 +01:00
parent 11a0e401ee
commit f4ef12a0dd
10 changed files with 315 additions and 44 deletions
+14 -2
View File
@@ -378,10 +378,22 @@ func (m *Model) rescan() tea.Cmd {
m.scanner, m.tree, m.cancel, m.scanning, m.ctrlC = s, t, cancel, s != nil, false
m.zoom, m.sel = t.Root(), noSel
m.kidsCache = map[inventory.NodeID]*kids{}
m.dups = nil
if m.dups != nil {
m.dups.cancel()
m.dups = nil
// Drop the duplicates view: its rows referred to the old tree.
for i, v := range m.views {
if _, ok := v.(*dupView); ok {
m.views = append(m.views[:i], m.views[i+1:]...)
m.view = 0
break
}
}
}
m.search.result = nil
m.search.gen++ // results computed against the old tree are discarded
m.invalidate()
return tea.Batch(waitScan(s), m.startTicking(), m.info("Rescanning…"))
return tea.Batch(waitScan(s), m.startTicking(), m.info("Rescanning…"), m.applyFilter())
}
// resolvePendingZoom re-zooms into the remembered directory once the new