lljz66 commited on
Commit
b93709a
·
verified ·
1 Parent(s): 24b528d

Update server.js

Browse files
Files changed (1) hide show
  1. server.js +197 -8
server.js CHANGED
@@ -102,7 +102,8 @@ class BrowserPool {
102
  '--disable-blink-features=AutomationControlled',
103
  '--disable-background-timer-throttling',
104
  '--disable-renderer-backgrounding'
105
- ]
 
106
  });
107
  console.log('🚀 Browser launched');
108
  this.requestCount = 1;
@@ -523,7 +524,7 @@ async function performBlacklightScan(url) {
523
  captureHar: true,
524
  saveScreenshots: false,
525
  headless: true,
526
- defaultTimeout: 25000, // 25 ثانية تكفي لأغلب المواقع
527
  extraChromiumArgs: [
528
  '--disable-blink-features=AutomationControlled',
529
  '--no-sandbox',
@@ -531,15 +532,20 @@ async function performBlacklightScan(url) {
531
  '--disable-dev-shm-usage',
532
  '--disable-gpu',
533
  '--ignore-certificate-errors'
534
- ]
 
 
 
535
  };
536
  const result = await collect(url, options);
537
  // تنظيف الملفات المؤقتة بعد الفحص
538
- const files = await readdir(tmpDir);
539
- await Promise.all(files.map(f => unlink(path.join(tmpDir, f)).catch(() => {})));
 
 
540
  return result;
541
  } catch (e) {
542
- console.error('Blacklight scan failed:', e.message);
543
  return { hosts: {}, cookies: [], error: e.message };
544
  }
545
  }
@@ -611,7 +617,7 @@ function calculatePrivacyScore(blacklight, enrichedTrackers, tosdrGrade, securit
611
  return { score, grade };
612
  }
613
 
614
- // ==================== نقطة نهاية API ====================
615
  app.get('/api/scan', async (req, res) => {
616
  const url = normalizeUrl(req.query.url);
617
  if (!url) return res.status(400).json({ error: 'Invalid URL' });
@@ -624,7 +630,6 @@ app.get('/api/scan', async (req, res) => {
624
  const startTime = Date.now();
625
 
626
  try {
627
- // تنفيذ جميع الفحوصات بالتوازي (مع حماية كل منها ضد الفشل)
628
  const [
629
  screenshotResult, hiddenStorageResult, cookieConsentResult,
630
  fingerprintResult, redirectChainResult, blacklightResult,
@@ -775,6 +780,190 @@ app.get('/api/scan', async (req, res) => {
775
  }
776
  });
777
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
778
  app.get('/health', (req, res) => res.json({ status: 'ok' }));
779
 
780
  // ==================== بدء التشغيل ====================
 
102
  '--disable-blink-features=AutomationControlled',
103
  '--disable-background-timer-throttling',
104
  '--disable-renderer-backgrounding'
105
+ ],
106
+ protocolTimeout: 240000, // 4 دقائق لتفادي مشاكل الـ timeout
107
  });
108
  console.log('🚀 Browser launched');
109
  this.requestCount = 1;
 
524
  captureHar: true,
525
  saveScreenshots: false,
526
  headless: true,
527
+ defaultTimeout: 90000, // 90 ثانية
528
  extraChromiumArgs: [
529
  '--disable-blink-features=AutomationControlled',
530
  '--no-sandbox',
 
532
  '--disable-dev-shm-usage',
533
  '--disable-gpu',
534
  '--ignore-certificate-errors'
535
+ ],
536
+ puppeteerOptions: {
537
+ protocolTimeout: 240000, // 4 دقائق
538
+ },
539
  };
540
  const result = await collect(url, options);
541
  // تنظيف الملفات المؤقتة بعد الفحص
542
+ try {
543
+ const files = await readdir(tmpDir);
544
+ await Promise.all(files.map(f => unlink(path.join(tmpDir, f)).catch(() => {})));
545
+ } catch (e) {}
546
  return result;
547
  } catch (e) {
548
+ console.error('Blacklight scan failed, returning empty result:', e.message);
549
  return { hosts: {}, cookies: [], error: e.message };
550
  }
551
  }
 
617
  return { score, grade };
618
  }
619
 
620
+ // ==================== نقطة نهاية API عادية (JSON) ====================
621
  app.get('/api/scan', async (req, res) => {
622
  const url = normalizeUrl(req.query.url);
623
  if (!url) return res.status(400).json({ error: 'Invalid URL' });
 
630
  const startTime = Date.now();
631
 
632
  try {
 
633
  const [
634
  screenshotResult, hiddenStorageResult, cookieConsentResult,
635
  fingerprintResult, redirectChainResult, blacklightResult,
 
780
  }
781
  });
782
 
783
+ // ==================== نقطة نهاية SSE (تقدم حي) ====================
784
+ app.get('/api/scan/live', async (req, res) => {
785
+ const url = normalizeUrl(req.query.url);
786
+ if (!url) {
787
+ res.status(400).send('Invalid URL');
788
+ return;
789
+ }
790
+
791
+ // إعداد رؤوس SSE
792
+ res.writeHead(200, {
793
+ 'Content-Type': 'text/event-stream',
794
+ 'Cache-Control': 'no-cache',
795
+ 'Connection': 'keep-alive',
796
+ });
797
+
798
+ const sendEvent = (event, data) => {
799
+ res.write(`event: ${event}\n`);
800
+ res.write(`data: ${JSON.stringify(data)}\n\n`);
801
+ };
802
+
803
+ sendEvent('start', { url, message: 'Scan started...' });
804
+
805
+ if (scanMutex.isLocked()) {
806
+ sendEvent('error', { error: 'Scanner is busy, please try again in a moment.' });
807
+ res.end();
808
+ return;
809
+ }
810
+
811
+ const release = await scanMutex.acquire();
812
+ const startTime = Date.now();
813
+
814
+ try {
815
+ // تنفيذ الفحوصات مع إرسال التقدم عند اكتمال كل خطوة
816
+ const promises = [
817
+ takeScreenshot(url).then(r => { sendEvent('progress', { step: 'screenshot', status: 'done' }); return r; }).catch(() => null),
818
+ checkHiddenStorage(url).then(r => { sendEvent('progress', { step: 'hidden_storage', status: 'done' }); return r; }).catch(() => null),
819
+ analyzeCookieConsent(url).then(r => { sendEvent('progress', { step: 'cookie_consent', status: 'done' }); return r; }).catch(() => null),
820
+ collectFingerprintRisks(url).then(r => { sendEvent('progress', { step: 'fingerprint_risks', status: 'done' }); return r; }).catch(() => null),
821
+ trackRedirectChain(url).then(r => { sendEvent('progress', { step: 'redirect_chain', status: 'done' }); return r; }).catch(() => null),
822
+ performBlacklightScan(url).then(r => { sendEvent('progress', { step: 'blacklight_scan', status: 'done' }); return r; }).catch(() => null),
823
+ getTosdrGrade(url).then(r => { sendEvent('progress', { step: 'tosdr', status: 'done' }); return r; }).catch(() => null),
824
+ performSecurityCheck(url).then(r => { sendEvent('progress', { step: 'security', status: 'done' }); return r; }).catch(() => null),
825
+ analyzePrivacyPolicy(url).then(r => { sendEvent('progress', { step: 'privacy_policy', status: 'done' }); return r; }).catch(() => null),
826
+ ];
827
+
828
+ const results = await Promise.allSettled(promises);
829
+
830
+ const screenshotData = results[0].status === 'fulfilled' ? results[0].value : null;
831
+ const hiddenData = results[1].status === 'fulfilled' ? results[1].value : null;
832
+ const cookieConsentData = results[2].status === 'fulfilled' ? results[2].value : null;
833
+ const fingerprintData = results[3].status === 'fulfilled' ? results[3].value : null;
834
+ const redirectData = results[4].status === 'fulfilled' ? results[4].value : null;
835
+ const blacklightData = results[5].status === 'fulfilled' ? results[5].value : { hosts: {}, cookies: [] };
836
+ const tosdrData = results[6].status === 'fulfilled' ? results[6].value : null;
837
+ const securityData = results[7].status === 'fulfilled' ? results[7].value : { ssl: { valid: false } };
838
+ const privacyPolicyData = results[8].status === 'fulfilled' ? results[8].value : null;
839
+
840
+ // تجميع المتتبعين
841
+ const thirdPartyDomains = [
842
+ ...(blacklightData.hosts?.thirdParty || []),
843
+ ...(blacklightData.hosts?.requests?.third_party || [])
844
+ ];
845
+ const uniqueDomains = [...new Set(thirdPartyDomains)];
846
+ const enrichedTrackers = [];
847
+ for (const domain of uniqueDomains) {
848
+ try {
849
+ const ddgInfo = getDDGInfo(domain);
850
+ const ghosteryInfo = await getGhosteryInfo(domain);
851
+ enrichedTrackers.push({
852
+ domain,
853
+ owner: ghosteryInfo?.organization || ddgInfo?.owner || getBaseDomain(domain),
854
+ category: ghosteryInfo?.category || ddgInfo?.category || 'unknown',
855
+ prevalence: ddgInfo?.prevalence || 0
856
+ });
857
+ } catch (e) {
858
+ enrichedTrackers.push({ domain, owner: getBaseDomain(domain), category: 'unknown', prevalence: 0 });
859
+ }
860
+ }
861
+
862
+ // Geo mapping
863
+ const uniqueIps = [...new Set((blacklightData.hosts?.requests?.third_party || []).map(r => r.ip_addr).filter(Boolean))];
864
+ const geoDestinations = [];
865
+ for (const ip of uniqueIps) {
866
+ try {
867
+ let geoData = null;
868
+ if (useMaxMind && geoReader) {
869
+ const mmGeo = geoReader.get(ip);
870
+ if (mmGeo) {
871
+ geoData = {
872
+ ip,
873
+ country: mmGeo.country?.names?.en || 'Unknown',
874
+ city: mmGeo.city?.names?.en || 'Unknown',
875
+ latitude: mmGeo.location?.latitude,
876
+ longitude: mmGeo.location?.longitude
877
+ };
878
+ }
879
+ }
880
+ if (!geoData) {
881
+ const geoLite = geoip.lookup(ip);
882
+ if (geoLite) {
883
+ geoData = {
884
+ ip,
885
+ country: geoLite.country,
886
+ city: geoLite.city,
887
+ latitude: geoLite.ll?.[0] || null,
888
+ longitude: geoLite.ll?.[1] || null
889
+ };
890
+ }
891
+ }
892
+ if (geoData) geoDestinations.push(geoData);
893
+ } catch (e) {}
894
+ }
895
+
896
+ // PII Leakage
897
+ const leakageAlerts = [];
898
+ for (const r of (blacklightData.hosts?.requests?.third_party || [])) {
899
+ if ((r.method === 'POST' || r.method === 'PUT') && r.body) {
900
+ try {
901
+ const detected = piiFilter.detect(r.body);
902
+ if (detected?.length > 0) {
903
+ leakageAlerts.push({
904
+ severity: 'high',
905
+ destination: r.url,
906
+ method: r.method,
907
+ types: detected.map(p => p.type),
908
+ message: 'Potential PII detected in request to third-party domain.'
909
+ });
910
+ }
911
+ } catch (e) {}
912
+ }
913
+ }
914
+
915
+ const { score, grade } = calculatePrivacyScore(
916
+ blacklightData, enrichedTrackers, tosdrData,
917
+ securityData, cookieConsentData, fingerprintData,
918
+ redirectData, privacyPolicyData
919
+ );
920
+
921
+ const finalResult = {
922
+ success: true,
923
+ url,
924
+ final_url: blacklightData.uri_dest || url,
925
+ scan_time_sec: (Date.now() - startTime) / 1000,
926
+ privacy_score: { score, grade },
927
+ trackers: { count: enrichedTrackers.length, list: enrichedTrackers.slice(0, 20) },
928
+ cookies: {
929
+ total: blacklightData.cookies?.length || 0,
930
+ third_party: blacklightData.cookies?.filter(c => c.thirdParty)?.length || 0
931
+ },
932
+ fingerprinting: {
933
+ canvas: !!(blacklightData.canvasFingerprinters?.length),
934
+ fonts: !!(blacklightData.canvasFontFingerprinters?.length),
935
+ live_risks: fingerprintData
936
+ },
937
+ session_recording: !!(blacklightData.sessionRecorders?.length),
938
+ key_logging: !!(blacklightData.keyLogging?.length),
939
+ hidden_storage: hiddenData ? {
940
+ localStorage: hiddenData.localStorage?.length || 0,
941
+ sessionStorage: hiddenData.sessionStorage?.length || 0,
942
+ indexedDB: hiddenData.indexedDB
943
+ } : null,
944
+ cookie_consent: cookieConsentData,
945
+ redirect_chain: redirectData,
946
+ security: securityData,
947
+ tosdr: tosdrData,
948
+ privacy_policy_analysis: privacyPolicyData,
949
+ geo_mapping: { data_destinations: geoDestinations },
950
+ leakage_detection: { alerts: leakageAlerts },
951
+ screenshot: screenshotData,
952
+ raw: blacklightData
953
+ };
954
+
955
+ sendEvent('result', finalResult);
956
+ sendEvent('end', { message: 'Scan completed.' });
957
+
958
+ } catch (e) {
959
+ console.error('Live scan error:', e);
960
+ sendEvent('error', { error: e.message });
961
+ } finally {
962
+ release();
963
+ res.end();
964
+ }
965
+ });
966
+
967
  app.get('/health', (req, res) => res.json({ status: 'ok' }));
968
 
969
  // ==================== بدء التشغيل ====================