@osqd/bothandlerjs 0.3.0
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.
- package/CHANGELOG.md +516 -0
- package/LICENSE +102 -0
- package/README.md +308 -0
- package/bin/bothandlerjs.mjs +14 -0
- package/dist/actions/index.d.ts +22 -0
- package/dist/actions/types.d.ts +59 -0
- package/dist/adapters/fastify.d.ts +43 -0
- package/dist/adapters/fetch.d.ts +51 -0
- package/dist/adapters/index.cjs +723 -0
- package/dist/adapters/index.cjs.map +1 -0
- package/dist/adapters/index.d.ts +8 -0
- package/dist/adapters/index.js +692 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/koa.d.ts +27 -0
- package/dist/adapters/node.d.ts +29 -0
- package/dist/adapters/shared.d.ts +39 -0
- package/dist/audit.d.ts +172 -0
- package/dist/challenge/index.d.ts +176 -0
- package/dist/challenge/language.d.ts +57 -0
- package/dist/challenge/page.d.ts +42 -0
- package/dist/challenge/pow.d.ts +33 -0
- package/dist/challenge/token.d.ts +72 -0
- package/dist/cli.cjs +13388 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.ts +5 -0
- package/dist/cli.js +13358 -0
- package/dist/cli.js.map +1 -0
- package/dist/client/index.cjs +139 -0
- package/dist/client/index.cjs.map +1 -0
- package/dist/client/index.d.ts +63 -0
- package/dist/client/index.js +112 -0
- package/dist/client/index.js.map +1 -0
- package/dist/config.d.ts +316 -0
- package/dist/core.d.ts +443 -0
- package/dist/corpus/adversarial.d.ts +2 -0
- package/dist/corpus/advertising-email.d.ts +2 -0
- package/dist/corpus/ai-crawlers.d.ts +2 -0
- package/dist/corpus/benign-bots.d.ts +2 -0
- package/dist/corpus/cdn-gateways.d.ts +2 -0
- package/dist/corpus/cookies.d.ts +49 -0
- package/dist/corpus/crawlers-regional.d.ts +2 -0
- package/dist/corpus/crawlers-vertical.d.ts +2 -0
- package/dist/corpus/headers.d.ts +403 -0
- package/dist/corpus/humans-apps.d.ts +2 -0
- package/dist/corpus/humans-browsers.d.ts +2 -0
- package/dist/corpus/humans.d.ts +2 -0
- package/dist/corpus/index.cjs +4196 -0
- package/dist/corpus/index.cjs.map +1 -0
- package/dist/corpus/index.d.ts +37 -0
- package/dist/corpus/index.js +4132 -0
- package/dist/corpus/index.js.map +1 -0
- package/dist/corpus/infrastructure.d.ts +17 -0
- package/dist/corpus/libraries-extended.d.ts +2 -0
- package/dist/corpus/ranges.d.ts +31 -0
- package/dist/corpus/reputation.d.ts +21 -0
- package/dist/corpus/runner.d.ts +130 -0
- package/dist/corpus/schema.d.ts +185 -0
- package/dist/corpus/tooling.d.ts +2 -0
- package/dist/corpus/unwanted.d.ts +2 -0
- package/dist/crawler-ranges.d.ts +113 -0
- package/dist/dashboard/client.generated.d.ts +7 -0
- package/dist/dashboard/feed.d.ts +186 -0
- package/dist/dashboard/index.d.ts +5 -0
- package/dist/dashboard/page.d.ts +51 -0
- package/dist/dashboard/parse-request.d.ts +28 -0
- package/dist/dashboard/preview.d.ts +45 -0
- package/dist/dashboard/server.d.ts +70 -0
- package/dist/dashboard/types.d.ts +714 -0
- package/dist/detectors/accept-signature.d.ts +6 -0
- package/dist/detectors/browsing-coherence.d.ts +44 -0
- package/dist/detectors/cadence.d.ts +28 -0
- package/dist/detectors/clearance.d.ts +25 -0
- package/dist/detectors/client-hints.d.ts +19 -0
- package/dist/detectors/client-signals.d.ts +27 -0
- package/dist/detectors/crawl-breadth.d.ts +28 -0
- package/dist/detectors/crawler-verification.d.ts +39 -0
- package/dist/detectors/fetch-metadata.d.ts +20 -0
- package/dist/detectors/header-integrity.d.ts +26 -0
- package/dist/detectors/header-order.d.ts +27 -0
- package/dist/detectors/identity-rotation.d.ts +34 -0
- package/dist/detectors/index.d.ts +56 -0
- package/dist/detectors/ip-intelligence.d.ts +30 -0
- package/dist/detectors/known-bots.d.ts +94 -0
- package/dist/detectors/probe-signature.d.ts +50 -0
- package/dist/detectors/rate-anomaly.d.ts +36 -0
- package/dist/detectors/self-identified.d.ts +33 -0
- package/dist/detectors/session-integrity.d.ts +24 -0
- package/dist/detectors/tls-fingerprint.d.ts +34 -0
- package/dist/detectors/trap.d.ts +98 -0
- package/dist/detectors/types.d.ts +70 -0
- package/dist/detectors/ua-coherence.d.ts +32 -0
- package/dist/evidence.d.ts +45 -0
- package/dist/facts.d.ts +46 -0
- package/dist/index.cjs +9600 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +63 -0
- package/dist/index.js +9444 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/async.d.ts +16 -0
- package/dist/internal/clock.d.ts +17 -0
- package/dist/internal/crypto.d.ts +26 -0
- package/dist/internal/dns.d.ts +100 -0
- package/dist/internal/emitter.d.ts +18 -0
- package/dist/internal/http.d.ts +22 -0
- package/dist/internal/ip.d.ts +73 -0
- package/dist/internal/lru.d.ts +34 -0
- package/dist/internal/matcher.d.ts +41 -0
- package/dist/internal/pattern.d.ts +14 -0
- package/dist/internal/ua.d.ts +92 -0
- package/dist/metrics.d.ts +120 -0
- package/dist/notify/hub.d.ts +70 -0
- package/dist/notify/index.d.ts +7 -0
- package/dist/notify/redact.d.ts +48 -0
- package/dist/notify/sinks.d.ts +62 -0
- package/dist/notify/types.d.ts +56 -0
- package/dist/policy/index.d.ts +7 -0
- package/dist/policy/match.d.ts +20 -0
- package/dist/policy/policy.d.ts +123 -0
- package/dist/policy/presets.d.ts +161 -0
- package/dist/policy/types.d.ts +155 -0
- package/dist/robots.d.ts +83 -0
- package/dist/state.d.ts +161 -0
- package/dist/stores/index.d.ts +5 -0
- package/dist/stores/memory.d.ts +33 -0
- package/dist/stores/redis.d.ts +59 -0
- package/dist/stores/types.d.ts +37 -0
- package/dist/types.d.ts +250 -0
- package/docs/challenge/index.md +152 -0
- package/docs/challenge/localisation.md +105 -0
- package/docs/concepts/actors.md +113 -0
- package/docs/concepts/evidence.md +133 -0
- package/docs/concepts/the-guard.md +121 -0
- package/docs/concepts/threat-model.md +120 -0
- package/docs/concepts/verdicts.md +108 -0
- package/docs/course/01-first-assessment.md +178 -0
- package/docs/course/02-proof-and-suspicion.md +164 -0
- package/docs/course/03-verdicts-and-scores.md +160 -0
- package/docs/course/04-the-guard.md +200 -0
- package/docs/course/05-detectors.md +178 -0
- package/docs/course/06-identity.md +161 -0
- package/docs/course/07-actors.md +178 -0
- package/docs/course/08-traps.md +183 -0
- package/docs/course/09-rules.md +224 -0
- package/docs/course/10-actions-and-presets.md +216 -0
- package/docs/course/11-the-challenge.md +227 -0
- package/docs/course/12-going-live.md +211 -0
- package/docs/course/13-operating-it.md +238 -0
- package/docs/course/14-scaling.md +195 -0
- package/docs/course/15-extending.md +259 -0
- package/docs/course/16-proving-it.md +266 -0
- package/docs/course/index.md +112 -0
- package/docs/design/decisions.md +216 -0
- package/docs/detection/client-signals.md +96 -0
- package/docs/detection/detectors.md +376 -0
- package/docs/detection/index.md +134 -0
- package/docs/detection/signatures.md +111 -0
- package/docs/detection/verification.md +123 -0
- package/docs/detection/writing-a-detector.md +145 -0
- package/docs/index.md +100 -0
- package/docs/integration/adapters.md +138 -0
- package/docs/integration/client-ip.md +114 -0
- package/docs/integration/index.md +55 -0
- package/docs/integration/stores.md +76 -0
- package/docs/operations/audit.md +116 -0
- package/docs/operations/dashboard.md +725 -0
- package/docs/operations/index.md +101 -0
- package/docs/operations/metrics.md +74 -0
- package/docs/operations/notifications.md +72 -0
- package/docs/operations/runtime-changes.md +118 -0
- package/docs/policy/actions.md +196 -0
- package/docs/policy/index.md +84 -0
- package/docs/policy/presets.md +191 -0
- package/docs/policy/robots.md +128 -0
- package/docs/policy/rules.md +134 -0
- package/docs/reference/api.md +126 -0
- package/docs/reference/configuration.md +175 -0
- package/docs/start/choosing-a-policy.md +127 -0
- package/docs/start/first-integration.md +127 -0
- package/docs/start/installation.md +63 -0
- package/docs/start/upgrading.md +72 -0
- package/docs/testing/cli.md +119 -0
- package/docs/testing/corpus.md +129 -0
- package/docs/testing/index.md +61 -0
- package/docs/testing/replay.md +102 -0
- package/docs/testing/try-it.md +189 -0
- package/package.json +145 -0
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import { IpRangeSet } from "./internal/ip.js";
|
|
2
|
+
import type { PresetName } from "./policy/presets.js";
|
|
3
|
+
import type { Clock } from "./internal/clock.js";
|
|
4
|
+
import type { DnsResolver } from "./internal/dns.js";
|
|
5
|
+
import type { Detector } from "./detectors/types.js";
|
|
6
|
+
import type { BotSignature } from "./detectors/known-bots.js";
|
|
7
|
+
import type { ActionParams, FalsePositivePolicy, Rule } from "./policy/types.js";
|
|
8
|
+
import type { CustomHandler } from "./actions/types.js";
|
|
9
|
+
import type { BotHandlerStore } from "./stores/types.js";
|
|
10
|
+
import type { AuditOptions, TrafficAnomaly } from "./audit.js";
|
|
11
|
+
import type { Decision } from "./policy/types.js";
|
|
12
|
+
import type { MetricsOptions } from "./metrics.js";
|
|
13
|
+
import type { NotificationOptions } from "./notify/hub.js";
|
|
14
|
+
import type { ChallengeOptions } from "./challenge/index.js";
|
|
15
|
+
import type { Assessment, RequestFacts } from "./types.js";
|
|
16
|
+
/**
|
|
17
|
+
* How the client address is recovered from behind a proxy.
|
|
18
|
+
*
|
|
19
|
+
* This is the highest-consequence configuration in the library and the easiest to get
|
|
20
|
+
* wrong, so it has no convenient default: `trustProxy` is off, and with it off the
|
|
21
|
+
* socket address is used and `X-Forwarded-For` is ignored entirely.
|
|
22
|
+
*
|
|
23
|
+
* The reason is that `X-Forwarded-For` is a *client-supplied header*. If you trust it
|
|
24
|
+
* without knowing how many proxies actually sit in front of you, anyone can prepend a
|
|
25
|
+
* fake hop and choose the address you rate-limit, allowlist and block on. The
|
|
26
|
+
* failure is silent, and it turns every per-actor mechanism here into an attacker
|
|
27
|
+
* input. Configure `trustedProxies` if you can — it is the only variant that does not
|
|
28
|
+
* depend on getting a number right.
|
|
29
|
+
*/
|
|
30
|
+
export interface ProxyConfig {
|
|
31
|
+
/** Read the forwarded header at all. Default false. */
|
|
32
|
+
trustProxy?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* CIDRs of proxies you operate. The header is walked from the right, discarding
|
|
35
|
+
* addresses inside these ranges, and the first address outside them is the client.
|
|
36
|
+
* Robust against an extra hop appearing, and the recommended setting.
|
|
37
|
+
*
|
|
38
|
+
* The connecting peer counts as the first hop and is checked the same way. A
|
|
39
|
+
* request that arrives from outside these ranges did not come through your proxies,
|
|
40
|
+
* so its forwarded header is not evidence of anything and the socket address is
|
|
41
|
+
* used instead — which is what makes this variant safe on a server that is
|
|
42
|
+
* reachable both through the load balancer and directly.
|
|
43
|
+
*/
|
|
44
|
+
trustedProxies?: readonly string[];
|
|
45
|
+
/**
|
|
46
|
+
* How many proxies sit in front of this process, when you cannot enumerate them.
|
|
47
|
+
* The Nth address from the right is taken. Default 1.
|
|
48
|
+
*
|
|
49
|
+
* If the chain turns out to be shorter than this, the socket address is used
|
|
50
|
+
* instead: a short chain means the request did not come through the expected
|
|
51
|
+
* topology, and every entry in it is then client-controlled. Prefer
|
|
52
|
+
* `trustedProxies`, which does not depend on getting a count right.
|
|
53
|
+
*/
|
|
54
|
+
hops?: number;
|
|
55
|
+
/** Header carrying the chain. Default `"x-forwarded-for"`. */
|
|
56
|
+
header?: string;
|
|
57
|
+
}
|
|
58
|
+
export interface BotHandlerConfig {
|
|
59
|
+
/** Replaces the built-in detector set entirely. */
|
|
60
|
+
detectors?: readonly Detector[];
|
|
61
|
+
/** Appended to the built-in set. Ignored when `detectors` is given. */
|
|
62
|
+
extraDetectors?: readonly Detector[];
|
|
63
|
+
/** A named starting policy. Combined with `rules`, which are evaluated first. */
|
|
64
|
+
preset?: PresetName;
|
|
65
|
+
/** Your own rules, evaluated before any preset's. */
|
|
66
|
+
rules?: readonly Rule[];
|
|
67
|
+
/** Action when nothing matches. Default `allow`. */
|
|
68
|
+
defaultAction?: import("./policy/types.js").ActionName;
|
|
69
|
+
defaultActionParams?: ActionParams;
|
|
70
|
+
/** How strictly terminal actions are gated. Default `strict`. See {@link FalsePositivePolicy}. */
|
|
71
|
+
falsePositivePolicy?: FalsePositivePolicy;
|
|
72
|
+
/** Substituted when the guard blocks a terminal action. Default `challenge`. */
|
|
73
|
+
fallbackAction?: import("./policy/types.js").ActionName;
|
|
74
|
+
/** Score needed for a terminal action under `balanced`. Default 85. */
|
|
75
|
+
terminalScoreThreshold?: number;
|
|
76
|
+
/** Score at or above which an unproven request is `suspected-bot`. Default 60. */
|
|
77
|
+
suspectThreshold?: number;
|
|
78
|
+
/**
|
|
79
|
+
* Reject `certain` evidence that carries no `deterministicBasis`. Defaults to true
|
|
80
|
+
* unless `NODE_ENV` is `production`, where it downgrades to a warning so a
|
|
81
|
+
* third-party detector cannot take a live site down.
|
|
82
|
+
*/
|
|
83
|
+
strictEvidence?: boolean;
|
|
84
|
+
/** Addresses and CIDRs exempt from detection entirely. Your monitors, your office, your CI. */
|
|
85
|
+
allowlist?: readonly string[];
|
|
86
|
+
/** Addresses and CIDRs treated as proven automation. An explicit local decision. */
|
|
87
|
+
denylist?: readonly string[];
|
|
88
|
+
/** Hosting-provider ranges. Supply your own; none ships with the library. */
|
|
89
|
+
datacenterRanges?: readonly string[];
|
|
90
|
+
/** Published crawler ranges, keyed by signature id, e.g. `{ gptbot: ["1.2.3.0/24"] }`. */
|
|
91
|
+
crawlerRanges?: Readonly<Record<string, readonly string[]>>;
|
|
92
|
+
proxy?: ProxyConfig;
|
|
93
|
+
/**
|
|
94
|
+
* Derives the key an actor is tracked under. Defaults to the client address.
|
|
95
|
+
*
|
|
96
|
+
* Worth replacing. An address is a poor identity — shared by a whole office,
|
|
97
|
+
* changed by a phone every few minutes — and every behavioural detector is only as
|
|
98
|
+
* good as this function. A session id, an authenticated user id, or an address
|
|
99
|
+
* combined with a TLS fingerprint all make the same detectors much sharper.
|
|
100
|
+
*/
|
|
101
|
+
actorKey?: (facts: RequestFacts) => string;
|
|
102
|
+
/** Paths detection skips entirely: health checks, your own polling endpoints, static assets. */
|
|
103
|
+
ignorePaths?: readonly (string | RegExp)[];
|
|
104
|
+
/**
|
|
105
|
+
* Lets your application declare a request human — an authenticated session, a
|
|
106
|
+
* completed payment, whatever bar you set. Produces `certain` human evidence, the
|
|
107
|
+
* only conclusive human signal available, because it comes from you and not from
|
|
108
|
+
* the client.
|
|
109
|
+
*/
|
|
110
|
+
isHuman?: (facts: RequestFacts) => boolean;
|
|
111
|
+
/** Enables the challenge action. Without it, rules asking for one degrade to `tag`. */
|
|
112
|
+
challenge?: Omit<ChallengeOptions, "store" | "clock">;
|
|
113
|
+
store?: BotHandlerStore;
|
|
114
|
+
/**
|
|
115
|
+
* Share `priorConfirmations` between replicas through the store. Default false.
|
|
116
|
+
*
|
|
117
|
+
* Behavioural state is process-local by design — see `state.ts` — because a round
|
|
118
|
+
* trip per request would buy accuracy for signals that are only ever allowed to raise
|
|
119
|
+
* suspicion. A **confirmation is not one of those.** `confirmed-bot` is a proven
|
|
120
|
+
* verdict: something declared itself, forged an identity, or walked into a trap. That
|
|
121
|
+
* is a fact about the client rather than a judgement about it, and behind eight
|
|
122
|
+
* replicas a fact established on one of them is unknown to the other seven — so a
|
|
123
|
+
* rule reading `minPriorConfirmations: 1` fires roughly an eighth as often as it
|
|
124
|
+
* reads.
|
|
125
|
+
*
|
|
126
|
+
* So proof travels and suspicion stays home. The cost is one store read the **first
|
|
127
|
+
* time each instance sees an actor** — not one per request — and it is never awaited:
|
|
128
|
+
* the request that triggered it is judged with whatever arrived before the detectors
|
|
129
|
+
* finished, and every request after it has the number. A store outage means the count
|
|
130
|
+
* falls back to what this process saw itself, which is where it was before.
|
|
131
|
+
*
|
|
132
|
+
* Needs a shared `store`. With the default in-memory one there is nothing to share
|
|
133
|
+
* with, and setting this does nothing at all.
|
|
134
|
+
*/
|
|
135
|
+
shareConfirmations?: boolean;
|
|
136
|
+
notifications?: NotificationOptions;
|
|
137
|
+
handlers?: readonly CustomHandler[];
|
|
138
|
+
/** Replaces the built-in signature database. */
|
|
139
|
+
signatures?: readonly BotSignature[];
|
|
140
|
+
/** Appended to the built-in database. */
|
|
141
|
+
extraSignatures?: readonly BotSignature[];
|
|
142
|
+
/** Budget for each `io` detector, ms. Default 300. Exceeding it drops that detector, not the request. */
|
|
143
|
+
detectorTimeoutMs?: number;
|
|
144
|
+
/** How long an idle actor is remembered, ms. Default 900000. */
|
|
145
|
+
actorWindowMs?: number;
|
|
146
|
+
/**
|
|
147
|
+
* Maximum actors tracked concurrently. Default 20000.
|
|
148
|
+
*
|
|
149
|
+
* This is a memory budget: see the cap arithmetic in `state.ts`. Past the limit the
|
|
150
|
+
* least recently seen actor is evicted, never one still sending traffic.
|
|
151
|
+
*/
|
|
152
|
+
maxActors?: number;
|
|
153
|
+
/**
|
|
154
|
+
* Put verdict headers on the *response*. Default false.
|
|
155
|
+
*
|
|
156
|
+
* Leave it off in production. An `X-Bot-Score` in the response is a live feedback
|
|
157
|
+
* signal for anyone tuning a scraper against you — they change one header, watch
|
|
158
|
+
* the number fall, and iterate. Request-side tagging gives your application the
|
|
159
|
+
* same information and tells the client nothing.
|
|
160
|
+
*/
|
|
161
|
+
exposeVerdictHeaders?: boolean;
|
|
162
|
+
/**
|
|
163
|
+
* Collect counters. Default true — see {@link BotHandler.metrics}. Switching it off
|
|
164
|
+
* saves a few integer increments per request and costs you the ability to see what
|
|
165
|
+
* your policy is doing.
|
|
166
|
+
*/
|
|
167
|
+
metrics?: boolean | MetricsOptions;
|
|
168
|
+
/**
|
|
169
|
+
* Watches the *shape* of your traffic and raises {@link BotHandlerConfig.onAnomaly}
|
|
170
|
+
* when it changes. `false` switches it off; the default is on with the shipped
|
|
171
|
+
* checks. See {@link AuditOptions}.
|
|
172
|
+
*/
|
|
173
|
+
audit?: AuditOptions | false;
|
|
174
|
+
resolver?: DnsResolver;
|
|
175
|
+
clock?: Clock;
|
|
176
|
+
/** Every assessment, including the ones that concluded nothing. The firehose. */
|
|
177
|
+
onAssessment?: (assessment: Assessment) => void;
|
|
178
|
+
/** Every decision, paired with the assessment behind it. */
|
|
179
|
+
onDecision?: (event: {
|
|
180
|
+
assessment: Assessment;
|
|
181
|
+
decision: Decision;
|
|
182
|
+
}) => void;
|
|
183
|
+
/** A request that was actually denied: `block`, `drop` or `redirect`. */
|
|
184
|
+
onDenial?: (event: {
|
|
185
|
+
assessment: Assessment;
|
|
186
|
+
decision: Decision;
|
|
187
|
+
}) => void;
|
|
188
|
+
/**
|
|
189
|
+
* The safety guard replaced a terminal action with something recoverable.
|
|
190
|
+
*
|
|
191
|
+
* The most useful hook here, and the one worth paging on a rise in: it means your
|
|
192
|
+
* rules are asking to deny requests the evidence does not prove.
|
|
193
|
+
*/
|
|
194
|
+
onDowngrade?: (event: {
|
|
195
|
+
assessment: Assessment;
|
|
196
|
+
decision: Decision;
|
|
197
|
+
}) => void;
|
|
198
|
+
/** A challenge was issued, solved or rejected. */
|
|
199
|
+
onChallenge?: (event: {
|
|
200
|
+
phase: "issued" | "solved" | "rejected";
|
|
201
|
+
actorKey?: string | undefined;
|
|
202
|
+
}) => void;
|
|
203
|
+
/**
|
|
204
|
+
* A detector threw or timed out. Operational: usually a resolver or a store, and a
|
|
205
|
+
* rising rate means detection is degraded rather than that traffic changed.
|
|
206
|
+
*/
|
|
207
|
+
onDetectorFailure?: (event: {
|
|
208
|
+
detector: string;
|
|
209
|
+
reason: string;
|
|
210
|
+
message: string;
|
|
211
|
+
requestId: string;
|
|
212
|
+
}) => void;
|
|
213
|
+
/** The rule set was replaced at runtime. Your audit trail for {@link BotHandler.updatePolicy}. */
|
|
214
|
+
onPolicyChange?: (event: {
|
|
215
|
+
rules: readonly string[];
|
|
216
|
+
warnings: readonly string[];
|
|
217
|
+
by?: string | undefined;
|
|
218
|
+
}) => void;
|
|
219
|
+
/**
|
|
220
|
+
* The guard settings changed at runtime — `falsePositivePolicy`, `fallbackAction`,
|
|
221
|
+
* `defaultAction`, `terminalScoreThreshold` or `suspectThreshold`.
|
|
222
|
+
*
|
|
223
|
+
* Worth its own hook rather than folding into `onPolicyChange`: this is the change
|
|
224
|
+
* that decides whether an unproven verdict can deny anybody, and it is the one an
|
|
225
|
+
* audit trail wants named. Both the before and the after travel with it.
|
|
226
|
+
*/
|
|
227
|
+
onGuardChange?: (event: {
|
|
228
|
+
before: import("./policy/policy.js").GuardSettings & {
|
|
229
|
+
suspectThreshold: number;
|
|
230
|
+
};
|
|
231
|
+
after: import("./policy/policy.js").GuardSettings & {
|
|
232
|
+
suspectThreshold: number;
|
|
233
|
+
};
|
|
234
|
+
by?: string | undefined;
|
|
235
|
+
}) => void;
|
|
236
|
+
/**
|
|
237
|
+
* A range set was replaced at runtime.
|
|
238
|
+
*
|
|
239
|
+
* The allowlist is the one worth watching: an address on it is not judged leniently,
|
|
240
|
+
* it is not judged at all, so a change to it changes what your bot handling can see.
|
|
241
|
+
*/
|
|
242
|
+
onRangeChange?: (event: {
|
|
243
|
+
name: string;
|
|
244
|
+
size: number;
|
|
245
|
+
entries: readonly string[];
|
|
246
|
+
by?: string | undefined;
|
|
247
|
+
}) => void;
|
|
248
|
+
/** An actor's behavioural memory was forgotten, or the actor was cleared as human. */
|
|
249
|
+
onActorChange?: (event: {
|
|
250
|
+
key: string;
|
|
251
|
+
action: "forget" | "clear";
|
|
252
|
+
until?: number | undefined;
|
|
253
|
+
by?: string | undefined;
|
|
254
|
+
}) => void;
|
|
255
|
+
/** The audit noticed the traffic change shape. See {@link AuditOptions}. */
|
|
256
|
+
onAnomaly?: (anomaly: TrafficAnomaly) => void;
|
|
257
|
+
/** Called when a detector, sink or store fails. Wire it to your logs. */
|
|
258
|
+
onError?: (error: unknown, context: {
|
|
259
|
+
source: string;
|
|
260
|
+
}) => void;
|
|
261
|
+
/** Called on a misconfiguration noticed at runtime. Wire it to your logs. */
|
|
262
|
+
onWarning?: (message: string) => void;
|
|
263
|
+
}
|
|
264
|
+
export interface ResolvedConfig {
|
|
265
|
+
detectors: Detector[];
|
|
266
|
+
rules: Rule[];
|
|
267
|
+
ranges: Map<string, IpRangeSet>;
|
|
268
|
+
signatures: readonly BotSignature[];
|
|
269
|
+
proxy: Required<Omit<ProxyConfig, "trustedProxies">> & {
|
|
270
|
+
trustedProxies: IpRangeSet | undefined;
|
|
271
|
+
};
|
|
272
|
+
actorKey: (facts: RequestFacts) => string;
|
|
273
|
+
ignorePaths: readonly (string | RegExp)[];
|
|
274
|
+
suspectThreshold: number;
|
|
275
|
+
strictEvidence: boolean;
|
|
276
|
+
detectorTimeoutMs: number;
|
|
277
|
+
actorWindowMs: number;
|
|
278
|
+
maxActors: number;
|
|
279
|
+
exposeVerdictHeaders: boolean;
|
|
280
|
+
clock: Clock;
|
|
281
|
+
onError: (error: unknown, context: {
|
|
282
|
+
source: string;
|
|
283
|
+
}) => void;
|
|
284
|
+
onWarning: (message: string) => void;
|
|
285
|
+
/** Problems found while resolving. Fatal ones throw; these are advisory. */
|
|
286
|
+
warnings: string[];
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Checks a rule list for the mistakes that are silent at runtime.
|
|
290
|
+
*
|
|
291
|
+
* Shared with {@link BotHandler.updatePolicy}, because rules can now be replaced while
|
|
292
|
+
* the process runs and a list swapped in from a dashboard deserves the same reading
|
|
293
|
+
* as one written in a config file. Structural problems throw; problems that merely
|
|
294
|
+
* make a rule useless are returned as warnings, since a policy that refuses to load
|
|
295
|
+
* over a dead rule is worse than one that says so.
|
|
296
|
+
*/
|
|
297
|
+
export declare function validateRules(rules: readonly Rule[]): string[];
|
|
298
|
+
export declare class ConfigError extends Error {
|
|
299
|
+
readonly name = "ConfigError";
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Validates and normalises configuration.
|
|
303
|
+
*
|
|
304
|
+
* Invalid input throws here, at construction, rather than degrading quietly at
|
|
305
|
+
* request time. A mistyped CIDR that silently matches nothing is a security control
|
|
306
|
+
* that looks configured and is not — the worst possible state to be in, because
|
|
307
|
+
* everything appears to be working.
|
|
308
|
+
*/
|
|
309
|
+
export declare function resolveConfig(config?: BotHandlerConfig): ResolvedConfig;
|
|
310
|
+
/**
|
|
311
|
+
* Recovers the client address from the socket address and the request headers.
|
|
312
|
+
*
|
|
313
|
+
* Exported because getting this right matters more than almost anything else here,
|
|
314
|
+
* and you may want to call it directly or test it in isolation.
|
|
315
|
+
*/
|
|
316
|
+
export declare function resolveClientIp(socketAddress: string | undefined, headers: Record<string, string | undefined>, proxy: ResolvedConfig["proxy"]): string;
|