fix: keep foreign probe parsing linear in what it reads (release review)

The parser and classifier run synchronously on the event loop over text a
pane, a container or an ssh host wrote, and one process could stall the
server for every session: 4 s for a 128 KiB argv of `/pi/` tokens, about a
minute for 2 MiB of socket rows.

The `cli.js` launcher rule leaves the signature regex for a per-word scan,
socket rows dedupe through a Set, the process row regex takes the `s` flag
(a bare CR made `$` fail and `\s+` give its spaces back, quadratic in a row),
argv and pane command are cut at FOREIGN_MAX_ARGV_CHARS, and one matcher per
scan answers each distinct argv once, since every pane of a hostile host can
claim the same process tree.

Measured on a private tmux server with the same 131 KiB argv: the largest
event-loop gap went from 3991 ms to 45 ms. The worst scan the caps allow
takes about 80 ms. Socket rows stay uncapped on purpose (see the docs).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Codeman maintainer committed 2026-11-06 07:10:49 +01:00
1 parent 2c084966f1
commit 800b3f4658
5 files changed
+187 -24

No files matched your search

+8
View File
@@ -46,3 +46,11 @@ export const FOREIGN_MAX_PROCS = 4000;
/** Max bytes of probe stdout kept. A runaway `ps` must not become a heap problem. */
export const FOREIGN_PROBE_MAX_BYTES = 2 * 1024 * 1024;
/**
* Longest argv or pane command kept from one row. The caps above bound how many rows a
* probe carries, this bounds what one costs: a row is text from someone else's process,
* and `ps` prints a 128 KiB argv in full. A launcher that needed more than this to be
* recognised reads as `shell`, which only changes the label on its row.
*/
export const FOREIGN_MAX_ARGV_CHARS = 4096;