@agent-beam/beam 0.2.34 → 0.2.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,510 @@
1
+ import { isAuthPath, isLlmPath } from "./monitor-rules.js";
2
+ export const PAUSE_SCOPES = ["all", "auth", "llm"];
3
+ export const PAUSE_MODES = ["off", "dry_run", "auto"];
4
+ export const DEFAULT_PAUSE_SETTINGS = { mode: "off", allowlist: [], maxActive: 500, burst: 50 };
5
+ const ALLOW = Object.freeze({ paused: false });
6
+ const MINUTE = 60_000;
7
+ const DAY = 24 * 60 * MINUTE;
8
+ export const MAX_AUTO_PAUSE_MS = DAY;
9
+ const BURST_WINDOW_MS = 10 * MINUTE;
10
+ const BREAKER_MS = 30 * MINUTE;
11
+ // After an admin lifts a pause, the sender isn't paused automatically again for this long.
12
+ const LIFTED_GRACE_MS = DAY;
13
+ /**
14
+ * Detections that pause their sender automatically, and for how long the first time. A repeat
15
+ * within a day pauses for four times as long, up to a day. Detections from a single request are
16
+ * left alone unless they're critical exploits that weren't refused: one odd request is rarely
17
+ * worth refusing someone, a pattern is.
18
+ */
19
+ export const AUTO_PAUSES = {
20
+ "app.auth_bruteforce": { scope: "auth", minutes: 15, label: "repeated failed sign-ins", by: "ip" },
21
+ "llm.denial_of_wallet": { scope: "llm", minutes: 15, label: "LLM request flood", by: "sender" },
22
+ "llm.token_spend": { scope: "llm", minutes: 15, label: "unusual LLM spend", by: "sender" },
23
+ "signal.path_walk": { scope: "all", minutes: 60, label: "walking the app's paths", by: "ip" },
24
+ "signal.probe_hit": { scope: "all", minutes: 60, label: "scan found a live path", by: "ip" },
25
+ };
26
+ const EXPLOIT_PAUSE = { scope: "all", minutes: 60, label: "critical exploit attempt", by: "ip" };
27
+ /** How a finding pauses its sender, or null when it doesn't. */
28
+ export function autoPauseFor(finding) {
29
+ if (finding.rollup)
30
+ return null;
31
+ const listed = AUTO_PAUSES[finding.ruleId];
32
+ if (listed)
33
+ return listed;
34
+ const outcome = finding.request?.outcome;
35
+ if (finding.severity === "critical" && finding.category === "exploit" && finding.source === "http" && outcome !== "blocked" && outcome !== "not_applicable")
36
+ return EXPLOIT_PAUSE;
37
+ return null;
38
+ }
39
+ /** An IPv4 or IPv6 address as bytes (IPv4-mapped IPv6 is read as IPv4), or null. */
40
+ export function parseIp(text) {
41
+ if (typeof text !== "string")
42
+ return null;
43
+ let s = text.trim();
44
+ if (!s || s.length > 64)
45
+ return null;
46
+ if (s.startsWith("["))
47
+ s = s.slice(1, s.indexOf("]") > 0 ? s.indexOf("]") : undefined);
48
+ s = s.replace(/%[\w.-]+$/, ""); // zone id
49
+ const v4 = s.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/);
50
+ if (v4) {
51
+ const bytes = v4.slice(1).map(Number);
52
+ return bytes.every(b => b <= 255) ? { v: 4, bytes } : null;
53
+ }
54
+ if (!/^[0-9a-fA-F:.]+$/.test(s) || !s.includes(":"))
55
+ return null;
56
+ let tail = [];
57
+ const lastColon = s.lastIndexOf(":");
58
+ const last = s.slice(lastColon + 1);
59
+ if (last.includes(".")) {
60
+ // An IPv4 tail (::ffff:192.0.2.1, 64:ff9b::192.0.2.1) is the last two groups.
61
+ const embedded = /^\d{1,3}(?:\.\d{1,3}){3}$/.test(last) ? parseIp(last) : null;
62
+ if (!embedded)
63
+ return null;
64
+ tail = [(embedded.bytes[0] << 8) | embedded.bytes[1], (embedded.bytes[2] << 8) | embedded.bytes[3]];
65
+ s = s.slice(0, lastColon + 1);
66
+ if (!s.endsWith("::"))
67
+ s = s.slice(0, -1);
68
+ }
69
+ const halves = s.split("::");
70
+ if (halves.length > 2)
71
+ return null;
72
+ const part = (h) => (h ? h.split(":") : []);
73
+ const head = part(halves[0]);
74
+ const back = halves.length === 2 ? part(halves[1]) : [];
75
+ if (![...head, ...back].every(g => /^[0-9a-fA-F]{1,4}$/.test(g)))
76
+ return null;
77
+ const groups = 8 - tail.length;
78
+ const missing = groups - head.length - back.length;
79
+ if (halves.length === 1 ? missing !== 0 : missing < 1)
80
+ return null;
81
+ const words = [...head.map(g => parseInt(g, 16)), ...new Array(halves.length === 2 ? missing : 0).fill(0), ...back.map(g => parseInt(g, 16)), ...tail];
82
+ if (words.length !== 8)
83
+ return null;
84
+ const bytes = words.flatMap(w => [w >> 8, w & 0xff]);
85
+ // ::ffff:a.b.c.d is an IPv4 client on a dual-stack socket.
86
+ if (bytes.slice(0, 10).every(b => b === 0) && bytes[10] === 0xff && bytes[11] === 0xff)
87
+ return { v: 4, bytes: bytes.slice(12) };
88
+ return { v: 6, bytes };
89
+ }
90
+ function parseRange(text) {
91
+ const [addr, len, extra] = text.trim().split("/");
92
+ if (extra !== undefined)
93
+ return null;
94
+ const ip = parseIp(addr);
95
+ if (!ip)
96
+ return null;
97
+ const max = ip.v === 4 ? 32 : 128;
98
+ const bits = len === undefined ? max : /^\d{1,3}$/.test(len) ? Number(len) : -1;
99
+ return bits >= 0 && bits <= max ? { ...ip, bits } : null;
100
+ }
101
+ /** A range in one canonical spelling: host bits cleared, IPv6 in its shortest form. */
102
+ function formatRange(range) {
103
+ const bytes = range.bytes.map((b, i) => {
104
+ const bit = i * 8;
105
+ if (bit >= range.bits)
106
+ return 0;
107
+ const take = Math.min(8, range.bits - bit);
108
+ return b & ((0xff << (8 - take)) & 0xff);
109
+ });
110
+ if (range.v === 4)
111
+ return `${bytes.join(".")}/${range.bits}`;
112
+ const words = [];
113
+ for (let i = 0; i < 16; i += 2)
114
+ words.push((bytes[i] << 8) | bytes[i + 1]);
115
+ return `${formatV6(words)}/${range.bits}`;
116
+ }
117
+ /** Eight 16-bit words as RFC 5952 text: lowercase, the longest run of two or more zero words as "::". */
118
+ function formatV6(words) {
119
+ let best = -1, bestLen = 0;
120
+ for (let i = 0; i < 8;) {
121
+ if (words[i] !== 0) {
122
+ i++;
123
+ continue;
124
+ }
125
+ let j = i;
126
+ while (j < 8 && words[j] === 0)
127
+ j++;
128
+ if (j - i > bestLen && j - i >= 2) {
129
+ best = i;
130
+ bestLen = j - i;
131
+ }
132
+ i = j;
133
+ }
134
+ const hex = words.map(w => w.toString(16));
135
+ if (best < 0)
136
+ return hex.join(":");
137
+ return `${hex.slice(0, best).join(":")}::${hex.slice(best + bestLen).join(":")}`;
138
+ }
139
+ function inRange(ip, range) {
140
+ if (ip.v !== range.v)
141
+ return false;
142
+ for (let bit = 0; bit < range.bits; bit += 8) {
143
+ const take = Math.min(8, range.bits - bit);
144
+ const mask = (0xff << (8 - take)) & 0xff;
145
+ if ((ip.bytes[bit / 8] & mask) !== (range.bytes[bit / 8] & mask))
146
+ return false;
147
+ }
148
+ return true;
149
+ }
150
+ // Private, loopback, link-local, shared (CGNAT) and unspecified ranges: usually a proxy, a load
151
+ // balancer or the app's own network. Pausing one could refuse everyone behind it.
152
+ const PROTECTED = ["0.0.0.0/8", "10.0.0.0/8", "100.64.0.0/10", "127.0.0.0/8", "169.254.0.0/16", "172.16.0.0/12", "192.168.0.0/16", "255.255.255.255/32", "::/128", "::1/128", "fc00::/7", "fe80::/10"]
153
+ .map(r => parseRange(r));
154
+ export function isProtectedIp(ip) {
155
+ const parsed = parseIp(ip);
156
+ return !parsed || PROTECTED.some(r => inRange(parsed, r));
157
+ }
158
+ /**
159
+ * What a pause on this address is keyed by: the address itself for IPv4, the /64 for IPv6 (one
160
+ * customer's network: rotating within it is free), or null when it isn't an address.
161
+ */
162
+ export function pauseTarget(ip) {
163
+ const parsed = parseIp(ip);
164
+ if (!parsed)
165
+ return null;
166
+ if (parsed.v === 4)
167
+ return parsed.bytes.join(".");
168
+ return formatRange({ ...parsed, bits: 64 });
169
+ }
170
+ /** user:<id> for a user id, or null. */
171
+ export function userTarget(user) {
172
+ const id = typeof user === "string" ? user.trim().slice(0, 80) : "";
173
+ return id ? `user:${id}` : null;
174
+ }
175
+ /** A target as typed by a person (an address, a CIDR range, or user:<id>), normalized; or null. */
176
+ export function normalizeTarget(text) {
177
+ const s = text.trim();
178
+ if (/^user:/i.test(s))
179
+ return userTarget(s.slice(5));
180
+ if (!s.includes("/"))
181
+ return pauseTarget(s);
182
+ const range = parseRange(s);
183
+ if (!range || range.bits < (range.v === 4 ? 16 : 32))
184
+ return null; // nothing wider than a /16 or a /32
185
+ if (range.v === 6 && range.bits === 64)
186
+ return pauseTarget(s.split("/")[0]);
187
+ if (range.v === 4 && range.bits === 32)
188
+ return range.bytes.join(".");
189
+ return formatRange(range);
190
+ }
191
+ function compileAllowlist(entries) {
192
+ const ranges = [];
193
+ const users = new Set();
194
+ for (const entry of entries.slice(0, 1000)) {
195
+ if (typeof entry !== "string")
196
+ continue;
197
+ if (/^user:/i.test(entry.trim())) {
198
+ const u = userTarget(entry.trim().slice(5));
199
+ if (u)
200
+ users.add(u);
201
+ continue;
202
+ }
203
+ const range = parseRange(entry);
204
+ if (range)
205
+ ranges.push(range);
206
+ }
207
+ return { ranges, users };
208
+ }
209
+ /** Pauses on a range wider than one sender (an admin's CIDR pause), to test addresses against. */
210
+ function rangeTargets(map) {
211
+ const out = [];
212
+ for (const pause of map.values()) {
213
+ if (!pause.target.includes("/") || pause.target.endsWith("::/64"))
214
+ continue;
215
+ const range = parseRange(pause.target);
216
+ if (range)
217
+ out.push({ range, pause });
218
+ }
219
+ return out;
220
+ }
221
+ // ---------------------------------------------------------------------------------------------
222
+ // Trusted client address
223
+ // ---------------------------------------------------------------------------------------------
224
+ /**
225
+ * The client address of a request, when it can be trusted enough to pause: one the client can't
226
+ * forge. Platforms that set or overwrite the header (Cloudflare, Vercel, Fly.io) are read
227
+ * directly. Otherwise the address the app passed is checked against X-Forwarded-For: the first
228
+ * entry of a list is whatever the client sent, so it's never trusted, while the last entry (added
229
+ * by the app's own proxy), the only entry, or an address not in the header (the socket's, or
230
+ * Express's req.ip with trust proxy) is. Returns null when the address can't be trusted.
231
+ */
232
+ export function trustedRequestIp(ip, header, platform) {
233
+ const valid = (v) => (parseIp(v) ? String(v).trim() : null);
234
+ if (platform.cloudflare)
235
+ return valid(header("cf-connecting-ip")) ?? valid(ip);
236
+ if (platform.fly)
237
+ return valid(header("fly-client-ip")) ?? valid(ip);
238
+ // Vercel overwrites X-Forwarded-For and sets X-Real-IP; neither carries what the client sent.
239
+ if (platform.vercel)
240
+ return valid(header("x-real-ip")) ?? valid(header("x-forwarded-for")?.split(",")[0]) ?? valid(ip);
241
+ const given = valid(ip);
242
+ if (!given || platform.trustGiven)
243
+ return given;
244
+ const forwarded = header("x-forwarded-for")?.split(",").map(s => s.trim()).filter(Boolean) ?? [];
245
+ if (forwarded.length < 2)
246
+ return given;
247
+ const at = forwarded.indexOf(given);
248
+ return at === -1 || at === forwarded.length - 1 ? given : null;
249
+ }
250
+ const key = (target, scope) => `${target}\0${scope}`;
251
+ /**
252
+ * The pauses one app instance knows about: those it made itself (local) and those from the
253
+ * workspace (remote: admins' pauses, and other instances' when pausing is on). Bounded.
254
+ */
255
+ export class PauseBook {
256
+ opts;
257
+ local = new Map();
258
+ remote = new Map();
259
+ remoteRanges = [];
260
+ lifted = new Map();
261
+ strikes = new Map();
262
+ recent = [];
263
+ breakerUntil = 0;
264
+ capNoticeAt = 0;
265
+ reports = [];
266
+ allow = { ranges: [], users: new Set() };
267
+ maintainedMinute = -1;
268
+ changes = 0;
269
+ settings = { ...DEFAULT_PAUSE_SETTINGS };
270
+ /** Goes up whenever a pause is made here, so a caller can skip re-checking when nothing changed. */
271
+ get version() { return this.changes; }
272
+ constructor(opts = {}) {
273
+ this.opts = opts;
274
+ }
275
+ now() { return this.opts.now?.() ?? Date.now(); }
276
+ get size() {
277
+ return { local: this.local.size, remote: this.remote.size, strikes: this.strikes.size, reports: this.reports.length };
278
+ }
279
+ /** The workspace's settings, its active pauses, and pauses an admin lifted (from the config). */
280
+ configure(settings, pauses, lifted) {
281
+ const s = settings && typeof settings === "object" ? settings : {};
282
+ this.settings = {
283
+ mode: PAUSE_MODES.includes(s.mode) ? s.mode : "off",
284
+ allowlist: Array.isArray(s.allowlist) ? s.allowlist.filter((e) => typeof e === "string").slice(0, 1000) : [],
285
+ maxActive: typeof s.maxActive === "number" && s.maxActive >= 0 ? Math.min(s.maxActive, 5000) : DEFAULT_PAUSE_SETTINGS.maxActive,
286
+ burst: typeof s.burst === "number" && s.burst >= 1 ? Math.min(s.burst, 1000) : DEFAULT_PAUSE_SETTINGS.burst,
287
+ };
288
+ this.allow = compileAllowlist(this.settings.allowlist);
289
+ const now = this.now();
290
+ this.remote.clear();
291
+ for (const p of Array.isArray(pauses) ? pauses.slice(0, 5000) : []) {
292
+ const pause = readPause(p, now);
293
+ if (pause)
294
+ this.remote.set(key(pause.target, pause.scope), pause);
295
+ }
296
+ this.remoteRanges = rangeTargets(this.remote);
297
+ for (const l of Array.isArray(lifted) ? lifted.slice(0, 5000) : []) {
298
+ const target = typeof l?.target === "string" ? l.target : null;
299
+ const at = typeof l?.at === "string" ? Date.parse(l.at) : Number.NaN;
300
+ if (!target || !Number.isFinite(at))
301
+ continue;
302
+ if (at + LIFTED_GRACE_MS > now)
303
+ this.lifted.set(target, at + LIFTED_GRACE_MS);
304
+ for (const scope of PAUSE_SCOPES)
305
+ this.local.delete(key(target, scope));
306
+ }
307
+ if (this.lifted.size > 5000)
308
+ this.lifted = new Map([...this.lifted].slice(-5000));
309
+ // Turned down in the dashboard: this instance's own pauses stop being enforced too. Turned up
310
+ // to auto: dry runs are dropped rather than enforced, so a pause always follows fresh evidence.
311
+ for (const [k, pause] of this.local) {
312
+ if (this.settings.mode === "off" || (this.settings.mode === "auto" && pause.dryRun))
313
+ this.local.delete(k);
314
+ else if (this.settings.mode === "dry_run")
315
+ pause.dryRun = true;
316
+ }
317
+ }
318
+ /** Whether the sender (address and/or user) is paused for this request. Never throws. */
319
+ check(who, where = {}) {
320
+ try {
321
+ const now = this.now();
322
+ this.maintain(now);
323
+ if (!this.local.size && !this.remote.size)
324
+ return ALLOW;
325
+ const scopes = ["all"];
326
+ if (where.path !== undefined && isAuthPath(where.path))
327
+ scopes.push("auth");
328
+ if (where.llm || (where.path !== undefined && isLlmPath(where.path)))
329
+ scopes.push("llm");
330
+ const targets = [];
331
+ const parsed = parseIp(who.ip);
332
+ const ipTarget = pauseTarget(who.ip);
333
+ if (ipTarget && !this.allowedIp(parsed))
334
+ targets.push(ipTarget);
335
+ const user = userTarget(who.user);
336
+ if (user && !this.allow.users.has(user))
337
+ targets.push(user);
338
+ let dry = null;
339
+ for (const target of targets)
340
+ for (const scope of scopes) {
341
+ const found = this.find(target, scope, now);
342
+ if (found && !found.dryRun)
343
+ return verdictFor(found, now);
344
+ if (found)
345
+ dry ??= found;
346
+ }
347
+ // A pause on a range (an admin's, from the dashboard) covers every address in it.
348
+ if (parsed && this.remoteRanges.length && !this.allowedIp(parsed))
349
+ for (const { range, pause } of this.remoteRanges)
350
+ if (scopes.includes(pause.scope) && pause.until > now && !pause.dryRun && inRange(parsed, range))
351
+ return verdictFor(pause, now);
352
+ return dry ? { ...verdictFor(dry, now), paused: false, wouldPause: true } : ALLOW;
353
+ }
354
+ catch {
355
+ return ALLOW;
356
+ }
357
+ }
358
+ find(target, scope, now) {
359
+ const k = key(target, scope);
360
+ const local = this.local.get(k);
361
+ const remote = this.remote.get(k);
362
+ const live = [local, remote].filter((p) => Boolean(p && p.until > now));
363
+ return live.find(p => !p.dryRun) ?? live[0];
364
+ }
365
+ allowedIp(ip) { return this.allow.ranges.some(r => inRange(ip, r)); }
366
+ /**
367
+ * A finding was raised (or repeated) about a sender. Pauses the sender when the finding calls
368
+ * for it and the mode allows. `who` holds only what can be trusted: an address from
369
+ * trustedRequestIp, or the app's own user id. Returns the new pause, or null.
370
+ */
371
+ consider(finding, who) {
372
+ try {
373
+ if (this.settings.mode === "off")
374
+ return null;
375
+ const rule = autoPauseFor(finding);
376
+ if (!rule)
377
+ return null;
378
+ const now = this.now();
379
+ this.maintain(now);
380
+ const user = rule.by === "sender" ? userTarget(who.user) : null;
381
+ const ip = user ? null : pauseTarget(who.ip);
382
+ const target = user ?? ip;
383
+ if (!target)
384
+ return null;
385
+ if (ip && (isProtectedIp(who.ip) || this.allowedIp(parseIp(who.ip))))
386
+ return null;
387
+ if (user && this.allow.users.has(user))
388
+ return null;
389
+ if ((this.lifted.get(target) ?? 0) > now)
390
+ return null;
391
+ for (const scope of rule.scope === "all" ? ["all"] : ["all", rule.scope]) {
392
+ const existing = this.find(target, scope, now);
393
+ if (existing && (!existing.dryRun || this.settings.mode === "dry_run"))
394
+ return null;
395
+ }
396
+ if (now < this.breakerUntil)
397
+ return null;
398
+ this.recent = this.recent.filter(t => t > now - BURST_WINDOW_MS);
399
+ if (this.recent.length >= this.settings.burst) {
400
+ this.breakerUntil = now + BREAKER_MS;
401
+ this.opts.onNotice?.(`${this.recent.length} senders were paused within 10 minutes, the most this app pauses in a burst. Automatic pausing is stopped for 30 minutes so a misread can't refuse real users. If this is a large attack, turn on your CDN's attack mode.`, "breaker");
402
+ return null;
403
+ }
404
+ let active = 0;
405
+ for (const p of this.local.values())
406
+ if (p.until > now)
407
+ active++;
408
+ if (active >= this.settings.maxActive) {
409
+ if (now - this.capNoticeAt > 60 * MINUTE) {
410
+ this.capNoticeAt = now;
411
+ this.opts.onNotice?.(`${active} senders are paused, the most this app pauses at once. New automatic pauses wait until some expire.`, "cap");
412
+ }
413
+ return null;
414
+ }
415
+ const prior = this.strikes.get(target);
416
+ const count = prior && prior.last > now - DAY ? prior.count + 1 : 1;
417
+ this.strikes.delete(target);
418
+ this.strikes.set(target, { count, last: now });
419
+ if (this.strikes.size > 10_000)
420
+ this.strikes.delete(this.strikes.keys().next().value);
421
+ const ms = Math.min(MAX_AUTO_PAUSE_MS, rule.minutes * MINUTE * 4 ** (count - 1));
422
+ const pause = {
423
+ target, scope: rule.scope, until: now + ms, source: "auto", dryRun: this.settings.mode !== "auto",
424
+ reason: `${rule.label[0].toUpperCase()}${rule.label.slice(1)} from ${target}${count > 1 ? ` (again: pause ${count})` : ""}`,
425
+ ruleId: finding.ruleId, findingId: finding.id,
426
+ };
427
+ this.local.set(key(target, rule.scope), pause);
428
+ this.changes++;
429
+ this.recent.push(now);
430
+ if (this.reports.length >= 200)
431
+ this.reports.shift();
432
+ this.reports.push({
433
+ target, scope: pause.scope, until: new Date(pause.until).toISOString(), reason: pause.reason, ruleId: finding.ruleId,
434
+ findingId: finding.id, source: "auto", dryRun: Boolean(pause.dryRun), strike: count,
435
+ });
436
+ return pause;
437
+ }
438
+ catch {
439
+ return null;
440
+ }
441
+ }
442
+ /** New pauses to send to the workspace; taken once. */
443
+ takeReports() { return this.reports.splice(0); }
444
+ /** Puts reports back after a failed send (newest kept when over the cap). */
445
+ returnReports(reports) { this.reports = [...reports, ...this.reports].slice(-200); }
446
+ /** Every pause in force now (not dry runs), local and remote, for a deny list. */
447
+ active() {
448
+ const now = this.now();
449
+ const out = new Map();
450
+ for (const map of [this.remote, this.local])
451
+ for (const [k, p] of map)
452
+ if (p.until > now && !p.dryRun && !out.has(k))
453
+ out.set(k, p);
454
+ return [...out.values()].filter(p => !this.isAllowlistedTarget(p.target));
455
+ }
456
+ isAllowlistedTarget(target) {
457
+ if (target.startsWith("user:"))
458
+ return this.allow.users.has(target);
459
+ const parsed = parseIp(target.split("/")[0]);
460
+ return Boolean(parsed && !target.includes("/") && this.allowedIp(parsed));
461
+ }
462
+ /** Once a minute: forget expired pauses, old strikes and lifted grace periods. */
463
+ maintain(now) {
464
+ const minute = Math.floor(now / MINUTE);
465
+ if (minute === this.maintainedMinute)
466
+ return;
467
+ this.maintainedMinute = minute;
468
+ for (const [k, p] of this.local)
469
+ if (p.until <= now)
470
+ this.local.delete(k);
471
+ for (const [k, p] of this.remote)
472
+ if (p.until <= now)
473
+ this.remote.delete(k);
474
+ for (const [t, s] of this.strikes)
475
+ if (s.last <= now - DAY)
476
+ this.strikes.delete(t);
477
+ for (const [t, until] of this.lifted)
478
+ if (until <= now)
479
+ this.lifted.delete(t);
480
+ }
481
+ }
482
+ function verdictFor(pause, now) {
483
+ return {
484
+ paused: true, scope: pause.scope, reason: pause.reason, target: pause.target,
485
+ until: new Date(pause.until).toISOString(), retryAfter: Math.max(1, Math.ceil((pause.until - now) / 1000)),
486
+ };
487
+ }
488
+ /** A pause from the config, validated: unknown, expired or malformed ones are dropped. */
489
+ function readPause(value, now) {
490
+ if (!value || typeof value !== "object")
491
+ return null;
492
+ const v = value;
493
+ const target = typeof v.target === "string" ? normalizeTarget(v.target) : null;
494
+ const scope = PAUSE_SCOPES.includes(v.scope) ? v.scope : "all";
495
+ const until = typeof v.until === "string" ? Date.parse(v.until) : Number.NaN;
496
+ if (!target || !Number.isFinite(until) || until <= now)
497
+ return null;
498
+ return {
499
+ target, scope, until: Math.min(until, now + 30 * DAY),
500
+ reason: typeof v.reason === "string" ? v.reason.slice(0, 200) : "Paused in the dashboard",
501
+ ruleId: typeof v.ruleId === "string" ? v.ruleId.slice(0, 100) : undefined,
502
+ source: v.source === "auto" ? "auto" : "manual",
503
+ dryRun: v.dryRun === true,
504
+ };
505
+ }
506
+ /** The body an app returns to a paused sender: generic, so it tells an attacker nothing about why. */
507
+ export function pausedBody() {
508
+ return JSON.stringify({ error: "Too many requests. Try again later." });
509
+ }
510
+ //# sourceMappingURL=monitor-pause.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"monitor-pause.js","sourceRoot":"","sources":["../src/monitor-pause.ts"],"names":[],"mappings":"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"}
@@ -49,6 +49,11 @@ export interface PipelineOptions {
49
49
  * are rolled up as not applicable, and its own framework's rules count in full.
50
50
  */
51
51
  stack?: Stack;
52
+ /**
53
+ * Called for every finding raised about a sender, new or a repeat folded into one already
54
+ * queued (the pause book decides whether to pause them). Errors are ignored.
55
+ */
56
+ onRaise?: (finding: MonitorFinding) => void;
52
57
  }
53
58
  /**
54
59
  * A hard cap on detection time per minute, so monitoring can never take more than a fixed share of
@@ -146,6 +151,7 @@ export declare class MonitorPipeline {
146
151
  /** Queues a finding produced elsewhere (dependency checks) under its own stable id, once. */
147
152
  addFinding(finding: MonitorFinding): boolean;
148
153
  private raise;
154
+ private notify;
149
155
  private emitSuppressed;
150
156
  private enqueue;
151
157
  /**
@@ -562,6 +562,7 @@ export class MonitorPipeline {
562
562
  const existing = this.seen.get(fp);
563
563
  if (existing && existing.bucket === bucket) {
564
564
  existing.finding.count++; // still queued: the batch carries the new count
565
+ this.notify(existing.finding);
565
566
  return;
566
567
  }
567
568
  const flood = Math.floor(now / FLOOD_WINDOW_MS);
@@ -573,12 +574,21 @@ export class MonitorPipeline {
573
574
  if (this.floodCount >= MAX_NEW_FINDINGS_PER_10_MIN) {
574
575
  const worst = this.suppressed && RANK[this.suppressed.severity] >= RANK[partial.severity] ? this.suppressed.severity : partial.severity;
575
576
  this.suppressed = { count: (this.suppressed?.count ?? 0) + 1, severity: worst };
577
+ // Not sent one by one, but a flood is exactly when pausing its senders matters.
578
+ this.notify({ id: `${fp}-${bucket}`, ...partial, count: 1, firstSeen: new Date(now).toISOString() });
576
579
  return;
577
580
  }
578
581
  this.floodCount++;
579
582
  const finding = { id: `${fp}-${bucket}`, ...partial, count: 1, firstSeen: new Date(now).toISOString() };
580
583
  this.seen.set(fp, { bucket, finding, at: now });
581
584
  this.enqueue(finding);
585
+ this.notify(finding);
586
+ }
587
+ notify(finding) {
588
+ try {
589
+ this.opts.onRaise?.(finding);
590
+ }
591
+ catch { /* never let a hook break detection */ }
582
592
  }
583
593
  emitSuppressed() {
584
594
  if (!this.suppressed)