@fanboynz/network-scanner 2.0.65 → 3.0.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/lib/referrer.js CHANGED
@@ -1,6 +1,12 @@
1
1
  // === Referrer Header Generation Module ===
2
2
  // This module handles generation of referrer headers for different traffic simulation modes
3
3
 
4
+ const { formatLogMessage, messageColors } = require('./colorize');
5
+
6
+ // Precomputed colored '[referrer]' subsystem prefix — matches the project
7
+ // convention used by other modules (flowproxy/cloudflare/smart-cache/etc.).
8
+ const REFERRER_TAG = messageColors.processing('[referrer]');
9
+
4
10
  /**
5
11
  * Performance utility: Get random element from array
6
12
  * Reduces code duplication and improves readability
@@ -78,7 +84,10 @@ const REFERRER_COLLECTIONS = Object.freeze({
78
84
  * @returns {string} Selected search term
79
85
  */
80
86
  function generateSearchTerm(customTerms, context = null) {
81
- if (customTerms && customTerms.length > 0) {
87
+ // Array.isArray guard belt-and-braces: validateReferrerConfig now blocks
88
+ // non-array search_terms at config load, but a direct internal caller
89
+ // could still pass a non-array and trigger a TypeError on .length.
90
+ if (Array.isArray(customTerms) && customTerms.length > 0) {
82
91
  return getRandomElement(customTerms);
83
92
  }
84
93
 
@@ -107,7 +116,7 @@ function generateSearchReferrer(searchTerms, context, forceDebug) {
107
116
  const referrerUrl = randomEngine + encodeURIComponent(searchTerm);
108
117
 
109
118
  if (forceDebug) {
110
- console.log(`[debug] Generated search referrer: ${referrerUrl} (engine: ${randomEngine.split('//')[1].split('/')[0]}, term: "${searchTerm}")`);
119
+ console.log(formatLogMessage('debug', `${REFERRER_TAG} Generated search referrer: ${referrerUrl} (engine: ${randomEngine.split('//')[1].split('/')[0]}, term: "${searchTerm}")`));
111
120
  }
112
121
 
113
122
  return referrerUrl;
@@ -122,7 +131,7 @@ function generateSocialMediaReferrer(forceDebug) {
122
131
  const randomSocial = getRandomElement(REFERRER_COLLECTIONS.SOCIAL_MEDIA);
123
132
 
124
133
  if (forceDebug) {
125
- console.log(`[debug] Generated social media referrer: ${randomSocial}`);
134
+ console.log(formatLogMessage('debug', `${REFERRER_TAG} Generated social media referrer: ${randomSocial}`));
126
135
  }
127
136
 
128
137
  return randomSocial;
@@ -137,7 +146,7 @@ function generateNewsReferrer(forceDebug) {
137
146
  const randomNews = getRandomElement(REFERRER_COLLECTIONS.NEWS_SITES);
138
147
 
139
148
  if (forceDebug) {
140
- console.log(`[debug] Generated news referrer: ${randomNews}`);
149
+ console.log(formatLogMessage('debug', `${REFERRER_TAG} Generated news referrer: ${randomNews}`));
141
150
  }
142
151
 
143
152
  return randomNews;
@@ -179,35 +188,44 @@ function shouldDisableReferrer(targetUrl, disableList, forceDebug = false) {
179
188
 
180
189
  for (const disablePattern of disableList) {
181
190
  if (typeof disablePattern !== 'string') continue;
182
-
191
+
183
192
  // Fast check: Exact URL match (no parsing needed)
184
193
  if (targetUrl === disablePattern) {
185
- if (forceDebug) console.log(`[debug] Referrer disabled for exact match: ${targetUrl}`);
194
+ if (forceDebug) console.log(formatLogMessage('debug', `${REFERRER_TAG} Referrer disabled for exact match: ${targetUrl}`));
186
195
  return true;
187
196
  }
188
-
189
- // Domain/hostname match (use cached parsed URL)
190
- if (targetUrlParsed) {
191
- try {
192
- const disableHostname = new URL(disablePattern).hostname;
193
- if (targetHostname === disableHostname) {
194
- if (forceDebug) console.log(`[debug] Referrer disabled for domain match: ${targetHostname}`);
195
- return true;
196
- }
197
- } catch (e) {
198
- // disablePattern is not a valid URL, try substring match below
199
- }
197
+
198
+ // Resolve pattern to a hostname full URL patterns ('https://example.com')
199
+ // and bare-hostname patterns ('example.com') both end up running through
200
+ // the same suffix-match logic so they behave identically. Previously
201
+ // full-URL patterns only did exact hostname equality (no subdomain
202
+ // match), while bare-hostname patterns did suffix match — same user
203
+ // intent, different result depending on string form.
204
+ let patternHostname = null;
205
+ if (disablePattern.includes('/')) {
206
+ try { patternHostname = new URL(disablePattern).hostname; } catch (_) { /* fall through */ }
207
+ } else {
208
+ patternHostname = disablePattern;
200
209
  }
201
-
202
- // Fallback: Simple substring match (for patterns like 'example.com')
203
- if (!targetUrlParsed || disablePattern.includes('/') === false) {
210
+
211
+ if (targetUrlParsed && patternHostname) {
212
+ const p = patternHostname.toLowerCase();
213
+ const h = targetHostname.toLowerCase();
214
+ if (h === p || h.endsWith('.' + p)) {
215
+ if (forceDebug) console.log(formatLogMessage('debug', `${REFERRER_TAG} Referrer disabled for hostname match: ${p} matches ${h}`));
216
+ return true;
217
+ }
218
+ } else if (!targetUrlParsed) {
219
+ // Pathological fallback: target URL didn't parse. Substring match
220
+ // as last resort. Shouldn't fire in practice — we only call this
221
+ // on URLs we're about to navigate to.
204
222
  if (targetUrl.includes(disablePattern)) {
205
- if (forceDebug) console.log(`[debug] Referrer disabled for pattern match: ${disablePattern} in ${targetUrl}`);
223
+ if (forceDebug) console.log(formatLogMessage('debug', `${REFERRER_TAG} Referrer disabled for pattern match (unparseable target): ${disablePattern}`));
206
224
  return true;
207
225
  }
208
226
  }
209
227
  }
210
-
228
+
211
229
  return false;
212
230
  }
213
231
 
@@ -218,94 +236,94 @@ function shouldDisableReferrer(targetUrl, disableList, forceDebug = false) {
218
236
  * @returns {string} Generated referrer URL or empty string
219
237
  */
220
238
  function generateReferrerUrl(referrerConfig, forceDebug = false) {
221
- try {
222
- // Handle simple string URLs
223
- if (typeof referrerConfig === 'string') {
224
- const url = isValidUrl(referrerConfig) ? referrerConfig : '';
225
- if (forceDebug && url) {
226
- console.log(`[debug] Using direct referrer URL: ${url}`);
227
- } else if (forceDebug && !url) {
228
- console.log(`[debug] Invalid referrer URL provided: ${referrerConfig}`);
229
- }
230
- return url;
239
+ // No top-level try/catch — nothing in here throws synchronously.
240
+ // typeof / Array.isArray / object property access / string concat /
241
+ // console.log / the helper functions are all non-throwing. The old
242
+ // try/catch was unreachable defensive scaffolding.
243
+
244
+ // Handle simple string URLs
245
+ if (typeof referrerConfig === 'string') {
246
+ const url = isValidUrl(referrerConfig) ? referrerConfig : '';
247
+ if (forceDebug && url) {
248
+ console.log(formatLogMessage('debug', `${REFERRER_TAG} Using direct referrer URL: ${url}`));
249
+ } else if (forceDebug && !url) {
250
+ console.log(formatLogMessage('debug', `${REFERRER_TAG} Invalid referrer URL provided: ${referrerConfig}`));
231
251
  }
232
-
233
- // Handle arrays - pick random URL
234
- if (Array.isArray(referrerConfig)) {
235
- if (referrerConfig.length === 0) {
236
- if (forceDebug) console.log(`[debug] Empty referrer array provided`);
237
- return '';
252
+ return url;
253
+ }
254
+
255
+ // Handle arrays - pick random URL
256
+ if (Array.isArray(referrerConfig)) {
257
+ if (referrerConfig.length === 0) {
258
+ if (forceDebug) console.log(formatLogMessage('debug', `${REFERRER_TAG} Empty referrer array provided`));
259
+ return '';
260
+ }
261
+
262
+ const randomUrl = getRandomElement(referrerConfig);
263
+ const url = isValidUrl(randomUrl) ? randomUrl : '';
264
+
265
+ if (forceDebug) {
266
+ console.log(formatLogMessage('debug', `${REFERRER_TAG} Selected referrer from array (${referrerConfig.length} options): ${url || 'invalid URL'}`));
267
+ }
268
+
269
+ return url;
270
+ }
271
+
272
+ // Handle object modes
273
+ if (typeof referrerConfig === 'object' && referrerConfig !== null && referrerConfig.mode) {
274
+ switch (referrerConfig.mode) {
275
+ case 'random_search': {
276
+ const searchTerms = referrerConfig.search_terms;
277
+ const context = referrerConfig.context; // Optional context hint
278
+ return generateSearchReferrer(searchTerms, context, forceDebug);
238
279
  }
239
-
240
- const randomUrl = getRandomElement(referrerConfig);
241
- const url = isValidUrl(randomUrl) ? randomUrl : '';
242
-
243
- if (forceDebug) {
244
- console.log(`[debug] Selected referrer from array (${referrerConfig.length} options): ${url || 'invalid URL'}`);
280
+
281
+ case 'social_media': {
282
+ return generateSocialMediaReferrer(forceDebug);
245
283
  }
246
-
247
- return url;
248
- }
249
-
250
- // Handle object modes
251
- if (typeof referrerConfig === 'object' && referrerConfig !== null && referrerConfig.mode) {
252
- switch (referrerConfig.mode) {
253
- case 'random_search': {
254
- const searchTerms = referrerConfig.search_terms;
255
- const context = referrerConfig.context; // Optional context hint
256
- return generateSearchReferrer(searchTerms, context, forceDebug);
257
- }
258
-
259
- case 'social_media': {
260
- return generateSocialMediaReferrer(forceDebug);
261
- }
262
-
263
- case 'news_sites': {
264
- return generateNewsReferrer(forceDebug);
265
- }
266
-
267
- case 'direct_navigation': {
268
- if (forceDebug) console.log(`[debug] Using direct navigation (no referrer)`);
269
- return '';
270
- }
271
-
272
- case 'custom': {
273
- const url = isValidUrl(referrerConfig.url) ? referrerConfig.url : '';
274
- if (forceDebug) {
275
- console.log(`[debug] Using custom referrer URL: ${url || 'invalid URL provided'}`);
276
- }
277
- return url;
278
- }
279
-
280
- case 'mixed': {
281
- // Randomly choose between different referrer types
282
- const modes = ['random_search', 'social_media', 'news_sites'];
283
- const randomMode = getRandomElement(modes);
284
-
285
- if (forceDebug) console.log(`[debug] Mixed mode selected: ${randomMode}`);
286
-
287
- const mixedConfig = { mode: randomMode };
288
- if (randomMode === 'random_search' && referrerConfig.search_terms) {
289
- mixedConfig.search_terms = referrerConfig.search_terms;
290
- mixedConfig.context = referrerConfig.context;
291
- }
292
-
293
- return generateReferrerUrl(mixedConfig, forceDebug);
284
+
285
+ case 'news_sites': {
286
+ return generateNewsReferrer(forceDebug);
287
+ }
288
+
289
+ case 'direct_navigation': {
290
+ if (forceDebug) console.log(formatLogMessage('debug', `${REFERRER_TAG} Using direct navigation (no referrer)`));
291
+ return '';
292
+ }
293
+
294
+ case 'custom': {
295
+ const url = isValidUrl(referrerConfig.url) ? referrerConfig.url : '';
296
+ if (forceDebug) {
297
+ console.log(formatLogMessage('debug', `${REFERRER_TAG} Using custom referrer URL: ${url || 'invalid URL provided'}`));
294
298
  }
295
-
296
- default: {
297
- if (forceDebug) console.log(`[debug] Unknown referrer mode: ${referrerConfig.mode}`);
298
- return '';
299
+ return url;
300
+ }
301
+
302
+ case 'mixed': {
303
+ // Randomly choose between different referrer types
304
+ const modes = ['random_search', 'social_media', 'news_sites'];
305
+ const randomMode = getRandomElement(modes);
306
+
307
+ if (forceDebug) console.log(formatLogMessage('debug', `${REFERRER_TAG} Mixed mode selected: ${randomMode}`));
308
+
309
+ const mixedConfig = { mode: randomMode };
310
+ if (randomMode === 'random_search' && referrerConfig.search_terms) {
311
+ mixedConfig.search_terms = referrerConfig.search_terms;
312
+ mixedConfig.context = referrerConfig.context;
299
313
  }
314
+
315
+ return generateReferrerUrl(mixedConfig, forceDebug);
316
+ }
317
+
318
+ default: {
319
+ if (forceDebug) console.log(formatLogMessage('debug', `${REFERRER_TAG} Unknown referrer mode: ${referrerConfig.mode}`));
320
+ return '';
300
321
  }
301
322
  }
302
-
303
- if (forceDebug) console.log(`[debug] Invalid referrer configuration type: ${typeof referrerConfig}`);
304
- return '';
305
- } catch (err) {
306
- if (forceDebug) console.log(`[debug] Referrer generation failed: ${err.message}`);
307
- return '';
308
323
  }
324
+
325
+ if (forceDebug) console.log(formatLogMessage('debug', `${REFERRER_TAG} Invalid referrer configuration type: ${typeof referrerConfig}`));
326
+ return '';
309
327
  }
310
328
 
311
329
  /**
@@ -350,30 +368,24 @@ function validateReferrerConfig(referrerConfig) {
350
368
  return result;
351
369
  }
352
370
 
353
- // Validate arrays
371
+ // Validate arrays — every item gets checked. The old code spot-checked
372
+ // only first and last when length > 10 "for performance", but config
373
+ // validation runs ONCE at startup and referrer arrays are tiny; the
374
+ // perf savings were imaginary, the correctness gap (items 2..N-1 never
375
+ // validated, typo'd URLs slipping through) was real.
354
376
  if (Array.isArray(referrerConfig)) {
355
377
  if (referrerConfig.length === 0) {
356
378
  result.warnings.push('Empty referrer array will result in no referrer');
357
379
  return result;
358
380
  }
359
-
360
- // Fast validation: check only first and last items if array is large
361
- const itemsToCheck = referrerConfig.length > 10
362
- ? [referrerConfig[0], referrerConfig[referrerConfig.length - 1]]
363
- : referrerConfig;
364
-
365
- itemsToCheck.forEach((url, index) => {
366
- if (!isValidUrl(url)) {
367
- const actualIndex = itemsToCheck === referrerConfig ? index : (index === 0 ? 0 : referrerConfig.length - 1);
368
- result.errors.push(`Array item ${actualIndex} is not a valid HTTP/HTTPS URL: ${url}`);
369
- result.isValid = false;
370
- }
371
- });
372
381
 
373
- if (referrerConfig.length > 10 && itemsToCheck.length < referrerConfig.length) {
374
- result.warnings.push(`Large array (${referrerConfig.length} items): only validated first and last items for performance`);
382
+ for (let i = 0; i < referrerConfig.length; i++) {
383
+ if (!isValidUrl(referrerConfig[i])) {
384
+ result.errors.push(`Array item ${i} is not a valid HTTP/HTTPS URL: ${referrerConfig[i]}`);
385
+ result.isValid = false;
386
+ }
375
387
  }
376
-
388
+
377
389
  return result;
378
390
  }
379
391
 
@@ -406,11 +418,25 @@ function validateReferrerConfig(referrerConfig) {
406
418
  break;
407
419
 
408
420
  case 'random_search':
409
- if (referrerConfig.search_terms && !Array.isArray(referrerConfig.search_terms)) {
410
- result.warnings.push('search_terms should be an array of strings');
411
- }
412
- if (referrerConfig.search_terms && referrerConfig.search_terms.length === 0) {
413
- result.warnings.push('Empty search_terms array will use default terms');
421
+ // Upgrade from warning to error: generateSearchTerm reads
422
+ // customTerms.length, which throws TypeError on a non-array
423
+ // (e.g. a single string the user expected to be auto-wrapped).
424
+ // Letting this slip past validation produces a runtime crash
425
+ // mid-scan instead of a clean config-load failure.
426
+ if (referrerConfig.search_terms !== undefined && !Array.isArray(referrerConfig.search_terms)) {
427
+ result.isValid = false;
428
+ result.errors.push(`search_terms must be an array of strings (got ${typeof referrerConfig.search_terms})`);
429
+ } else if (Array.isArray(referrerConfig.search_terms)) {
430
+ if (referrerConfig.search_terms.length === 0) {
431
+ result.warnings.push('Empty search_terms array will use default terms');
432
+ } else {
433
+ for (let i = 0; i < referrerConfig.search_terms.length; i++) {
434
+ if (typeof referrerConfig.search_terms[i] !== 'string') {
435
+ result.isValid = false;
436
+ result.errors.push(`search_terms[${i}] must be a string (got ${typeof referrerConfig.search_terms[i]})`);
437
+ }
438
+ }
439
+ }
414
440
  }
415
441
  break;
416
442
  }
@@ -464,47 +490,14 @@ function validateReferrerDisable(referrerDisable) {
464
490
  return result;
465
491
  }
466
492
 
467
- /**
468
- * Gets available referrer modes and their descriptions
469
- * @returns {Object} Object containing mode descriptions
470
- */
471
- function getReferrerModes() {
472
- return {
473
- 'random_search': 'Generate random search engine referrers with customizable search terms',
474
- 'social_media': 'Use random social media platform referrers',
475
- 'news_sites': 'Use random news website referrers',
476
- 'direct_navigation': 'No referrer (simulates direct URL entry)',
477
- 'custom': 'Use a specific custom referrer URL',
478
- 'mixed': 'Randomly mix different referrer types for varied traffic simulation'
479
- };
480
- }
481
-
482
- /**
483
- * Gets statistics about available referrer collections
484
- * @returns {Object} Statistics about referrer collections
485
- */
486
- function getReferrerStats() {
487
- return {
488
- searchEngines: REFERRER_COLLECTIONS.SEARCH_ENGINES.length,
489
- socialMedia: REFERRER_COLLECTIONS.SOCIAL_MEDIA.length,
490
- newsSites: REFERRER_COLLECTIONS.NEWS_SITES.length,
491
- defaultSearchTerms: REFERRER_COLLECTIONS.DEFAULT_SEARCH_TERMS.length,
492
- ecommerceTerms: REFERRER_COLLECTIONS.ECOMMERCE_TERMS.length,
493
- techTerms: REFERRER_COLLECTIONS.TECH_TERMS.length
494
- };
495
- }
496
-
493
+ // Public surface used by nwss.js. Internal helpers
494
+ // (generateReferrerUrl, shouldDisableReferrer, generateSearch/Social/News
495
+ // Referrer, isValidUrl, REFERRER_COLLECTIONS) stay module-private
496
+ // the old export list included nine names no caller imported, plus two
497
+ // dead helper functions (getReferrerModes, getReferrerStats) that have
498
+ // been removed entirely.
497
499
  module.exports = {
498
- generateReferrerUrl,
499
500
  getReferrerForUrl,
500
- shouldDisableReferrer,
501
501
  validateReferrerConfig,
502
- validateReferrerDisable,
503
- getReferrerModes,
504
- getReferrerStats,
505
- generateSearchReferrer,
506
- generateSocialMediaReferrer,
507
- generateNewsReferrer,
508
- isValidUrl,
509
- REFERRER_COLLECTIONS
502
+ validateReferrerDisable
510
503
  };