Observed behavior: collectEventSidecars scans the events directory by pathname and, after asynchronous outbox reads and close, pruneSidecarFiles removes the database, WAL and SHM paths without retaining/revalidating the events-directory entry. Replacing that parent between scan and prune redirects the removal to another directory. collectStats invokes this scanner with default pruning enabled.
Expected behavior: Authenticate and retain the events directory identity across scan, asynchronous operations, and each prune; refuse a replaced or symlinked parent before deleting sidecars. Preserve valid orphan pruning.
Root cause: At de0362ad77537eb2007178ca759a7204d177136f, src/db/event-sidecars.ts uses readdirSync(dir) and later rmSync(path + suffix) without a retained directory witness. Database-open admission alone cannot protect this later prune lifetime.
How to reproduce: Use a hermetic private fixture with an orphan event sidecar. At a deterministic outbox-close boundary, rename the events directory and replace its pathname with a symlink to a second private fixture containing same-named sentinel database/WAL/SHM files. Enable orphan pruning. The current prune function follows the replaced parent. No live daemon/state or operational exploit was used; this finding is from static source tracing.
Scope: Discovered by GLM planning review of #973 (Epic #968). Distinct from #973 project stats SQLite admission and #935 connection/outbox database-open parent authentication: this finding concerns scanner/pruner directory authority across asynchronous work. Outside campaign frozen inventory; no native parent requested.
Environment:
Observed behavior:
collectEventSidecarsscans the events directory by pathname and, after asynchronous outbox reads and close,pruneSidecarFilesremoves the database, WAL and SHM paths without retaining/revalidating the events-directory entry. Replacing that parent between scan and prune redirects the removal to another directory.collectStatsinvokes this scanner with default pruning enabled.Expected behavior: Authenticate and retain the events directory identity across scan, asynchronous operations, and each prune; refuse a replaced or symlinked parent before deleting sidecars. Preserve valid orphan pruning.
Root cause: At
de0362ad77537eb2007178ca759a7204d177136f,src/db/event-sidecars.tsusesreaddirSync(dir)and laterrmSync(path + suffix)without a retained directory witness. Database-open admission alone cannot protect this later prune lifetime.How to reproduce: Use a hermetic private fixture with an orphan event sidecar. At a deterministic outbox-close boundary, rename the events directory and replace its pathname with a symlink to a second private fixture containing same-named sentinel database/WAL/SHM files. Enable orphan pruning. The current prune function follows the replaced parent. No live daemon/state or operational exploit was used; this finding is from static source tracing.
Scope: Discovered by GLM planning review of #973 (Epic #968). Distinct from #973 project stats SQLite admission and #935 connection/outbox database-open parent authentication: this finding concerns scanner/pruner directory authority across asynchronous work. Outside campaign frozen inventory; no native parent requested.
Environment: