@riavzon/bot-detector-create 1.0.10 → 1.0.12

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
@@ -12,16 +12,17 @@ Run this in the root of your Express project:
12
12
  npx @riavzon/bot-detector-create
13
13
  ```
14
14
 
15
- The command does the following in order:
15
+ The command does the following:
16
16
 
17
17
  1. Installs `@riavzon/bot-detector`, `express`, `cookie-parser`, and
18
18
  `better-sqlite3` into your project.
19
- 2. Triggers the data source installer downloads and compiles all threat
20
- intelligence feeds (FireHOL, Tor, ASN, GeoIP, JA4, user-agent lists) into
21
- fast local MMDB/LMDB files.
19
+ 2. Triggers the data source installer and downloads and compiles all threat
20
+ intelligence feeds.
22
21
  3. Writes a `botDetectorConfig.ts` file at your project root with all 17
23
22
  checkers pre-configured at their default values.
24
- 4. Runs `load-schema` to create the `visitors` and `banned` tables in a local
23
+ 4. Writes a `mainBotDetector.ts` file at your project root, a ready-to-run
24
+ Express entry point that imports the config and mounts the middleware.
25
+ 5. Runs `load-schema` to create the `visitors` and `banned` tables in a local
25
26
  `bot_detector.sqlite` file.
26
27
 
27
28
  ## What you get
@@ -43,8 +44,7 @@ await defineConfiguration({
43
44
  });
44
45
  ```
45
46
 
46
- The defaults use SQLite and in-process memory cache no external services
47
- required. When you're ready for production, swap the adapters:
47
+ The defaults use SQLite and in-process memory cache, When you're ready for production, swap the adapters:
48
48
 
49
49
  ```ts
50
50
  // MySQL
@@ -57,28 +57,39 @@ storage: { driver: 'redis', url: process.env.REDIS_URL }
57
57
  storage: { driver: 'upstash', url: process.env.UPSTASH_URL, token: process.env.UPSTASH_TOKEN }
58
58
  ```
59
59
 
60
- ## Mount the middleware
60
+ ## What you get
61
61
 
62
- Import `botDetectorConfig.ts` at the top of your app entry point, before any
63
- routes, then mount the middleware:
62
+ `mainBotDetector.ts` is a ready-to-run Express entry point. It imports the
63
+ config, mounts the middleware, and starts the server:
64
64
 
65
65
  ```ts
66
66
  import './botDetectorConfig.js';
67
- import { detectBots } from '@riavzon/bot-detector';
68
- import cookieParser from 'cookie-parser';
69
67
  import express from 'express';
68
+ import cookieParser from 'cookie-parser';
69
+ import { detectBots } from '@riavzon/bot-detector';
70
70
 
71
71
  const app = express();
72
72
  app.use(cookieParser());
73
73
  app.use(detectBots());
74
74
 
75
75
  app.get('/', (req, res) => {
76
- res.send('Hello, human.');
76
+ res.json({ banned: req.botDetection?.banned });
77
77
  });
78
78
 
79
79
  app.listen(3000);
80
80
  ```
81
81
 
82
+ ## Mount the middleware
83
+
84
+ `botDetectorConfig.ts` must be imported before any routes. If you prefer to
85
+ wire it into your own entry point instead of using `mainBotDetector.ts`:
86
+
87
+ ```ts
88
+ import './botDetectorConfig.js';
89
+ import { detectBots } from '@riavzon/bot-detector';
90
+ // ... rest of your app
91
+ ```
92
+
82
93
  ## Keep data sources fresh
83
94
 
84
95
  Threat intelligence feeds update continuously. Run a refresh at least once
package/dist/create.js CHANGED
@@ -9,6 +9,21 @@ const defaultStore = { main: {
9
9
  driver: "sqlite",
10
10
  name: "./bot_detector.sqlite"
11
11
  } };
12
+ const mainContent = `import './botDetectorConfig.js';
13
+ import express from 'express';
14
+ import cookieParser from 'cookie-parser';
15
+ import { detectBots } from '@riavzon/bot-detector';
16
+
17
+ const app = express();
18
+ app.use(cookieParser());
19
+ app.use(detectBots());
20
+
21
+ app.get('/', (req, res) => {
22
+ res.json({ banned: req.botDetection?.banned });
23
+ });
24
+
25
+ app.listen(3000);
26
+ `;
12
27
  const content = `import { defineConfiguration } from '@riavzon/bot-detector';
13
28
 
14
29
  /**
@@ -43,7 +58,7 @@ await defineConfiguration({
43
58
  setNewComputedScore: false,
44
59
 
45
60
  // ─── Whitelist (IPv4, IPv6, or CIDR) ───────────────────────────────────────
46
- whiteList: [],
61
+ whiteList: ['127.0.0.1', '::1'],
47
62
 
48
63
  // ─── Re-check interval for returning visitors ──────────────────────────────
49
64
  checksTimeRateControl: {
@@ -300,6 +315,7 @@ const start = defineCommand({
300
315
  await run("npx", ["@riavzon/bot-detector", "init"]);
301
316
  consola.start("Writing botDetectorConfig.ts...");
302
317
  await fs.writeFile(output, content, "utf-8");
318
+ await fs.writeFile(path.resolve(process.cwd(), "mainBotDetector.ts"), mainContent, "utf-8");
303
319
  consola.success("botDetectorConfig.ts created");
304
320
  consola.start("Creating database tables...");
305
321
  const { defineConfiguration, createTables, getDb } = await import(path.resolve(process.cwd(), "node_modules/@riavzon/bot-detector/dist/main.mjs"));
@@ -309,6 +325,7 @@ const start = defineCommand({
309
325
  consola.log("");
310
326
  consola.log("Keep data sources fresh (run daily or via cron):");
311
327
  consola.log(" npx @riavzon/bot-detector refresh");
328
+ process.exit(0);
312
329
  }
313
330
  });
314
331
  await runMain(start);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riavzon/bot-detector-create",
3
- "version": "1.0.10",
3
+ "version": "1.0.12",
4
4
  "type": "module",
5
5
  "author": "Sergio contact@riavzon.com",
6
6
  "license": "Apache-2.0",
@@ -13,6 +13,9 @@
13
13
  "bugs": {
14
14
  "url": "https://github.com/Sergo706/botDetector/issues"
15
15
  },
16
+ "files": [
17
+ "dist"
18
+ ],
16
19
  "keywords": [
17
20
  "bot-detection",
18
21
  "express-middleware",
package/eslint.config.mjs DELETED
@@ -1,6 +0,0 @@
1
- import { defineStrictTSConfig } from '@riavzon/utils/eslint/strict';
2
-
3
- export default defineStrictTSConfig({
4
- rootDir: import.meta.dirname,
5
- extraIgnores: ['tsdown.config.ts'],
6
- });
package/src/create.ts DELETED
@@ -1,57 +0,0 @@
1
- #!/usr/bin/env node
2
- /* eslint-disable @typescript-eslint/no-unsafe-assignment */
3
-
4
- import consola from 'consola';
5
- import { defineCommand, runMain } from 'citty';
6
- import fs from 'node:fs/promises';
7
- import path from 'node:path';
8
- import { spawn } from 'node:child_process';
9
- import { content, defaultStore } from './default.js';
10
-
11
- function run(cmd: string, args: string[]): Promise<void> {
12
- return new Promise((resolve, reject) => {
13
- const child = spawn(cmd, args, { stdio: 'inherit', shell: true });
14
- child.on('close', code => {
15
- if (code === 0) { resolve(); } else { reject(new Error(`${cmd} exited with code ${String(code)}`)); }
16
- });
17
- });
18
- }
19
-
20
- export const start = defineCommand({
21
- meta: {
22
- name: 'Create Bot Detector',
23
- description: 'Quick starter for @riavzon/bot-detector'
24
- },
25
-
26
- async run() {
27
- const output = path.resolve(process.cwd(), 'botDetectorConfig.ts');
28
-
29
- consola.start('Installing dependencies...');
30
- await run('npm', ['install', 'express', 'cookie-parser', 'better-sqlite3']);
31
-
32
- consola.start('Installing @riavzon/bot-detector...');
33
- await run('npm', ['install', '@riavzon/bot-detector']);
34
-
35
- consola.start('Fetching data sources...');
36
- await run('npx', ['@riavzon/bot-detector', 'init']);
37
-
38
- consola.start('Writing botDetectorConfig.ts...');
39
- await fs.writeFile(output, content, 'utf-8');
40
- consola.success('botDetectorConfig.ts created');
41
-
42
- consola.start('Creating database tables...');
43
- const pkgMain = path.resolve(process.cwd(), 'node_modules/@riavzon/bot-detector/dist/main.mjs');
44
- const { defineConfiguration, createTables, getDb } = await import(pkgMain);
45
-
46
- // eslint-disable-next-line @typescript-eslint/no-unsafe-call
47
- await defineConfiguration({ store: defaultStore });
48
- // eslint-disable-next-line @typescript-eslint/no-unsafe-call
49
- await createTables(getDb());
50
-
51
- consola.success('Setup complete. Import botDetectorConfig.ts at the top of your app entry point and mount the middleware.');
52
- consola.log('');
53
- consola.log('Keep data sources fresh (run daily or via cron):');
54
- consola.log(' npx @riavzon/bot-detector refresh');
55
- }
56
- });
57
- await runMain(start);
package/src/default.ts DELETED
@@ -1,260 +0,0 @@
1
- export const defaultStore = { main: { driver: 'sqlite' as const, name: './bot_detector.sqlite' } };
2
-
3
- export const content = `import { defineConfiguration } from '@riavzon/bot-detector';
4
-
5
- /**
6
- * Bot Detector configuration — all values shown are the defaults.
7
- * Tune penalties, enable/disable checkers, and swap adapters as needed.
8
- *
9
- * Next steps:
10
- * Import this file before mounting the middleware in your app entry point
11
- *
12
- * Keep data sources fresh:
13
- * npx @riavzon/bot-detector refresh
14
- */
15
- await defineConfiguration({
16
-
17
- // ─── Database (SQLite default — swap for mysql-pool / postgresql in production)
18
- store: {
19
- main: {
20
- driver: 'sqlite',
21
- name: './bot_detector.sqlite',
22
- },
23
- },
24
-
25
- // ─── Cache (default: in-process memory — uncomment to use a different one)
26
- // storage: { driver: 'lru', max: 500, ttl: 1000 * 60 * 60 * 2 },
27
- // storage: { driver: 'redis', url: process.env.REDIS_URL ?? 'redis://localhost:6379' },
28
- // storage: { driver: 'upstash', url: process.env.UPSTASH_URL, token: process.env.UPSTASH_TOKEN },
29
-
30
- // ─── Core scoring ──────────────────────────────────────────────────────────
31
- banScore: 100,
32
- maxScore: 100,
33
- restoredReputationPoints: 10,
34
- setNewComputedScore: false,
35
-
36
- // ─── Whitelist (IPv4, IPv6, or CIDR) ───────────────────────────────────────
37
- whiteList: [],
38
-
39
- // ─── Re-check interval for returning visitors ──────────────────────────────
40
- checksTimeRateControl: {
41
- checkEveryRequest: true,
42
- checkEvery: 1000 * 60 * 5,
43
- },
44
-
45
- // ─── Async batch write queue ───────────────────────────────────────────────
46
- batchQueue: {
47
- flushIntervalMs: 5000,
48
- maxBufferSize: 100,
49
- maxRetries: 3,
50
- },
51
-
52
- // ─── Logging ───────────────────────────────────────────────────────────────
53
- logLevel: 'info',
54
-
55
- // ─── Firewall ban (requires sudo ufw) ──────────────────────────────────────
56
- punishmentType: {
57
- enableFireWallBan: false,
58
- },
59
-
60
- // ─── Custom MMDB generation from visitor history ───────────────────────────
61
- generator: {
62
- scoreThreshold: 70,
63
- generateTypes: false,
64
- deleteAfterBuild: false,
65
- mmdbctlPath: 'mmdbctl',
66
- },
67
-
68
- // ─── HTTP header anomaly penalties ─────────────────────────────────────────
69
- headerOptions: {
70
- weightPerMustHeader: 20,
71
- missingBrowserEngine: 30,
72
- postManOrInsomiaHeaders: 50,
73
- AJAXHeaderExists: 30,
74
- connectionHeaderIsClose: 20,
75
- originHeaderIsNULL: 10,
76
- originHeaderMismatch: 30,
77
- omittedAcceptHeader: 30,
78
- clientHintsMissingForBlink: 30,
79
- teHeaderUnexpectedForBlink: 10,
80
- clientHintsUnexpectedForGecko: 30,
81
- teHeaderMissingForGecko: 20,
82
- aggressiveCacheControlOnGet: 15,
83
- crossSiteRequestMissingReferer: 10,
84
- inconsistentSecFetchMode: 20,
85
- hostMismatchWeight: 40,
86
- },
87
-
88
- // ─── Path traversal detection ──────────────────────────────────────────────
89
- pathTraveler: {
90
- maxIterations: 3,
91
- maxPathLength: 1500,
92
- pathLengthToLong: 100,
93
- longDecoding: 100,
94
- traversalDetected: 60,
95
- },
96
-
97
- // ─── Checkers (set enable: false to disable any individual checker) ─────────
98
- checkers: {
99
-
100
- localeMapsCheck: {
101
- enable: true,
102
- penalties: {
103
- ipAndHeaderMismatch: 20,
104
- missingHeader: 20,
105
- missingGeoData: 20,
106
- malformedHeader: 30,
107
- },
108
- },
109
-
110
- knownBadUserAgents: {
111
- enable: true,
112
- penalties: {
113
- criticalSeverity: 100,
114
- highSeverity: 80,
115
- mediumSeverity: 30,
116
- lowSeverity: 10,
117
- },
118
- },
119
-
120
- enableIpChecks: {
121
- enable: true,
122
- penalties: 10,
123
- },
124
-
125
- enableGoodBotsChecks: {
126
- enable: true,
127
- banUnlistedBots: true,
128
- penalties: 100,
129
- },
130
-
131
- enableBehaviorRateCheck: {
132
- enable: true,
133
- behavioral_window: 60_000,
134
- behavioral_threshold: 30,
135
- penalties: 60,
136
- },
137
-
138
- enableProxyIspCookiesChecks: {
139
- enable: true,
140
- penalties: {
141
- cookieMissing: 80,
142
- proxyDetected: 40,
143
- multiSourceBonus2to3: 10,
144
- multiSourceBonus4plus: 20,
145
- hostingDetected: 50,
146
- ispUnknown: 10,
147
- orgUnknown: 10,
148
- },
149
- },
150
-
151
- enableUaAndHeaderChecks: {
152
- enable: true,
153
- penalties: {
154
- headlessBrowser: 100,
155
- shortUserAgent: 80,
156
- tlsCheckFailed: 60,
157
- badUaChecker: true,
158
- },
159
- },
160
-
161
- enableBrowserAndDeviceChecks: {
162
- enable: true,
163
- penalties: {
164
- cliOrLibrary: 100,
165
- internetExplorer: 100,
166
- linuxOs: 10,
167
- impossibleBrowserCombinations: 30,
168
- browserTypeUnknown: 10,
169
- browserNameUnknown: 10,
170
- desktopWithoutOS: 10,
171
- deviceVendorUnknown: 10,
172
- browserVersionUnknown: 10,
173
- deviceModelUnknown: 5,
174
- },
175
- },
176
-
177
- enableGeoChecks: {
178
- enable: true,
179
- bannedCountries: [],
180
- penalties: {
181
- countryUnknown: 10,
182
- regionUnknown: 10,
183
- latLonUnknown: 10,
184
- districtUnknown: 10,
185
- cityUnknown: 10,
186
- timezoneUnknown: 10,
187
- subregionUnknown: 10,
188
- phoneUnknown: 10,
189
- continentUnknown: 10,
190
- },
191
- },
192
-
193
- enableKnownThreatsDetections: {
194
- enable: true,
195
- penalties: {
196
- anonymiseNetwork: 20,
197
- threatLevels: {
198
- criticalLevel1: 40,
199
- currentAttacksLevel2: 30,
200
- threatLevel3: 20,
201
- threatLevel4: 10,
202
- },
203
- },
204
- },
205
-
206
- enableAsnClassification: {
207
- enable: true,
208
- penalties: {
209
- contentClassification: 20,
210
- unknownClassification: 10,
211
- lowVisibilityPenalty: 10,
212
- lowVisibilityThreshold: 15,
213
- comboHostingLowVisibility: 20,
214
- },
215
- },
216
-
217
- enableTorAnalysis: {
218
- enable: true,
219
- penalties: {
220
- runningNode: 15,
221
- exitNode: 20,
222
- webExitCapable: 15,
223
- guardNode: 10,
224
- badExit: 40,
225
- obsoleteVersion: 10,
226
- },
227
- },
228
-
229
- enableTimezoneConsistency: {
230
- enable: true,
231
- penalties: 20,
232
- },
233
-
234
- honeypot: {
235
- enable: true,
236
- paths: ['/.env', '/wp-admin', '/wp-login.php', '/.git/config', '/phpinfo.php'],
237
- },
238
-
239
- enableSessionCoherence: {
240
- enable: true,
241
- penalties: {
242
- pathMismatch: 10,
243
- missingReferer: 20,
244
- domainMismatch: 30,
245
- },
246
- },
247
-
248
- enableVelocityFingerprint: {
249
- enable: true,
250
- cvThreshold: 0.1,
251
- penalties: 40,
252
- },
253
-
254
- enableKnownBadIpsCheck: {
255
- enable: true,
256
- highRiskPenalty: 30,
257
- },
258
- },
259
- });
260
- `;
package/tsconfig.json DELETED
@@ -1,10 +0,0 @@
1
- {
2
- "extends": "../tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "./dist",
5
- "baseUrl": ".",
6
- "paths": {}
7
- },
8
- "include": ["src/**/*"],
9
- "exclude": ["node_modules"]
10
- }
package/tsdown.config.ts DELETED
@@ -1,10 +0,0 @@
1
- import { defineConfig } from 'tsdown';
2
-
3
- export default defineConfig({
4
- entry: ['./src/create.ts'],
5
- format: ['esm'],
6
- target: 'node18',
7
- dts: false,
8
- clean: true,
9
- sourcemap: false,
10
- });