@fanboynz/network-scanner 3.0.2 → 3.1.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.
@@ -70,7 +70,7 @@ const TARGETS = [
70
70
  extract: async (page) => {
71
71
  return await page.evaluate(() => {
72
72
  const cells = Array.from(document.querySelectorAll('td'));
73
- const out = { passed: 0, failed: 0, warn: 0, total: 0, failures: [] };
73
+ const out = { passed: 0, failed: 0, warn: 0, total: 0, failures: [], warnings: [] };
74
74
  for (const c of cells) {
75
75
  const cls = c.className || '';
76
76
  if (cls.includes('passed')) { out.passed++; out.total++; }
@@ -81,7 +81,14 @@ const TARGETS = [
81
81
  const label = row?.querySelector('td')?.textContent?.trim() || '?';
82
82
  out.failures.push(label);
83
83
  }
84
- else if (cls.includes('warn')) { out.warn++; out.total++; }
84
+ else if (cls.includes('warn')) {
85
+ out.warn++; out.total++;
86
+ // Capture warn-row labels too so a soft regression (cell moving
87
+ // passed -> warn) is debuggable without --headful.
88
+ const row = c.closest('tr');
89
+ const label = row?.querySelector('td')?.textContent?.trim() || '?';
90
+ out.warnings.push(label);
91
+ }
85
92
  }
86
93
  return out;
87
94
  });
@@ -97,15 +104,29 @@ const TARGETS = [
97
104
  await new Promise(r => setTimeout(r, 8000)); // give async tests time
98
105
  return await page.evaluate(() => {
99
106
  const text = document.body.innerText || '';
100
- // CreepJS reports a "Trust Score" percentage and individual signal entries.
101
- const trustMatch = text.match(/Trust Score[:\s]+(\d+(?:\.\d+)?)\s*%/i);
102
- const lieMatch = text.match(/lies[:\s]+(\d+)/i);
103
- const botMatch = text.match(/bot[:\s]+(true|false)/i);
107
+ // CreepJS's actual stealth-relevant outputs are in a "Headless"
108
+ // section as percentages (e.g. "67% headless", "40% stealth",
109
+ // "44% like headless"), not the "Trust Score" label the old
110
+ // regex expected. Engine identification comes from "chromium:
111
+ // true/false" in the same block. Lower headless % and higher
112
+ // stealth % are better for evasion.
113
+ const headlessMatch = text.match(/(\d+(?:\.\d+)?)\s*%\s+headless\b/i);
114
+ const likeHeadlessMatch = text.match(/(\d+(?:\.\d+)?)\s*%\s+like\s+headless/i);
115
+ const stealthMatch = text.match(/(\d+(?:\.\d+)?)\s*%\s+stealth\b/i);
116
+ const chromiumMatch = text.match(/chromium\s*:\s*(true|false)/i);
117
+ // FP ID is CreepJS's stable fingerprint hash — same value across
118
+ // reloads if the fingerprint is unchanged; lets you A/B before
119
+ // and after a spoof change.
120
+ const fpIdMatch = text.match(/FP\s*ID\s*:?\s*([0-9a-f]{16,})/i);
104
121
  return {
105
- trustScore: trustMatch ? parseFloat(trustMatch[1]) : null,
106
- lies: lieMatch ? parseInt(lieMatch[1], 10) : null,
107
- botDetected: botMatch ? botMatch[1] === 'true' : null,
108
- excerpt: text.split('\n').slice(0, 15).join('\n').slice(0, 400)
122
+ headlessPct: headlessMatch ? parseFloat(headlessMatch[1]) : null,
123
+ likeHeadlessPct: likeHeadlessMatch ? parseFloat(likeHeadlessMatch[1]) : null,
124
+ stealthPct: stealthMatch ? parseFloat(stealthMatch[1]) : null,
125
+ isChromium: chromiumMatch ? chromiumMatch[1] === 'true' : null,
126
+ fpId: fpIdMatch ? fpIdMatch[1].slice(0, 16) : null,
127
+ // Larger excerpt (40 lines, up to 2KB) so a future UI rotation
128
+ // is debuggable from the output without --headful.
129
+ excerpt: text.split('\n').slice(0, 40).join('\n').slice(0, 2000)
109
130
  };
110
131
  });
111
132
  }
@@ -165,10 +186,15 @@ function formatResult(target, result) {
165
186
  if (result.failures.length) {
166
187
  lines.push(` failure rows: ${result.failures.slice(0, 10).join(', ')}${result.failures.length > 10 ? ` ... +${result.failures.length - 10} more` : ''}`);
167
188
  }
189
+ if (result.warnings && result.warnings.length) {
190
+ lines.push(` warn rows: ${result.warnings.slice(0, 10).join(', ')}${result.warnings.length > 10 ? ` ... +${result.warnings.length - 10} more` : ''}`);
191
+ }
168
192
  } else if (target.name === 'creepjs') {
169
- lines.push(` trust score: ${result.trustScore ?? 'n/a'}%`);
170
- lines.push(` lies detected: ${result.lies ?? 'n/a'}`);
171
- lines.push(` bot flagged: ${result.botDetected ?? 'n/a'}`);
193
+ lines.push(` FP ID: ${result.fpId ?? 'n/a'} (stable across reloads if fingerprint unchanged)`);
194
+ lines.push(` engine chromium: ${result.isChromium ?? 'n/a'}`);
195
+ lines.push(` headless score: ${result.headlessPct ?? 'n/a'}% (lower = better; 0% = real browser)`);
196
+ lines.push(` like-headless: ${result.likeHeadlessPct ?? 'n/a'}% (lower = better; soft headless signals)`);
197
+ lines.push(` stealth score: ${result.stealthPct ?? 'n/a'}% (lower = better; % likely to be using anti-detection tooling)`);
172
198
  if (result.excerpt) lines.push(` excerpt:\n ${result.excerpt.split('\n').join('\n ')}`);
173
199
  } else if (target.name === 'browserleaks') {
174
200
  for (const [k, v] of Object.entries(result)) {