s3db.js 13.6.0 → 14.0.2

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.
Files changed (193) hide show
  1. package/README.md +139 -43
  2. package/dist/s3db.cjs +72425 -38970
  3. package/dist/s3db.cjs.map +1 -1
  4. package/dist/s3db.es.js +72177 -38764
  5. package/dist/s3db.es.js.map +1 -1
  6. package/mcp/lib/base-handler.js +157 -0
  7. package/mcp/lib/handlers/connection-handler.js +280 -0
  8. package/mcp/lib/handlers/query-handler.js +533 -0
  9. package/mcp/lib/handlers/resource-handler.js +428 -0
  10. package/mcp/lib/tool-registry.js +336 -0
  11. package/mcp/lib/tools/connection-tools.js +161 -0
  12. package/mcp/lib/tools/query-tools.js +267 -0
  13. package/mcp/lib/tools/resource-tools.js +404 -0
  14. package/package.json +94 -49
  15. package/src/clients/memory-client.class.js +346 -191
  16. package/src/clients/memory-storage.class.js +300 -84
  17. package/src/clients/s3-client.class.js +7 -6
  18. package/src/concerns/geo-encoding.js +19 -2
  19. package/src/concerns/ip.js +59 -9
  20. package/src/concerns/money.js +8 -1
  21. package/src/concerns/password-hashing.js +49 -8
  22. package/src/concerns/plugin-storage.js +186 -18
  23. package/src/concerns/storage-drivers/filesystem-driver.js +284 -0
  24. package/src/database.class.js +139 -29
  25. package/src/errors.js +332 -42
  26. package/src/plugins/api/auth/oidc-auth.js +66 -17
  27. package/src/plugins/api/auth/strategies/base-strategy.class.js +74 -0
  28. package/src/plugins/api/auth/strategies/factory.class.js +63 -0
  29. package/src/plugins/api/auth/strategies/global-strategy.class.js +44 -0
  30. package/src/plugins/api/auth/strategies/path-based-strategy.class.js +83 -0
  31. package/src/plugins/api/auth/strategies/path-rules-strategy.class.js +118 -0
  32. package/src/plugins/api/concerns/failban-manager.js +106 -57
  33. package/src/plugins/api/concerns/opengraph-helper.js +116 -0
  34. package/src/plugins/api/concerns/route-context.js +601 -0
  35. package/src/plugins/api/concerns/state-machine.js +288 -0
  36. package/src/plugins/api/index.js +180 -41
  37. package/src/plugins/api/routes/auth-routes.js +198 -30
  38. package/src/plugins/api/routes/resource-routes.js +19 -4
  39. package/src/plugins/api/server/health-manager.class.js +163 -0
  40. package/src/plugins/api/server/middleware-chain.class.js +310 -0
  41. package/src/plugins/api/server/router.class.js +472 -0
  42. package/src/plugins/api/server.js +280 -1303
  43. package/src/plugins/api/utils/custom-routes.js +17 -5
  44. package/src/plugins/api/utils/guards.js +76 -17
  45. package/src/plugins/api/utils/openapi-generator-cached.class.js +133 -0
  46. package/src/plugins/api/utils/openapi-generator.js +7 -6
  47. package/src/plugins/api/utils/template-engine.js +77 -3
  48. package/src/plugins/audit.plugin.js +30 -8
  49. package/src/plugins/backup.plugin.js +110 -14
  50. package/src/plugins/cache/cache.class.js +22 -5
  51. package/src/plugins/cache/filesystem-cache.class.js +116 -19
  52. package/src/plugins/cache/memory-cache.class.js +211 -57
  53. package/src/plugins/cache/multi-tier-cache.class.js +371 -0
  54. package/src/plugins/cache/partition-aware-filesystem-cache.class.js +168 -47
  55. package/src/plugins/cache/redis-cache.class.js +552 -0
  56. package/src/plugins/cache/s3-cache.class.js +17 -8
  57. package/src/plugins/cache.plugin.js +176 -61
  58. package/src/plugins/cloud-inventory/drivers/alibaba-driver.js +8 -1
  59. package/src/plugins/cloud-inventory/drivers/aws-driver.js +60 -29
  60. package/src/plugins/cloud-inventory/drivers/azure-driver.js +8 -1
  61. package/src/plugins/cloud-inventory/drivers/base-driver.js +16 -2
  62. package/src/plugins/cloud-inventory/drivers/cloudflare-driver.js +8 -1
  63. package/src/plugins/cloud-inventory/drivers/digitalocean-driver.js +8 -1
  64. package/src/plugins/cloud-inventory/drivers/hetzner-driver.js +8 -1
  65. package/src/plugins/cloud-inventory/drivers/linode-driver.js +8 -1
  66. package/src/plugins/cloud-inventory/drivers/mongodb-atlas-driver.js +8 -1
  67. package/src/plugins/cloud-inventory/drivers/vultr-driver.js +8 -1
  68. package/src/plugins/cloud-inventory/index.js +29 -8
  69. package/src/plugins/cloud-inventory/registry.js +64 -42
  70. package/src/plugins/cloud-inventory.plugin.js +240 -138
  71. package/src/plugins/concerns/plugin-dependencies.js +54 -0
  72. package/src/plugins/concerns/resource-names.js +100 -0
  73. package/src/plugins/consumers/index.js +10 -2
  74. package/src/plugins/consumers/sqs-consumer.js +12 -2
  75. package/src/plugins/cookie-farm-suite.plugin.js +278 -0
  76. package/src/plugins/cookie-farm.errors.js +73 -0
  77. package/src/plugins/cookie-farm.plugin.js +869 -0
  78. package/src/plugins/costs.plugin.js +7 -1
  79. package/src/plugins/eventual-consistency/analytics.js +94 -19
  80. package/src/plugins/eventual-consistency/config.js +15 -7
  81. package/src/plugins/eventual-consistency/consolidation.js +29 -11
  82. package/src/plugins/eventual-consistency/garbage-collection.js +3 -1
  83. package/src/plugins/eventual-consistency/helpers.js +39 -14
  84. package/src/plugins/eventual-consistency/install.js +21 -2
  85. package/src/plugins/eventual-consistency/utils.js +32 -10
  86. package/src/plugins/fulltext.plugin.js +38 -11
  87. package/src/plugins/geo.plugin.js +61 -9
  88. package/src/plugins/identity/concerns/config.js +61 -0
  89. package/src/plugins/identity/concerns/mfa-manager.js +15 -2
  90. package/src/plugins/identity/concerns/rate-limit.js +124 -0
  91. package/src/plugins/identity/concerns/resource-schemas.js +9 -1
  92. package/src/plugins/identity/concerns/token-generator.js +29 -4
  93. package/src/plugins/identity/drivers/auth-driver.interface.js +76 -0
  94. package/src/plugins/identity/drivers/client-credentials-driver.js +127 -0
  95. package/src/plugins/identity/drivers/index.js +18 -0
  96. package/src/plugins/identity/drivers/password-driver.js +122 -0
  97. package/src/plugins/identity/email-service.js +17 -2
  98. package/src/plugins/identity/index.js +413 -69
  99. package/src/plugins/identity/oauth2-server.js +413 -30
  100. package/src/plugins/identity/oidc-discovery.js +16 -8
  101. package/src/plugins/identity/rsa-keys.js +115 -35
  102. package/src/plugins/identity/server.js +166 -45
  103. package/src/plugins/identity/session-manager.js +53 -7
  104. package/src/plugins/identity/ui/pages/mfa-verification.js +17 -15
  105. package/src/plugins/identity/ui/routes.js +363 -255
  106. package/src/plugins/importer/index.js +153 -20
  107. package/src/plugins/index.js +9 -2
  108. package/src/plugins/kubernetes-inventory/index.js +6 -0
  109. package/src/plugins/kubernetes-inventory/k8s-driver.js +867 -0
  110. package/src/plugins/kubernetes-inventory/resource-types.js +274 -0
  111. package/src/plugins/kubernetes-inventory.plugin.js +980 -0
  112. package/src/plugins/metrics.plugin.js +64 -16
  113. package/src/plugins/ml/base-model.class.js +25 -15
  114. package/src/plugins/ml/regression-model.class.js +1 -1
  115. package/src/plugins/ml.errors.js +57 -25
  116. package/src/plugins/ml.plugin.js +28 -4
  117. package/src/plugins/namespace.js +210 -0
  118. package/src/plugins/plugin.class.js +180 -8
  119. package/src/plugins/puppeteer/console-monitor.js +729 -0
  120. package/src/plugins/puppeteer/cookie-manager.js +492 -0
  121. package/src/plugins/puppeteer/network-monitor.js +816 -0
  122. package/src/plugins/puppeteer/performance-manager.js +746 -0
  123. package/src/plugins/puppeteer/proxy-manager.js +478 -0
  124. package/src/plugins/puppeteer/stealth-manager.js +556 -0
  125. package/src/plugins/puppeteer.errors.js +81 -0
  126. package/src/plugins/puppeteer.plugin.js +1327 -0
  127. package/src/plugins/queue-consumer.plugin.js +69 -14
  128. package/src/plugins/recon/behaviors/uptime-behavior.js +691 -0
  129. package/src/plugins/recon/concerns/command-runner.js +148 -0
  130. package/src/plugins/recon/concerns/diff-detector.js +372 -0
  131. package/src/plugins/recon/concerns/fingerprint-builder.js +307 -0
  132. package/src/plugins/recon/concerns/process-manager.js +338 -0
  133. package/src/plugins/recon/concerns/report-generator.js +478 -0
  134. package/src/plugins/recon/concerns/security-analyzer.js +571 -0
  135. package/src/plugins/recon/concerns/target-normalizer.js +68 -0
  136. package/src/plugins/recon/config/defaults.js +321 -0
  137. package/src/plugins/recon/config/resources.js +370 -0
  138. package/src/plugins/recon/index.js +778 -0
  139. package/src/plugins/recon/managers/dependency-manager.js +174 -0
  140. package/src/plugins/recon/managers/scheduler-manager.js +179 -0
  141. package/src/plugins/recon/managers/storage-manager.js +745 -0
  142. package/src/plugins/recon/managers/target-manager.js +274 -0
  143. package/src/plugins/recon/stages/asn-stage.js +314 -0
  144. package/src/plugins/recon/stages/certificate-stage.js +84 -0
  145. package/src/plugins/recon/stages/dns-stage.js +107 -0
  146. package/src/plugins/recon/stages/dnsdumpster-stage.js +362 -0
  147. package/src/plugins/recon/stages/fingerprint-stage.js +71 -0
  148. package/src/plugins/recon/stages/google-dorks-stage.js +440 -0
  149. package/src/plugins/recon/stages/http-stage.js +89 -0
  150. package/src/plugins/recon/stages/latency-stage.js +148 -0
  151. package/src/plugins/recon/stages/massdns-stage.js +302 -0
  152. package/src/plugins/recon/stages/osint-stage.js +1373 -0
  153. package/src/plugins/recon/stages/ports-stage.js +169 -0
  154. package/src/plugins/recon/stages/screenshot-stage.js +94 -0
  155. package/src/plugins/recon/stages/secrets-stage.js +514 -0
  156. package/src/plugins/recon/stages/subdomains-stage.js +295 -0
  157. package/src/plugins/recon/stages/tls-audit-stage.js +78 -0
  158. package/src/plugins/recon/stages/vulnerability-stage.js +78 -0
  159. package/src/plugins/recon/stages/web-discovery-stage.js +113 -0
  160. package/src/plugins/recon/stages/whois-stage.js +349 -0
  161. package/src/plugins/recon.plugin.js +75 -0
  162. package/src/plugins/recon.plugin.js.backup +2635 -0
  163. package/src/plugins/relation.errors.js +87 -14
  164. package/src/plugins/replicator.plugin.js +514 -137
  165. package/src/plugins/replicators/base-replicator.class.js +89 -1
  166. package/src/plugins/replicators/bigquery-replicator.class.js +66 -22
  167. package/src/plugins/replicators/dynamodb-replicator.class.js +22 -15
  168. package/src/plugins/replicators/mongodb-replicator.class.js +22 -15
  169. package/src/plugins/replicators/mysql-replicator.class.js +52 -17
  170. package/src/plugins/replicators/planetscale-replicator.class.js +30 -4
  171. package/src/plugins/replicators/postgres-replicator.class.js +62 -27
  172. package/src/plugins/replicators/s3db-replicator.class.js +25 -18
  173. package/src/plugins/replicators/schema-sync.helper.js +3 -3
  174. package/src/plugins/replicators/sqs-replicator.class.js +8 -2
  175. package/src/plugins/replicators/turso-replicator.class.js +23 -3
  176. package/src/plugins/replicators/webhook-replicator.class.js +42 -4
  177. package/src/plugins/s3-queue.plugin.js +464 -65
  178. package/src/plugins/scheduler.plugin.js +20 -6
  179. package/src/plugins/state-machine.plugin.js +40 -9
  180. package/src/plugins/tfstate/README.md +126 -126
  181. package/src/plugins/tfstate/base-driver.js +28 -4
  182. package/src/plugins/tfstate/errors.js +65 -10
  183. package/src/plugins/tfstate/filesystem-driver.js +52 -8
  184. package/src/plugins/tfstate/index.js +163 -90
  185. package/src/plugins/tfstate/s3-driver.js +64 -6
  186. package/src/plugins/ttl.plugin.js +72 -17
  187. package/src/plugins/vector/distances.js +18 -12
  188. package/src/plugins/vector/kmeans.js +26 -4
  189. package/src/resource.class.js +115 -19
  190. package/src/testing/factory.class.js +20 -3
  191. package/src/testing/seeder.class.js +7 -1
  192. package/src/clients/memory-client.md +0 -917
  193. package/src/plugins/cloud-inventory/drivers/mock-drivers.js +0 -449
@@ -0,0 +1,556 @@
1
+ /**
2
+ * StealthManager - Advanced Anti-Detection for Cookie Farm
3
+ *
4
+ * Implements sophisticated evasion techniques against cookie farm detection:
5
+ *
6
+ * Defense Mechanisms We Counter:
7
+ * 1. Session binding (IP/UA consistency)
8
+ * 2. Rate limiting (human-like timing)
9
+ * 3. JS challenges (automatic execution)
10
+ * 4. Fingerprint tracking (consistent profiles)
11
+ * 5. Geographic impossible travel (proxy planning)
12
+ * 6. User-Agent churn detection (stable profiles)
13
+ * 7. Honeypot cookies (JS execution simulation)
14
+ * 8. Behavioral analysis (mouse/scroll/timing)
15
+ * 9. Request velocity monitoring (throttled requests)
16
+ * 10. Multi-session concurrent use detection (serialized usage)
17
+ *
18
+ * Key Strategies:
19
+ * - ONE persona = ONE complete identity (never mix!)
20
+ * - Maintain consistency: IP, UA, viewport, timezone, language
21
+ * - Human-like timing: delays, jitter, patterns
22
+ * - JS execution: load resources, execute challenges
23
+ * - Geographic coherence: proxy geo + timezone + language match
24
+ */
25
+ import { NavigationError } from '../puppeteer.errors.js';
26
+
27
+ export class StealthManager {
28
+ constructor(plugin) {
29
+ this.plugin = plugin;
30
+ this.config = plugin.config.stealth || {};
31
+
32
+ // Timing profiles (human-like delays)
33
+ this.timingProfiles = {
34
+ 'very-slow': { min: 5000, max: 15000, jitter: 2000 },
35
+ 'slow': { min: 3000, max: 8000, jitter: 1500 },
36
+ 'normal': { min: 1000, max: 5000, jitter: 1000 },
37
+ 'fast': { min: 500, max: 2000, jitter: 500 }
38
+ };
39
+
40
+ // Geographic data (timezone + language mapping)
41
+ this.geoData = {
42
+ 'US': { timezones: ['America/New_York', 'America/Chicago', 'America/Los_Angeles'], languages: ['en-US'] },
43
+ 'BR': { timezones: ['America/Sao_Paulo'], languages: ['pt-BR', 'en-US'] },
44
+ 'GB': { timezones: ['Europe/London'], languages: ['en-GB', 'en-US'] },
45
+ 'DE': { timezones: ['Europe/Berlin'], languages: ['de-DE', 'en-US'] },
46
+ 'FR': { timezones: ['Europe/Paris'], languages: ['fr-FR', 'en-US'] },
47
+ 'JP': { timezones: ['Asia/Tokyo'], languages: ['ja-JP', 'en-US'] },
48
+ 'CN': { timezones: ['Asia/Shanghai'], languages: ['zh-CN', 'en-US'] }
49
+ };
50
+ }
51
+
52
+ /**
53
+ * Create a stealth-optimized persona profile
54
+ * Ensures all fingerprint components are consistent
55
+ */
56
+ async createStealthProfile(options = {}) {
57
+ const {
58
+ proxy = null,
59
+ country = null, // Target country (if known from proxy)
60
+ timingProfile = 'normal',
61
+ screenResolution = null
62
+ } = options;
63
+
64
+ // 1. Select consistent timezone + language based on country
65
+ const geoProfile = this._selectGeoProfile(country);
66
+
67
+ // 2. Generate consistent user agent
68
+ const userAgent = this._generateConsistentUserAgent();
69
+
70
+ // 3. Generate viewport that matches screen resolution
71
+ const viewport = this._generateConsistentViewport(screenResolution);
72
+
73
+ // 4. Create fingerprint profile
74
+ const profile = {
75
+ // Core identity
76
+ userAgent,
77
+ viewport,
78
+ timezone: geoProfile.timezone,
79
+ language: geoProfile.language,
80
+
81
+ // Consistency markers
82
+ acceptLanguage: `${geoProfile.language},en;q=0.9`,
83
+ acceptEncoding: 'gzip, deflate, br',
84
+ accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
85
+
86
+ // Platform consistency
87
+ platform: this._getPlatformFromUA(userAgent),
88
+ hardwareConcurrency: this._getHardwareConcurrency(userAgent),
89
+ deviceMemory: this._getDeviceMemory(userAgent),
90
+
91
+ // Timing behavior
92
+ timingProfile: this.timingProfiles[timingProfile],
93
+
94
+ // Proxy binding
95
+ proxyId: proxy?.id || null,
96
+ proxyCountry: country,
97
+
98
+ // Behavioral markers
99
+ behavioral: {
100
+ typingSpeed: { min: 100, max: 300 }, // ms per character
101
+ mouseMovements: true,
102
+ scrollBehavior: 'smooth',
103
+ clickDelay: { min: 200, max: 800 }
104
+ }
105
+ };
106
+
107
+ return profile;
108
+ }
109
+
110
+ /**
111
+ * Select geo profile (timezone + language) based on country
112
+ * @private
113
+ */
114
+ _selectGeoProfile(country) {
115
+ if (country && this.geoData[country]) {
116
+ const data = this.geoData[country];
117
+ const timezone = data.timezones[Math.floor(Math.random() * data.timezones.length)];
118
+ const language = data.languages[0]; // Primary language
119
+ return { timezone, language };
120
+ }
121
+
122
+ // Default to US
123
+ return {
124
+ timezone: 'America/New_York',
125
+ language: 'en-US'
126
+ };
127
+ }
128
+
129
+ /**
130
+ * Generate consistent user agent (avoid churn)
131
+ * @private
132
+ */
133
+ _generateConsistentUserAgent() {
134
+ // Use common, stable user agents
135
+ const stableUserAgents = [
136
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
137
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36',
138
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
139
+ 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
140
+ ];
141
+
142
+ return stableUserAgents[Math.floor(Math.random() * stableUserAgents.length)];
143
+ }
144
+
145
+ /**
146
+ * Generate viewport consistent with screen resolution
147
+ * @private
148
+ */
149
+ _generateConsistentViewport(screenResolution) {
150
+ const commonResolutions = [
151
+ { width: 1920, height: 1080, deviceScaleFactor: 1 }, // Full HD
152
+ { width: 1680, height: 1050, deviceScaleFactor: 1 }, // WSXGA+
153
+ { width: 1440, height: 900, deviceScaleFactor: 1 }, // WXGA+
154
+ { width: 1366, height: 768, deviceScaleFactor: 1 }, // HD
155
+ { width: 2560, height: 1440, deviceScaleFactor: 1 } // QHD
156
+ ];
157
+
158
+ if (screenResolution) {
159
+ return screenResolution;
160
+ }
161
+
162
+ return commonResolutions[Math.floor(Math.random() * commonResolutions.length)];
163
+ }
164
+
165
+ /**
166
+ * Get platform from user agent
167
+ * @private
168
+ */
169
+ _getPlatformFromUA(userAgent) {
170
+ if (userAgent.includes('Windows')) return 'Win32';
171
+ if (userAgent.includes('Mac')) return 'MacIntel';
172
+ if (userAgent.includes('Linux')) return 'Linux x86_64';
173
+ return 'Win32';
174
+ }
175
+
176
+ /**
177
+ * Get hardware concurrency (CPU cores) from user agent
178
+ * @private
179
+ */
180
+ _getHardwareConcurrency(userAgent) {
181
+ // Desktop: 4-8 cores common
182
+ // Mobile: 4-8 cores
183
+ return [4, 6, 8][Math.floor(Math.random() * 3)];
184
+ }
185
+
186
+ /**
187
+ * Get device memory from user agent
188
+ * @private
189
+ */
190
+ _getDeviceMemory(userAgent) {
191
+ // Common: 4, 8, 16 GB
192
+ return [4, 8, 16][Math.floor(Math.random() * 3)];
193
+ }
194
+
195
+ /**
196
+ * Apply stealth profile to page
197
+ * Overrides navigator properties to match profile
198
+ */
199
+ async applyStealthProfile(page, profile) {
200
+ // 1. Set timezone
201
+ await page.emulateTimezone(profile.timezone);
202
+
203
+ // 2. Override navigator properties
204
+ await page.evaluateOnNewDocument((profile) => {
205
+ // Override platform
206
+ Object.defineProperty(navigator, 'platform', {
207
+ get: () => profile.platform
208
+ });
209
+
210
+ // Override hardwareConcurrency
211
+ Object.defineProperty(navigator, 'hardwareConcurrency', {
212
+ get: () => profile.hardwareConcurrency
213
+ });
214
+
215
+ // Override deviceMemory
216
+ Object.defineProperty(navigator, 'deviceMemory', {
217
+ get: () => profile.deviceMemory
218
+ });
219
+
220
+ // Override languages
221
+ Object.defineProperty(navigator, 'languages', {
222
+ get: () => [profile.language, 'en']
223
+ });
224
+
225
+ // Override webdriver flag (anti-detection)
226
+ Object.defineProperty(navigator, 'webdriver', {
227
+ get: () => false
228
+ });
229
+
230
+ // Add chrome object (missing in headless)
231
+ if (!window.chrome) {
232
+ window.chrome = {
233
+ runtime: {}
234
+ };
235
+ }
236
+
237
+ // Override permissions
238
+ const originalQuery = window.navigator.permissions.query;
239
+ window.navigator.permissions.query = (parameters) => (
240
+ parameters.name === 'notifications'
241
+ ? Promise.resolve({ state: Notification.permission })
242
+ : originalQuery(parameters)
243
+ );
244
+ }, profile);
245
+
246
+ // 3. Set extra HTTP headers
247
+ await page.setExtraHTTPHeaders({
248
+ 'Accept-Language': profile.acceptLanguage,
249
+ 'Accept-Encoding': profile.acceptEncoding,
250
+ 'Accept': profile.accept
251
+ });
252
+ }
253
+
254
+ /**
255
+ * Execute JS challenges automatically
256
+ * Simulates human JS execution
257
+ */
258
+ async executeJSChallenges(page) {
259
+ try {
260
+ // 1. Wait for page to be fully loaded
261
+ await page.waitForFunction(() => document.readyState === 'complete', {
262
+ timeout: 10000
263
+ });
264
+
265
+ // 2. Execute common JS challenges
266
+ await page.evaluate(() => {
267
+ // Set honeypot cookie (if expected)
268
+ if (!document.cookie.includes('js_ok')) {
269
+ document.cookie = 'js_ok=1; path=/';
270
+ }
271
+
272
+ // Execute any window.__JS_CHALLENGE if present
273
+ if (window.__JS_CHALLENGE_INIT) {
274
+ window.__JS_CHALLENGE_INIT();
275
+ }
276
+
277
+ // Trigger load events
278
+ window.dispatchEvent(new Event('load'));
279
+ document.dispatchEvent(new Event('DOMContentLoaded'));
280
+ });
281
+
282
+ // 3. Load resources (images, CSS, fonts) to simulate real browser
283
+ await this._loadPageResources(page);
284
+
285
+ // 4. Execute fetch to /.js-challenge if endpoint exists
286
+ await page.evaluate(async () => {
287
+ try {
288
+ const response = await fetch('/.js-challenge', {
289
+ method: 'GET',
290
+ credentials: 'include'
291
+ });
292
+ if (response.ok) {
293
+ const data = await response.json();
294
+ if (data.token) {
295
+ window.__JS_CHALLENGE_TOKEN = data.token;
296
+ }
297
+ }
298
+ } catch (err) {
299
+ // Endpoint may not exist - that's ok
300
+ }
301
+ });
302
+
303
+ } catch (err) {
304
+ // JS challenge execution failed - continue anyway
305
+ this.plugin.emit('stealth.jsChallengeWarning', {
306
+ error: err.message
307
+ });
308
+ }
309
+ }
310
+
311
+ /**
312
+ * Load page resources to simulate real browser
313
+ * @private
314
+ */
315
+ async _loadPageResources(page) {
316
+ try {
317
+ // Get all images and trigger load
318
+ await page.evaluate(() => {
319
+ const images = Array.from(document.querySelectorAll('img'));
320
+ images.forEach(img => {
321
+ if (!img.complete) {
322
+ img.dispatchEvent(new Event('load'));
323
+ }
324
+ });
325
+ });
326
+
327
+ // Wait for fonts to load
328
+ await page.evaluateHandle(() => document.fonts.ready);
329
+
330
+ } catch (err) {
331
+ // Resource loading failed - continue
332
+ }
333
+ }
334
+
335
+ /**
336
+ * Add human-like delay between actions
337
+ * Uses timing profile from persona
338
+ */
339
+ async humanDelay(profile, action = 'default') {
340
+ const timing = profile.timingProfile;
341
+
342
+ const baseDelay = timing.min + Math.random() * (timing.max - timing.min);
343
+ const jitter = (Math.random() - 0.5) * timing.jitter;
344
+ const totalDelay = Math.max(100, baseDelay + jitter);
345
+
346
+ await this._delay(totalDelay);
347
+ }
348
+
349
+ /**
350
+ * Simulate human typing with profile-specific speed
351
+ */
352
+ async humanType(page, selector, text, profile) {
353
+ const element = await page.$(selector);
354
+ if (!element) {
355
+ throw new NavigationError(`Element not found: ${selector}`, {
356
+ operation: 'humanType',
357
+ retriable: false,
358
+ suggestion: 'Verify the selector exists on the target page before invoking humanType.',
359
+ selector
360
+ });
361
+ }
362
+
363
+ await element.click();
364
+
365
+ // Type character by character with human timing
366
+ for (const char of text) {
367
+ await page.keyboard.type(char);
368
+
369
+ const { min, max } = profile.behavioral.typingSpeed;
370
+ const charDelay = min + Math.random() * (max - min);
371
+ await this._delay(charDelay);
372
+
373
+ // Random pause after punctuation
374
+ if (['.', ',', '!', '?'].includes(char)) {
375
+ await this._delay(200 + Math.random() * 300);
376
+ }
377
+ }
378
+ }
379
+
380
+ /**
381
+ * Simulate realistic request pacing
382
+ * Prevents rate limiting / velocity detection
383
+ */
384
+ async paceRequests(persona, requestCount) {
385
+ // Calculate safe delay based on request count
386
+ // Goal: stay under detection thresholds (e.g., 50 req/min)
387
+
388
+ const maxRequestsPerMinute = 30; // Conservative
389
+ const minDelayMs = (60 * 1000) / maxRequestsPerMinute;
390
+
391
+ // Add random jitter to avoid patterns
392
+ const jitter = minDelayMs * (0.5 + Math.random() * 0.5);
393
+ const totalDelay = minDelayMs + jitter;
394
+
395
+ await this._delay(totalDelay);
396
+
397
+ // Track request for persona stats
398
+ persona.metadata.lastRequestTime = Date.now();
399
+ }
400
+
401
+ /**
402
+ * Check if persona should "rest" (cooldown)
403
+ * Prevents velocity/concurrent use detection
404
+ */
405
+ shouldRest(persona) {
406
+ const now = Date.now();
407
+ const lastUsed = persona.metadata.lastUsed || 0;
408
+ const timeSinceLastUse = now - lastUsed;
409
+
410
+ // If used in last 5 seconds, should rest
411
+ if (timeSinceLastUse < 5000) {
412
+ return true;
413
+ }
414
+
415
+ // If high request count in short time, should rest
416
+ const requestsInLastMinute = persona.metadata.recentRequests || 0;
417
+ if (requestsInLastMinute > 20) {
418
+ return true;
419
+ }
420
+
421
+ return false;
422
+ }
423
+
424
+ /**
425
+ * Simulate mouse movements and scrolling
426
+ * Generates behavioral fingerprint
427
+ */
428
+ async simulateHumanBehavior(page, profile) {
429
+ try {
430
+ // 1. Random scroll
431
+ const scrollDistance = Math.floor(Math.random() * 500) + 200;
432
+ await page.evaluate((distance) => {
433
+ window.scrollBy({
434
+ top: distance,
435
+ behavior: 'smooth'
436
+ });
437
+ }, scrollDistance);
438
+
439
+ await this._delay(1000 + Math.random() * 1000);
440
+
441
+ // 2. Random mouse movement (if ghost-cursor available)
442
+ if (page._cursor) {
443
+ try {
444
+ // Move to random position
445
+ const viewport = await page.viewport();
446
+ const x = Math.floor(Math.random() * viewport.width);
447
+ const y = Math.floor(Math.random() * viewport.height);
448
+
449
+ await page._cursor.move({ x, y });
450
+ await this._delay(500);
451
+ } catch (err) {
452
+ // Cursor movement failed - continue
453
+ }
454
+ }
455
+
456
+ // 3. Random hover over elements
457
+ const elements = await page.$$('a, button, input');
458
+ if (elements.length > 0) {
459
+ const randomElement = elements[Math.floor(Math.random() * elements.length)];
460
+ await randomElement.hover().catch(() => {});
461
+ await this._delay(300 + Math.random() * 500);
462
+ }
463
+
464
+ } catch (err) {
465
+ // Behavior simulation failed - continue
466
+ }
467
+ }
468
+
469
+ /**
470
+ * Validate persona consistency before use
471
+ * Ensures no fingerprint leakage
472
+ */
473
+ validatePersonaConsistency(persona, currentContext) {
474
+ const warnings = [];
475
+
476
+ // Check IP consistency (if proxy bound)
477
+ if (persona.proxyId && currentContext.proxyId !== persona.proxyId) {
478
+ warnings.push({
479
+ type: 'PROXY_MISMATCH',
480
+ message: `Persona ${persona.personaId} bound to ${persona.proxyId} but using ${currentContext.proxyId}`,
481
+ severity: 'HIGH'
482
+ });
483
+ }
484
+
485
+ // Check UA consistency
486
+ if (currentContext.userAgent && currentContext.userAgent !== persona.userAgent) {
487
+ warnings.push({
488
+ type: 'UA_MISMATCH',
489
+ message: 'User agent changed',
490
+ severity: 'HIGH'
491
+ });
492
+ }
493
+
494
+ // Check viewport consistency
495
+ if (currentContext.viewport) {
496
+ if (currentContext.viewport.width !== persona.viewport.width ||
497
+ currentContext.viewport.height !== persona.viewport.height) {
498
+ warnings.push({
499
+ type: 'VIEWPORT_MISMATCH',
500
+ message: 'Viewport changed',
501
+ severity: 'MEDIUM'
502
+ });
503
+ }
504
+ }
505
+
506
+ // Check usage velocity
507
+ if (this.shouldRest(persona)) {
508
+ warnings.push({
509
+ type: 'HIGH_VELOCITY',
510
+ message: 'Persona used too frequently',
511
+ severity: 'MEDIUM'
512
+ });
513
+ }
514
+
515
+ return warnings;
516
+ }
517
+
518
+ /**
519
+ * Generate realistic browsing session
520
+ * Visits pages with human-like patterns
521
+ */
522
+ async generateBrowsingSession(page, profile, urls) {
523
+ for (let i = 0; i < urls.length; i++) {
524
+ const url = urls[i];
525
+
526
+ // Navigate
527
+ await page.goto(url, { waitUntil: 'networkidle2' });
528
+
529
+ // Execute JS challenges
530
+ await this.executeJSChallenges(page);
531
+
532
+ // Simulate human behavior
533
+ await this.simulateHumanBehavior(page, profile);
534
+
535
+ // Human delay before next page
536
+ await this.humanDelay(profile);
537
+
538
+ // Random chance to go back
539
+ if (i > 0 && Math.random() < 0.1) {
540
+ await page.goBack();
541
+ await this._delay(1000);
542
+ await page.goForward();
543
+ }
544
+ }
545
+ }
546
+
547
+ /**
548
+ * Delay helper
549
+ * @private
550
+ */
551
+ async _delay(ms) {
552
+ return new Promise(resolve => setTimeout(resolve, ms));
553
+ }
554
+ }
555
+
556
+ export default StealthManager;
@@ -0,0 +1,81 @@
1
+ import { PluginError } from '../errors.js';
2
+
3
+ /**
4
+ * Custom errors for PuppeteerPlugin with actionable messaging.
5
+ */
6
+
7
+ export class PuppeteerError extends PluginError {
8
+ constructor(message, details = {}) {
9
+ const merged = {
10
+ pluginName: details.pluginName || 'PuppeteerPlugin',
11
+ operation: details.operation || 'unknown',
12
+ statusCode: details.statusCode ?? 500,
13
+ retriable: details.retriable ?? false,
14
+ suggestion: details.suggestion ?? 'Review PuppeteerPlugin configuration (proxies, sessions, scripts) and retry.',
15
+ ...details
16
+ };
17
+ super(message, merged);
18
+ this.name = 'PuppeteerError';
19
+ }
20
+ }
21
+
22
+ export class BrowserPoolError extends PuppeteerError {
23
+ constructor(message, details = {}) {
24
+ super(message, {
25
+ code: 'BROWSER_POOL_ERROR',
26
+ retriable: details.retriable ?? true,
27
+ suggestion: details.suggestion ?? 'Verify browser instances are healthy and increase pool size or restart browsers.',
28
+ docs: details.docs || 'https://github.com/forattini-dev/s3db.js/blob/main/docs/plugins/puppeteer.md#browser-pool',
29
+ ...details
30
+ });
31
+ this.name = 'BrowserPoolError';
32
+ }
33
+ }
34
+
35
+ export class CookieManagerError extends PuppeteerError {
36
+ constructor(message, details = {}) {
37
+ super(message, {
38
+ code: 'COOKIE_MANAGER_ERROR',
39
+ retriable: details.retriable ?? false,
40
+ suggestion: details.suggestion ?? 'Check cookie storage configuration and ensure persona sessions are valid.',
41
+ ...details
42
+ });
43
+ this.name = 'CookieManagerError';
44
+ }
45
+ }
46
+
47
+ export class NavigationError extends PuppeteerError {
48
+ constructor(message, details = {}) {
49
+ super(message, {
50
+ code: 'NAVIGATION_ERROR',
51
+ retriable: details.retriable ?? true,
52
+ suggestion: details.suggestion ?? 'Validate target URLs, network access, and waitFor options before retrying.',
53
+ ...details
54
+ });
55
+ this.name = 'NavigationError';
56
+ }
57
+ }
58
+
59
+ export class HumanBehaviorError extends PuppeteerError {
60
+ constructor(message, details = {}) {
61
+ super(message, {
62
+ code: 'HUMAN_BEHAVIOR_ERROR',
63
+ retriable: details.retriable ?? false,
64
+ suggestion: details.suggestion ?? 'Adjust human behavior thresholds or provide alternate interaction scripts.',
65
+ ...details
66
+ });
67
+ this.name = 'HumanBehaviorError';
68
+ }
69
+ }
70
+
71
+ export class SessionError extends PuppeteerError {
72
+ constructor(message, details = {}) {
73
+ super(message, {
74
+ code: 'SESSION_ERROR',
75
+ retriable: details.retriable ?? true,
76
+ suggestion: details.suggestion ?? 'Refresh or recreate the browser session and ensure session storage is writable.',
77
+ ...details
78
+ });
79
+ this.name = 'SessionError';
80
+ }
81
+ }