@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 +24 -13
- package/dist/create.js +18 -1
- package/package.json +4 -1
- package/eslint.config.mjs +0 -6
- package/src/create.ts +0 -57
- package/src/default.ts +0 -260
- package/tsconfig.json +0 -10
- package/tsdown.config.ts +0 -10
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
|
|
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
|
|
20
|
-
intelligence feeds
|
|
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.
|
|
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
|
|
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
|
-
##
|
|
60
|
+
## What you get
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
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.
|
|
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.
|
|
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
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