botshield-express 0.1.0 → 0.2.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/README.md CHANGED
@@ -52,19 +52,108 @@ app.get('/login', (req, res) => {
52
52
  | `protectedRoutes` | `[]` | Patterns the middleware scores; every other request passes through untouched |
53
53
  | `fingerprintCookie` | – | Overrides the cookie name the browser sensor sets |
54
54
  | `timeoutMs` | – | Bounded time budget for the engine call |
55
+ | `tls` | – | `{ ca, rejectUnauthorized, servername }` for an engine with a private cert |
55
56
  | `cacheMaxEntries` | `5000` | Entries retained in the in-process verdict cache |
57
+ | `trustedProxies` | `[]` | Addresses/CIDRs of the hops in front of the app whose IP header is believed |
58
+ | `clientIpHeader` | `x-real-ip` | Header a trusted proxy asserts the client in |
59
+ | `trustForwardedChain` | `false` | Also walk `X-Forwarded-For` from the nearest hop outward |
56
60
  | `onVerdict` | – | `(context, req) => void` hook |
57
61
  | `logger` | – | `{ warn(obj, msg?), error(obj, msg?) }` |
58
62
 
63
+ ## Client IP resolution
64
+
65
+ The address the engine records is used for rate limiting, the blocklist, honeypot
66
+ flags, the verdict cache, sessions and alerts. Only the TCP peer address cannot
67
+ be forged — completing the handshake requires a routable source — so a header is
68
+ believed **only when it arrives from a proxy you name**:
69
+
70
+ ```ts
71
+ app.use(
72
+ botShield({
73
+ engineUrl: 'http://engine:4000',
74
+ appName: 'my-app',
75
+ // The BotShield gateway (or your own ingress) — nothing else may assert an IP.
76
+ trustedProxies: ['10.0.0.10/32'],
77
+ }),
78
+ );
79
+ ```
80
+
81
+ With `trustedProxies` empty (the default) every header is ignored and the peer
82
+ address is used. That is deliberate: a believed-but-forged address is
83
+ indistinguishable from a real one once stored, and an attacker who can write the
84
+ header can also name a third party and have them blocked.
85
+
86
+ `clientIpHeader` defaults to `x-real-ip`, which the BotShield gateway always
87
+ overwrites with its own resolved observation. Point it at `cf-connecting-ip`
88
+ when a Cloudflare-fronted ingress is the trusted hop.
89
+
90
+ `trustForwardedChain` additionally honours an `X-Forwarded-For` chain, walking
91
+ from the nearest hop outward and stopping at the first untrusted address. Enable
92
+ it only when every trusted hop *appends* its own observation: a hop that relays
93
+ the client's chain verbatim makes this a spoofing hole, because a relayed claim
94
+ and an asserted one look identical.
95
+
96
+ Do not rely on Express's `trust proxy: true`. It makes `req.ip` whatever the
97
+ client sent. The SDK warns once when it detects that setting.
98
+
99
+ ## TLS and self-signed certificates
100
+
101
+ Self-hosted BotShield serves the gateway with a self-signed certificate whose SAN
102
+ covers `localhost` and `127.0.0.1`, so a client reaching it over HTTPS sees two
103
+ separate failures: an untrusted chain, and — whenever the engine is dialled by an
104
+ address the certificate does not name — a hostname mismatch. Node's `fetch`
105
+ takes no TLS options, so **both look identical from your side: the middleware
106
+ fails open and records nothing.** A verdict of `allow` with
107
+ `req.botshield.requestId === 'unavailable'` is the signature.
108
+
109
+ Pass `tls` to fix it. Trusting the certificate is not enough on its own: the
110
+ hostname check still applies, so supply `servername` when the engine is reached
111
+ by IP or an alias.
112
+
113
+ ```js
114
+ const fs = require('node:fs');
115
+
116
+ app.use(
117
+ botShield({
118
+ engineUrl: 'https://10.0.0.10:8443/botshield',
119
+ appName: 'my-app',
120
+ tls: {
121
+ ca: fs.readFileSync('./certs/dev.crt'), // the gateway's certificate
122
+ servername: 'localhost', // name on the certificate you dial past
123
+ },
124
+ }),
125
+ );
126
+ ```
127
+
128
+ | field | meaning |
129
+ | -------------------- | ------------------------------------------------------------------------------------------- |
130
+ | `ca` | PEM certificate(s) to trust. Required for a private or self-signed CA. |
131
+ | `servername` | Name to verify against the certificate and send as SNI. Required when dialling an IP. |
132
+ | `rejectUnauthorized` | Defaults to `true`. `false` skips chain verification entirely — a development escape hatch. |
133
+
134
+ Supplying `tls` routes the engine call through `node:https`; without it the
135
+ leaner `fetch` path is used unchanged. A dispatcher from the separately installed
136
+ `undici` package cannot be used instead: Node's built-in fetch rejects it with
137
+ `UND_ERR_INVALID_ARG`.
138
+
139
+ A certificate trusted by the host is a cleaner answer than `rejectUnauthorized`:
140
+ prefer adding the deployment's real address to the certificate's SAN, or
141
+ installing a real CA-issued certificate.
142
+
59
143
  ## Exports
60
144
 
61
145
  `botShield`, `VerdictCache`, `matchesProtected`, `createEngineClient`, and the
62
146
  types `BotShieldConfig`, `BotShieldContext`, `BotShieldMiddleware`, `Verdict`,
63
- `VerifyRequest`, `EngineClient`, `Action`, `NextFunction`, `Request`,
64
- `RequestHandler`, `Response`.
147
+ `VerifyRequest`, `TlsOptions`, `EngineClient`, `Action`, `NextFunction`,
148
+ `Request`, `RequestHandler`, `Response`.
65
149
 
66
150
  ## Notes
67
151
 
152
+ - **Upgrading from 0.1.0:** client IP resolution is now scoped to
153
+ `trustedProxies`, so a header is only believed when it arrives from a hop you
154
+ name. Deployments that relied on the old blind `X-Real-IP` trust must set
155
+ `trustedProxies` to their gateway/ingress addresses, or every request will
156
+ fall back to the peer address.
68
157
  - The engine must be reachable. From a container outside the BotShield compose
69
158
  project, join its network first (`docker network ls | grep botshield`).
70
159
  - Verdicts are cached for the TTL the engine returns, and a verdict supplied by
package/dist/client.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Verdict, VerifyRequest } from './types';
1
+ import type { TlsOptions, Verdict, VerifyRequest } from './types';
2
2
  /**
3
3
  * Engine client for the SDK.
4
4
  *
@@ -12,6 +12,8 @@ import type { Verdict, VerifyRequest } from './types';
12
12
  export interface EngineClientOptions {
13
13
  engineUrl: string;
14
14
  timeoutMs?: number;
15
+ /** TLS settings, for an engine served with a private or self-signed certificate. */
16
+ tls?: TlsOptions;
15
17
  }
16
18
  export declare const DEFAULT_TIMEOUT_MS = 1000;
17
19
  export interface EngineClient {
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAEtD;;;;;;;;;GASG;AAEH,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,kBAAkB,OAAQ,CAAC;AAExC,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IACtD,WAAW,CAAC,KAAK,EAAE;QACjB,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,MAAM,CAAC,EAAE,MAAM,CAAC;YAAC,WAAW,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAC/D,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACrC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnB;AA4CD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,YAAY,CAiB7E"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAElE;;;;;;;;;GASG;AAEH,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oFAAoF;IACpF,GAAG,CAAC,EAAE,UAAU,CAAC;CAClB;AAED,eAAO,MAAM,kBAAkB,OAAQ,CAAC;AAExC,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IACtD,WAAW,CAAC,KAAK,EAAE;QACjB,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,MAAM,CAAC,EAAE,MAAM,CAAC;YAAC,WAAW,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAC/D,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACrC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnB;AAsID,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,YAAY,CAiB7E"}
package/dist/client.js CHANGED
@@ -2,6 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DEFAULT_TIMEOUT_MS = void 0;
4
4
  exports.createEngineClient = createEngineClient;
5
+ const node_http_1 = require("node:http");
6
+ const node_https_1 = require("node:https");
5
7
  exports.DEFAULT_TIMEOUT_MS = 1_000;
6
8
  function isVerdict(value) {
7
9
  if (typeof value !== 'object' || value === null)
@@ -11,7 +13,7 @@ function isVerdict(value) {
11
13
  typeof candidate.score === 'number' &&
12
14
  typeof candidate.verdict === 'string');
13
15
  }
14
- async function postJson(url, body, timeoutMs) {
16
+ async function postJsonWithFetch(url, body, timeoutMs) {
15
17
  const controller = new AbortController();
16
18
  const timer = setTimeout(() => controller.abort(), timeoutMs);
17
19
  try {
@@ -38,12 +40,86 @@ async function postJson(url, body, timeoutMs) {
38
40
  clearTimeout(timer);
39
41
  }
40
42
  }
43
+ /**
44
+ * HTTPS transport for clients that need TLS options.
45
+ *
46
+ * `fetch` cannot express them: it takes no `ca`, and Node's own fetch rejects a
47
+ * dispatcher built from the separately installed `undici` package with
48
+ * `UND_ERR_INVALID_ARG`, because the two copies of undici are different classes.
49
+ * `node:https` is the only transport in the runtime that accepts a trusted CA and
50
+ * an SNI override, so it is used here and nowhere else.
51
+ */
52
+ async function postJsonWithTls(url, body, timeoutMs, tls) {
53
+ const target = new URL(url);
54
+ const secure = target.protocol === 'https:';
55
+ const send = secure ? node_https_1.request : node_http_1.request;
56
+ const payload = JSON.stringify(body);
57
+ return new Promise((resolve) => {
58
+ let settled = false;
59
+ const finish = (result) => {
60
+ if (settled)
61
+ return;
62
+ settled = true;
63
+ clearTimeout(timer);
64
+ resolve(result);
65
+ };
66
+ const request = send({
67
+ protocol: target.protocol,
68
+ hostname: target.hostname,
69
+ port: target.port === '' ? (secure ? 443 : 80) : Number(target.port),
70
+ path: `${target.pathname}${target.search}`,
71
+ method: 'POST',
72
+ headers: {
73
+ 'Content-Type': 'application/json',
74
+ 'Content-Length': Buffer.byteLength(payload),
75
+ },
76
+ ...(secure && tls.ca !== undefined ? { ca: tls.ca } : {}),
77
+ ...(secure && tls.rejectUnauthorized !== undefined
78
+ ? { rejectUnauthorized: tls.rejectUnauthorized }
79
+ : {}),
80
+ ...(secure && tls.servername !== undefined ? { servername: tls.servername } : {}),
81
+ }, (response) => {
82
+ const chunks = [];
83
+ response.on('data', (chunk) => chunks.push(chunk));
84
+ response.on('error', () => finish(null));
85
+ response.on('end', () => {
86
+ const text = Buffer.concat(chunks).toString('utf8');
87
+ let parsed = null;
88
+ try {
89
+ parsed = text === '' ? null : JSON.parse(text);
90
+ }
91
+ catch {
92
+ parsed = null;
93
+ }
94
+ const status = response.statusCode ?? 0;
95
+ finish({ ok: status >= 200 && status < 300, status, body: parsed });
96
+ });
97
+ });
98
+ const timer = setTimeout(() => {
99
+ finish(null);
100
+ request.destroy();
101
+ }, timeoutMs);
102
+ // One failure mode, reported once: DNS, refused connection, TLS rejection.
103
+ request.on('error', () => finish(null));
104
+ request.end(payload);
105
+ });
106
+ }
107
+ /**
108
+ * Picks the transport. `fetch` stays the default because it is the leanest path
109
+ * and covers deployments whose certificate is publicly trusted; only clients that
110
+ * need TLS options pay for the dedicated one.
111
+ */
112
+ async function postJson(url, body, timeoutMs, tls) {
113
+ if (tls === undefined)
114
+ return postJsonWithFetch(url, body, timeoutMs);
115
+ return postJsonWithTls(url, body, timeoutMs, tls);
116
+ }
41
117
  function createEngineClient(options) {
42
118
  const timeoutMs = options.timeoutMs ?? exports.DEFAULT_TIMEOUT_MS;
43
119
  const base = options.engineUrl.replace(/\/+$/, '');
44
120
  return {
45
121
  async verify(input) {
46
- const result = await postJson(`${base}/verify`, input, timeoutMs);
122
+ const result = await postJson(`${base}/verify`, input, timeoutMs, options.tls);
47
123
  if (!result || !result.ok)
48
124
  return null;
49
125
  return isVerdict(result.body) ? result.body : null;
@@ -51,7 +127,7 @@ function createEngineClient(options) {
51
127
  async recordEvent(input) {
52
128
  // Business-event reporting is explicitly fire-and-forget. The /events
53
129
  // endpoint lands in Phase 3; until then this is a tolerated 404.
54
- await postJson(`${base}/events`, input, timeoutMs);
130
+ await postJson(`${base}/events`, input, timeoutMs, options.tls);
55
131
  },
56
132
  };
57
133
  }
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":";;;AAyEA,gDAiBC;AAxEY,QAAA,kBAAkB,GAAG,KAAK,CAAC;AAaxC,SAAS,SAAS,CAAC,KAAc;IAC/B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,SAAS,GAAG,KAAyB,CAAC;IAC5C,OAAO,CACL,OAAO,SAAS,CAAC,MAAM,KAAK,QAAQ;QACpC,OAAO,SAAS,CAAC,KAAK,KAAK,QAAQ;QACnC,OAAO,SAAS,CAAC,OAAO,KAAK,QAAQ,CACtC,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,QAAQ,CACrB,GAAW,EACX,IAAa,EACb,SAAiB;IAEjB,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,SAAS,CAAC,CAAC;IAE9D,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAChC,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;YAC1B,MAAM,EAAE,UAAU,CAAC,MAAM;SAC1B,CAAC,CAAC;QAEH,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnC,IAAI,MAAM,GAAY,IAAI,CAAC;QAC3B,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACjD,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,GAAG,IAAI,CAAC;QAChB,CAAC;QAED,OAAO,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IACpE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;AACH,CAAC;AAED,SAAgB,kBAAkB,CAAC,OAA4B;IAC7D,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,0BAAkB,CAAC;IAC1D,MAAM,IAAI,GAAG,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAEnD,OAAO;QACL,KAAK,CAAC,MAAM,CAAC,KAAoB;YAC/B,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,GAAG,IAAI,SAAS,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;YAClE,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE;gBAAE,OAAO,IAAI,CAAC;YACvC,OAAO,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;QACrD,CAAC;QAED,KAAK,CAAC,WAAW,CAAC,KAAK;YACrB,sEAAsE;YACtE,iEAAiE;YACjE,MAAM,QAAQ,CAAC,GAAG,IAAI,SAAS,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;QACrD,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Client IP resolution (§13.4).
3
+ *
4
+ * The TCP peer address is the only fact a request carries that cannot be forged:
5
+ * completing the handshake requires the source to be routable. Every header is
6
+ * merely a *claim*, so the question is never "what does the header say" but
7
+ * "whose claim is this".
8
+ *
9
+ * This module answers that question with one rule:
10
+ *
11
+ * a claim is believed only when it arrives from a configured trusted hop.
12
+ *
13
+ * That matters more than it looks. A laundered header is indistinguishable from
14
+ * a real address once it is stored, so a wrong answer here silently corrupts
15
+ * every IP-keyed decision downstream — rate limits, the blocklist, honeypot
16
+ * flags, the verdict cache, sessions and alerts. Worse, an attacker can name a
17
+ * third party and have *them* blocked.
18
+ */
19
+ export interface ClientIpInput {
20
+ /** `req.socket.remoteAddress` — the only unforgeable input. */
21
+ peer: string | undefined;
22
+ /** Lower-cased request headers. */
23
+ headers: Record<string, string>;
24
+ /**
25
+ * Addresses (bare IP or CIDR) of the hops that sit in front of the app. Empty
26
+ * or absent means "trust nobody", which is the safe default.
27
+ */
28
+ trustedProxies?: readonly string[] | undefined;
29
+ /** Header a trusted hop asserts the client in. Defaults to `x-real-ip`. */
30
+ clientIpHeader?: string | undefined;
31
+ /**
32
+ * Additionally honour an `X-Forwarded-For` chain, walking from the nearest hop
33
+ * outward and skipping trusted hops. Off by default: it is only correct when
34
+ * every trusted hop appends rather than relays what it received.
35
+ */
36
+ trustForwardedChain?: boolean | undefined;
37
+ /** Called at most once per resolution when a claim is present but unusable. */
38
+ onWarn?: ((message: string) => void) | undefined;
39
+ }
40
+ /**
41
+ * Canonical form of an address, or null when it is not one.
42
+ *
43
+ * Nodes are keyed by these strings (Redis sets, rate-limit buckets, session
44
+ * lookup), so `::ffff:203.0.113.45` and `203.0.113.45` must never be allowed to
45
+ * become two identities for one client. IPv6 is lower-cased for the same reason.
46
+ */
47
+ export declare function normalizeIp(raw: string | undefined | null): string | null;
48
+ /** True when `peer` is one of the configured hops. Cross-family rules never match. */
49
+ export declare function isTrustedPeer(peer: string | undefined, trustedProxies: readonly string[] | undefined): boolean;
50
+ /**
51
+ * Resolves the client address for a request.
52
+ *
53
+ * Order is deliberate and load-bearing — see the module comment.
54
+ */
55
+ export declare function resolveClientIp(input: ClientIpInput): string;
56
+ //# sourceMappingURL=clientIp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clientIp.d.ts","sourceRoot":"","sources":["../src/clientIp.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,WAAW,aAAa;IAC5B,+DAA+D;IAC/D,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC;;;OAGG;IACH,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IAC/C,2EAA2E;IAC3E,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC1C,+EAA+E;IAC/E,MAAM,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;CAClD;AASD;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAkBzE;AAED,sFAAsF;AACtF,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,cAAc,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,GAC5C,OAAO,CAOT;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CA0B5D"}
@@ -0,0 +1,164 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeIp = normalizeIp;
4
+ exports.isTrustedPeer = isTrustedPeer;
5
+ exports.resolveClientIp = resolveClientIp;
6
+ const node_net_1 = require("node:net");
7
+ /** Stands in for "we could not establish an address" — never a real client. */
8
+ const UNKNOWN_IP = '0.0.0.0';
9
+ const DEFAULT_CLIENT_IP_HEADER = 'x-real-ip';
10
+ const FORWARDED_FOR = 'x-forwarded-for';
11
+ const IPV4_MAPPED = /^::ffff:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/i;
12
+ /**
13
+ * Canonical form of an address, or null when it is not one.
14
+ *
15
+ * Nodes are keyed by these strings (Redis sets, rate-limit buckets, session
16
+ * lookup), so `::ffff:203.0.113.45` and `203.0.113.45` must never be allowed to
17
+ * become two identities for one client. IPv6 is lower-cased for the same reason.
18
+ */
19
+ function normalizeIp(raw) {
20
+ if (typeof raw !== 'string')
21
+ return null;
22
+ let value = raw.trim();
23
+ if (value === '')
24
+ return null;
25
+ // IPv4-mapped IPv6 arrives whenever a v4 client reaches a dual-stack listener.
26
+ const mapped = IPV4_MAPPED.exec(value);
27
+ if (mapped?.[1] !== undefined)
28
+ value = mapped[1];
29
+ value = value.toLowerCase();
30
+ // A scope/zone id (`fe80::1%eth0`) is link-local routing information, not part
31
+ // of the address identity.
32
+ const zone = value.indexOf('%');
33
+ if (zone !== -1)
34
+ value = value.slice(0, zone);
35
+ return (0, node_net_1.isIP)(value) === 0 ? null : value;
36
+ }
37
+ /** True when `peer` is one of the configured hops. Cross-family rules never match. */
38
+ function isTrustedPeer(peer, trustedProxies) {
39
+ if (trustedProxies === undefined || trustedProxies.length === 0)
40
+ return false;
41
+ const normalized = normalizeIp(peer);
42
+ if (normalized === null)
43
+ return false;
44
+ return trustedProxies.some((rule) => matchesRule(normalized, rule));
45
+ }
46
+ /**
47
+ * Resolves the client address for a request.
48
+ *
49
+ * Order is deliberate and load-bearing — see the module comment.
50
+ */
51
+ function resolveClientIp(input) {
52
+ const peer = normalizeIp(input.peer) ?? UNKNOWN_IP;
53
+ // Untrusted peer (or no configuration): headers are attacker-controlled text.
54
+ if (!isTrustedPeer(peer, input.trustedProxies))
55
+ return peer;
56
+ const headerName = (input.clientIpHeader ?? DEFAULT_CLIENT_IP_HEADER).toLowerCase();
57
+ const raw = input.headers[headerName];
58
+ const claimed = normalizeIp(raw);
59
+ if (claimed !== null)
60
+ return claimed;
61
+ // A claim was present but unusable. Silent fallback here would hide a
62
+ // misconfigured hop, which is exactly the class of bug this module exists to
63
+ // catch, so it is reported once.
64
+ if (raw !== undefined && raw.trim() !== '') {
65
+ input.onWarn?.(`[botshield] ignoring unusable ${headerName} value from trusted proxy ${peer}: ${JSON.stringify(raw)}`);
66
+ }
67
+ if (input.trustForwardedChain === true) {
68
+ const walked = walkForwardedChain(input.headers[FORWARDED_FOR], input.trustedProxies);
69
+ if (walked !== null)
70
+ return walked;
71
+ }
72
+ return peer;
73
+ }
74
+ /**
75
+ * Walks `X-Forwarded-For` from the right (nearest hop) to the left, skipping
76
+ * trusted hops, and returns the first untrusted address.
77
+ *
78
+ * Addresses further left are whatever the client sent, so they are only reached
79
+ * if the intervening hops are themselves trusted — which is the whole point of
80
+ * scoping by peer.
81
+ */
82
+ function walkForwardedChain(value, trustedProxies) {
83
+ if (typeof value !== 'string' || value.trim() === '')
84
+ return null;
85
+ const hops = value
86
+ .split(',')
87
+ .map((part) => normalizeIp(part))
88
+ .filter((part) => part !== null);
89
+ for (let index = hops.length - 1; index >= 0; index -= 1) {
90
+ const hop = hops[index];
91
+ if (hop === undefined)
92
+ continue;
93
+ if (isTrustedPeer(hop, trustedProxies))
94
+ continue;
95
+ return hop;
96
+ }
97
+ return null;
98
+ }
99
+ /** Matches a bare address or a CIDR, never across address families. */
100
+ function matchesRule(ip, rule) {
101
+ const slash = rule.indexOf('/');
102
+ const base = normalizeIp(slash === -1 ? rule : rule.slice(0, slash));
103
+ if (base === null)
104
+ return false;
105
+ const family = (0, node_net_1.isIP)(ip);
106
+ if (family === 0 || family !== (0, node_net_1.isIP)(base))
107
+ return false;
108
+ const bits = family === 4 ? 32 : 128;
109
+ let prefix = bits;
110
+ if (slash !== -1) {
111
+ const raw = rule.slice(slash + 1);
112
+ if (!/^\d+$/.test(raw))
113
+ return false;
114
+ prefix = Number(raw);
115
+ if (prefix > bits)
116
+ return false;
117
+ }
118
+ const candidate = toBigInt(ip, family);
119
+ const network = toBigInt(base, family);
120
+ if (candidate === null || network === null)
121
+ return false;
122
+ if (prefix === 0)
123
+ return true;
124
+ const shift = BigInt(bits - prefix);
125
+ return candidate >> shift === network >> shift;
126
+ }
127
+ function toBigInt(ip, family) {
128
+ return family === 4 ? ipv4ToBigInt(ip) : ipv6ToBigInt(ip);
129
+ }
130
+ function ipv4ToBigInt(ip) {
131
+ const parts = ip.split('.');
132
+ if (parts.length !== 4)
133
+ return null;
134
+ let value = 0n;
135
+ for (const part of parts) {
136
+ const octet = Number(part);
137
+ if (!Number.isInteger(octet) || octet < 0 || octet > 255)
138
+ return null;
139
+ value = (value << 8n) | BigInt(octet);
140
+ }
141
+ return value;
142
+ }
143
+ /** Expands `::` shorthand to eight groups; no embedded-IPv4 form is accepted here. */
144
+ function ipv6ToBigInt(ip) {
145
+ const doubleColon = ip.indexOf('::');
146
+ const head = doubleColon === -1 ? ip : ip.slice(0, doubleColon);
147
+ const tail = doubleColon === -1 ? '' : ip.slice(doubleColon + 2);
148
+ const headGroups = head === '' ? [] : head.split(':');
149
+ const tailGroups = tail === '' ? [] : tail.split(':');
150
+ const missing = 8 - (headGroups.length + tailGroups.length);
151
+ if (missing < 0 || (doubleColon === -1 && missing !== 0))
152
+ return null;
153
+ const groups = [...headGroups, ...new Array(Math.max(missing, 0)).fill('0'), ...tailGroups];
154
+ if (groups.length !== 8)
155
+ return null;
156
+ let value = 0n;
157
+ for (const group of groups) {
158
+ if (!/^[0-9a-f]{1,4}$/.test(group))
159
+ return null;
160
+ value = (value << 16n) | BigInt(Number.parseInt(group, 16));
161
+ }
162
+ return value;
163
+ }
164
+ //# sourceMappingURL=clientIp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clientIp.js","sourceRoot":"","sources":["../src/clientIp.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,KAAK,YAAY,EAAE,MAAM,UAAU,CAAC;AACjE,OAAO,KAAK,EAEV,eAAe,EAEf,mBAAmB,EAKnB,OAAO,EACR,MAAM,SAAS,CAAC;AAEjB;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,0EAA0E;AAC1E,qBAAa,YAAY;IAGX,OAAO,CAAC,QAAQ,CAAC,UAAU;IAFvC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwC;gBAEnC,UAAU,SAAQ;IAE/C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,OAAO,GAAG,IAAI;IAYlD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,SAAa,GAAG,IAAI;IAU1D,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B;AAsCD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAKnF;AAQD,wBAAgB,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,mBAAmB,CAqItE;AAED,OAAO,EAAE,kBAAkB,EAAE,YAAY,IAAI,gBAAgB,EAAE,CAAC;AAChE,YAAY,EAAE,YAAY,EAAE,CAAC"}
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,KAAK,YAAY,EAAE,MAAM,UAAU,CAAC;AAEjE,OAAO,KAAK,EAEV,eAAe,EAEf,mBAAmB,EAKnB,OAAO,EACR,MAAM,SAAS,CAAC;AAEjB;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,0EAA0E;AAC1E,qBAAa,YAAY;IAGX,OAAO,CAAC,QAAQ,CAAC,UAAU;IAFvC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwC;gBAEnC,UAAU,SAAQ;IAE/C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,OAAO,GAAG,IAAI;IAYlD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,SAAa,GAAG,IAAI;IAU1D,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B;AAsCD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAKnF;AAQD,wBAAgB,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,mBAAmB,CAsKtE;AAED,OAAO,EAAE,kBAAkB,EAAE,YAAY,IAAI,gBAAgB,EAAE,CAAC;AAChE,YAAY,EAAE,YAAY,EAAE,CAAC"}
@@ -5,6 +5,7 @@ exports.matchesProtected = matchesProtected;
5
5
  exports.botShield = botShield;
6
6
  const client_1 = require("./client");
7
7
  Object.defineProperty(exports, "createEngineClient", { enumerable: true, get: function () { return client_1.createEngineClient; } });
8
+ const clientIp_1 = require("./clientIp");
8
9
  /** Small bounded cache; Map insertion order doubles as eviction order. */
9
10
  class VerdictCache {
10
11
  maxEntries;
@@ -98,7 +99,20 @@ function botShield(config) {
98
99
  const client = (0, client_1.createEngineClient)({
99
100
  engineUrl: config.engineUrl,
100
101
  timeoutMs: config.timeoutMs,
102
+ tls: config.tls,
101
103
  });
104
+ /**
105
+ * Client-IP problems are configuration faults, not per-request events: a
106
+ * misconfigured hop would otherwise log on every request and turn a detection
107
+ * gap into a log flood. One line per distinct cause is enough to find it.
108
+ */
109
+ const reported = new Set();
110
+ function reportOnce(message) {
111
+ if (reported.has(message))
112
+ return;
113
+ reported.add(message);
114
+ config.logger?.warn({ reason: 'client_ip' }, message);
115
+ }
102
116
  function buildContext(verdict, score, requestId, cached, scope) {
103
117
  return {
104
118
  verdict,
@@ -126,8 +140,24 @@ function botShield(config) {
126
140
  const handler = async (req, res, next) => {
127
141
  const headers = toHeaderRecord(req.headers);
128
142
  const cookies = parseCookies(headerValue(headers, 'cookie'));
143
+ // Express's own `trust proxy: true` makes `req.ip` whatever the client sent.
144
+ // We resolve the address ourselves, so the host's setting cannot loosen our
145
+ // rule — but a host that enabled it is worth one warning, because its own
146
+ // rate limiting and logging are then using a forgeable value.
147
+ if (req.app?.get?.('trust proxy') === true) {
148
+ reportOnce('[botshield] host app sets `trust proxy: true`, which trusts X-Forwarded-For from any peer. ' +
149
+ 'BotShield resolves the client IP from its own trustedProxies config, but Express `req.ip` ' +
150
+ 'elsewhere in the app is client-controlled.');
151
+ }
129
152
  const scope = {
130
- ip: headerValue(headers, 'x-real-ip') ?? req.socket?.remoteAddress ?? '0.0.0.0',
153
+ ip: (0, clientIp_1.resolveClientIp)({
154
+ peer: req.socket?.remoteAddress,
155
+ headers,
156
+ trustedProxies: config.trustedProxies,
157
+ clientIpHeader: config.clientIpHeader,
158
+ trustForwardedChain: config.trustForwardedChain,
159
+ onWarn: reportOnce,
160
+ }),
131
161
  fingerprint: cookies[fingerprintCookie],
132
162
  sessionId: cookies.bs_sid,
133
163
  userId: req.user?.id,
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -25,6 +25,27 @@ export interface BotShieldContext {
25
25
  */
26
26
  record(event: string, attributes: Record<string, unknown>): void;
27
27
  }
28
+ /**
29
+ * TLS settings for the engine connection (§3.3).
30
+ *
31
+ * Node's `fetch` accepts no TLS options, so supplying any of these routes the
32
+ * engine call through `node:https` instead. Self-hosted BotShield ships a
33
+ * self-signed gateway certificate, so a deployment addressing it over HTTPS needs
34
+ * at least `ca`. When the engine is reached by an address the certificate does not
35
+ * name — a LAN IP, say — `servername` is required as well: trusting a CA does not,
36
+ * and must not, stand in for the hostname check.
37
+ */
38
+ export interface TlsOptions {
39
+ /** PEM certificate(s) to trust, for a private or self-signed CA. */
40
+ ca?: string | string[];
41
+ /**
42
+ * Verify the server certificate. Defaults to true. `false` disables
43
+ * verification for the engine connection and is a development escape hatch.
44
+ */
45
+ rejectUnauthorized?: boolean;
46
+ /** Name to verify against the certificate and to send as SNI. */
47
+ servername?: string;
48
+ }
28
49
  export interface BotShieldConfig {
29
50
  engineUrl: string;
30
51
  appName: string;
@@ -35,8 +56,33 @@ export interface BotShieldConfig {
35
56
  fingerprintCookie?: string;
36
57
  /** Bounded time budget for the engine call, ms. */
37
58
  timeoutMs?: number;
59
+ /** TLS settings, for an engine served with a private or self-signed certificate. */
60
+ tls?: TlsOptions;
38
61
  /** Entries retained in the in-process verdict cache. */
39
62
  cacheMaxEntries?: number;
63
+ /**
64
+ * Addresses (bare IP or CIDR) of the reverse proxies that sit in front of the
65
+ * app — the BotShield gateway, an ingress, an ALB.
66
+ *
67
+ * Headers asserting a client IP are believed **only** when the request's TCP
68
+ * peer is listed here; anything else falls back to the peer address, which is
69
+ * the one thing a client cannot forge. Empty by default, and empty means
70
+ * "trust nobody", because a believed-but-forged address silently corrupts
71
+ * every IP-keyed decision downstream, and can get a third party blocked.
72
+ */
73
+ trustedProxies?: string[];
74
+ /** Header a trusted proxy asserts the client in. Defaults to `x-real-ip`. */
75
+ clientIpHeader?: string;
76
+ /**
77
+ * Also honour an `X-Forwarded-For` chain, walking from the nearest hop outward
78
+ * and skipping trusted hops, stopping at the first untrusted address.
79
+ *
80
+ * Off by default: it is only correct when every trusted hop *appends* its own
81
+ * observation. A hop that forwards the client's chain verbatim turns this into
82
+ * a spoofing hole, because a relayed claim is indistinguishable from an
83
+ * asserted one.
84
+ */
85
+ trustForwardedChain?: boolean;
40
86
  onVerdict?: (context: BotShieldContext, req: Request) => void;
41
87
  logger?: {
42
88
  warn(obj: unknown, msg?: string): void;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAE/E,uCAAuC;AACvC,MAAM,MAAM,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,WAAW,GAAG,OAAO,CAAC;AAEhE,MAAM,WAAW,OAAO;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,OAAO,CAAC;IAChB,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,oDAAoD;AACpD,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,MAAM,EAAE,OAAO,CAAC;IAChB;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAClE;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,iEAAiE;IACjE,IAAI,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC5B,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,2EAA2E;IAC3E,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,mDAAmD;IACnD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wDAAwD;IACxD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;IAC9D,MAAM,CAAC,EAAE;QAAE,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;CAC9F;AAED,OAAO,QAAQ,2BAA2B,CAAC;IACzC,UAAU,OAAO;QACf,SAAS,CAAC,EAAE,gBAAgB,CAAC;KAC9B;CACF;AAED,MAAM,MAAM,mBAAmB,GAAG,cAAc,GAAG;IACjD,yBAAyB;IACzB,QAAQ,CAAC,MAAM,EAAE;QAAE,MAAM,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAA;KAAE,CAAC;CAC5E,CAAC;AAEF,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,yEAAyE;IACzE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,MAAM,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACrD;AAED,YAAY,EAAE,YAAY,EAAE,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "botshield-express",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Drop-in Express middleware for BotShield — reads gateway verdicts, scores via the engine, fails open",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",