@npm-pipl/device-intelligence 1.1.1 → 1.1.3
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/dist/device-intelligence.cjs.js +144 -50
- package/dist/device-intelligence.cjs.js.map +1 -1
- package/dist/device-intelligence.esm.js +144 -50
- package/dist/device-intelligence.esm.js.map +1 -1
- package/dist/device-intelligence.esm.min.js +2 -2
- package/dist/device-intelligence.esm.min.js.map +1 -1
- package/dist/device-intelligence.js +144 -50
- package/dist/device-intelligence.js.map +1 -1
- package/dist/device-intelligence.min.js +2 -2
- package/dist/device-intelligence.min.js.map +1 -1
- package/dist/device-intelligence.umd.js +144 -50
- package/dist/device-intelligence.umd.js.map +1 -1
- package/dist/device-intelligence.umd.min.js +2 -2
- package/dist/device-intelligence.umd.min.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Device Intelligence SDK v1.1.
|
|
2
|
+
* Device Intelligence SDK v1.1.3
|
|
3
3
|
* (c) 2026 Your Company
|
|
4
4
|
* Released under the MIT License
|
|
5
5
|
*/
|
|
@@ -5521,7 +5521,19 @@ ZwIDAQAB
|
|
|
5521
5521
|
// Check webdriver property
|
|
5522
5522
|
if (nav.webdriver === true)
|
|
5523
5523
|
return true;
|
|
5524
|
-
//
|
|
5524
|
+
// Detect navigator.webdriver tampering (stealth plugins redefine the getter)
|
|
5525
|
+
try {
|
|
5526
|
+
const desc = Object.getOwnPropertyDescriptor(Navigator.prototype, "webdriver");
|
|
5527
|
+
if (desc?.get) {
|
|
5528
|
+
const src = Function.prototype.toString.call(desc.get);
|
|
5529
|
+
if (!/\[native code\]/.test(src))
|
|
5530
|
+
return true;
|
|
5531
|
+
}
|
|
5532
|
+
}
|
|
5533
|
+
catch {
|
|
5534
|
+
/* ignore */
|
|
5535
|
+
}
|
|
5536
|
+
// Check for automation-related properties on window / document
|
|
5525
5537
|
const automationProps = [
|
|
5526
5538
|
"__selenium_unwrapped",
|
|
5527
5539
|
"__webdriver_evaluate",
|
|
@@ -5540,12 +5552,22 @@ ZwIDAQAB
|
|
|
5540
5552
|
"$chrome_asyncScriptInfo",
|
|
5541
5553
|
"__nightmare",
|
|
5542
5554
|
"__puppeteer_evaluation_script__",
|
|
5543
|
-
"__playwright_evaluation_script__"
|
|
5555
|
+
"__playwright_evaluation_script__",
|
|
5556
|
+
"__playwright",
|
|
5557
|
+
"__pw_manual"
|
|
5544
5558
|
];
|
|
5545
5559
|
for (const prop of automationProps) {
|
|
5546
5560
|
if (prop in win || prop in document)
|
|
5547
5561
|
return true;
|
|
5548
5562
|
}
|
|
5563
|
+
// Dynamic $cdc_ scan — ChromeDriver injects keys with varying suffixes
|
|
5564
|
+
try {
|
|
5565
|
+
if (Object.keys(win).some((k) => k.startsWith("$cdc_")))
|
|
5566
|
+
return true;
|
|
5567
|
+
}
|
|
5568
|
+
catch {
|
|
5569
|
+
/* ignore */
|
|
5570
|
+
}
|
|
5549
5571
|
// Check for PhantomJS
|
|
5550
5572
|
if ("callPhantom" in win || "_phantom" in win || "phantom" in win) {
|
|
5551
5573
|
return true;
|
|
@@ -5555,8 +5577,44 @@ ZwIDAQAB
|
|
|
5555
5577
|
if (ua.includes("headless") || ua.includes("phantomjs")) {
|
|
5556
5578
|
return true;
|
|
5557
5579
|
}
|
|
5558
|
-
//
|
|
5559
|
-
|
|
5580
|
+
// WebGL renderer — headless Chromium uses SwiftShader (software renderer)
|
|
5581
|
+
try {
|
|
5582
|
+
const canvas = document.createElement("canvas");
|
|
5583
|
+
const gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
|
|
5584
|
+
if (gl) {
|
|
5585
|
+
const dbg = gl.getExtension("WEBGL_debug_renderer_info");
|
|
5586
|
+
if (dbg) {
|
|
5587
|
+
const renderer = gl.getParameter(dbg.UNMASKED_RENDERER_WEBGL);
|
|
5588
|
+
if (typeof renderer === "string" && /swiftshader/i.test(renderer)) {
|
|
5589
|
+
return true;
|
|
5590
|
+
}
|
|
5591
|
+
}
|
|
5592
|
+
}
|
|
5593
|
+
}
|
|
5594
|
+
catch {
|
|
5595
|
+
/* ignore */
|
|
5596
|
+
}
|
|
5597
|
+
// Plugins — headless browsers report 0 plugins; real Chrome always has >=1
|
|
5598
|
+
try {
|
|
5599
|
+
if (nav.plugins && nav.plugins.length === 0 && !ua.includes("firefox")) {
|
|
5600
|
+
return true;
|
|
5601
|
+
}
|
|
5602
|
+
}
|
|
5603
|
+
catch {
|
|
5604
|
+
/* ignore */
|
|
5605
|
+
}
|
|
5606
|
+
// Chrome object inconsistency — Chrome UA but missing window.chrome
|
|
5607
|
+
try {
|
|
5608
|
+
if (ua.includes("chrome") && !("chrome" in win)) {
|
|
5609
|
+
return true;
|
|
5610
|
+
}
|
|
5611
|
+
if (win.chrome && !win.chrome.runtime && !win.chrome.csi) {
|
|
5612
|
+
return true;
|
|
5613
|
+
}
|
|
5614
|
+
}
|
|
5615
|
+
catch {
|
|
5616
|
+
/* ignore */
|
|
5617
|
+
}
|
|
5560
5618
|
return false;
|
|
5561
5619
|
}
|
|
5562
5620
|
};
|
|
@@ -5718,67 +5776,103 @@ ZwIDAQAB
|
|
|
5718
5776
|
const incognitoMode = {
|
|
5719
5777
|
category: "risk",
|
|
5720
5778
|
key: "incognito_mode",
|
|
5721
|
-
version:
|
|
5779
|
+
version: 2,
|
|
5722
5780
|
cacheTtlMs: 300000,
|
|
5723
|
-
timeoutMs:
|
|
5781
|
+
timeoutMs: 3000,
|
|
5724
5782
|
collect: async () => {
|
|
5725
|
-
|
|
5783
|
+
const ua = (navigator.userAgent || "").toLowerCase();
|
|
5784
|
+
const isChromium = ua.includes("chrome") || ua.includes("chromium");
|
|
5785
|
+
const isFirefox = ua.includes("firefox");
|
|
5786
|
+
// ── Storage quota ──────────────────────────────────────────────────
|
|
5787
|
+
// Normal mode: quota ≈ 50–60 % of disk (tens to hundreds of GB).
|
|
5788
|
+
// Incognito: quota is RAM-based and much smaller.
|
|
5789
|
+
// • Safari / Firefox private: typically < 200 MB
|
|
5790
|
+
// • Chrome incognito: scales with device RAM (1–16 GB)
|
|
5726
5791
|
try {
|
|
5727
|
-
const
|
|
5728
|
-
if (
|
|
5729
|
-
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
5792
|
+
const est = await withTimeout(navigator.storage.estimate(), 1500);
|
|
5793
|
+
if (est && est.quota !== undefined) {
|
|
5794
|
+
const quota = est.quota;
|
|
5795
|
+
// Tier 1: very small quota — catches Safari & Firefox private
|
|
5796
|
+
if (quota < 200000000) {
|
|
5797
|
+
return {
|
|
5798
|
+
is_private: true,
|
|
5799
|
+
confidence: "high",
|
|
5800
|
+
method: "storage_quota"
|
|
5801
|
+
};
|
|
5802
|
+
}
|
|
5803
|
+
if (isChromium) {
|
|
5804
|
+
// Tier 2: compare quota to device memory (most reliable for Chrome).
|
|
5805
|
+
// In incognito, quota is RAM-based → quota ≤ ~RAM.
|
|
5806
|
+
// In normal mode, quota is disk-based → quota >> RAM.
|
|
5807
|
+
// navigator.deviceMemory is capped at 8 (GB) and coarsened,
|
|
5808
|
+
// so we use a 3× multiplier to cover the gap between the
|
|
5809
|
+
// capped value and real RAM. Even on a 64 GB RAM machine
|
|
5810
|
+
// (deviceMemory=8), incognito quota (~16 GB) < 3×8 GB = 24 GB,
|
|
5811
|
+
// while normal quota on a 128 GB SSD (~64 GB) > 24 GB.
|
|
5812
|
+
const deviceMemGB = navigator.deviceMemory;
|
|
5813
|
+
if (deviceMemGB && quota < deviceMemGB * 3 * 1024 * 1024 * 1024) {
|
|
5814
|
+
return {
|
|
5815
|
+
is_private: true,
|
|
5816
|
+
confidence: "medium",
|
|
5817
|
+
method: "storage_quota_vs_memory"
|
|
5818
|
+
};
|
|
5819
|
+
}
|
|
5820
|
+
// Tier 3: absolute fallback when deviceMemory is unavailable.
|
|
5821
|
+
// 16 GB catches incognito on most machines (up to ~32 GB RAM)
|
|
5822
|
+
// while staying below normal-mode quota on any disk ≥ 64 GB.
|
|
5823
|
+
if (!deviceMemGB && quota < 16000000000) {
|
|
5824
|
+
return {
|
|
5825
|
+
is_private: true,
|
|
5826
|
+
confidence: "medium",
|
|
5827
|
+
method: "storage_quota_chrome"
|
|
5828
|
+
};
|
|
5829
|
+
}
|
|
5830
|
+
}
|
|
5736
5831
|
}
|
|
5737
5832
|
}
|
|
5738
5833
|
catch {
|
|
5739
|
-
//
|
|
5834
|
+
// API not supported
|
|
5740
5835
|
}
|
|
5741
|
-
//
|
|
5836
|
+
// ── webkitTemporaryStorage (Chromium legacy) ───────────────────────
|
|
5837
|
+
// Still present in some Chromium builds; incognito returns a small
|
|
5838
|
+
// quota (often ≈ 120 MB) vs normal (multiple GB).
|
|
5742
5839
|
try {
|
|
5743
|
-
const
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
|
|
5840
|
+
const tmpStorage = navigator.webkitTemporaryStorage;
|
|
5841
|
+
if (tmpStorage?.queryUsageAndQuota) {
|
|
5842
|
+
const quota = await withTimeout(new Promise((resolve, reject) => {
|
|
5843
|
+
tmpStorage.queryUsageAndQuota((_usage, q) => resolve(q), () => reject(new Error("quota query failed")));
|
|
5844
|
+
}), 1000);
|
|
5845
|
+
if (quota !== null && quota < 200000000) {
|
|
5846
|
+
return {
|
|
5847
|
+
is_private: true,
|
|
5848
|
+
confidence: "medium",
|
|
5849
|
+
method: "webkit_temporary_storage"
|
|
5850
|
+
};
|
|
5851
|
+
}
|
|
5754
5852
|
}
|
|
5755
5853
|
}
|
|
5756
5854
|
catch {
|
|
5757
|
-
|
|
5758
|
-
is_private: true,
|
|
5759
|
-
confidence: "medium",
|
|
5760
|
-
method: "indexeddb"
|
|
5761
|
-
};
|
|
5855
|
+
// Not available
|
|
5762
5856
|
}
|
|
5763
|
-
//
|
|
5857
|
+
// ── IndexedDB ────────────────────────────────────────────────────────
|
|
5858
|
+
// Firefox private mode rejects indexedDB.open(). Some Chrome/Edge
|
|
5859
|
+
// incognito edge-cases (enterprise policies, older builds) also fail
|
|
5860
|
+
// here, so we keep this as a universal late fallback.
|
|
5764
5861
|
try {
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
// Timeout - can't determine
|
|
5774
|
-
}
|
|
5775
|
-
}
|
|
5862
|
+
await withTimeout(new Promise((resolve, reject) => {
|
|
5863
|
+
const req = indexedDB.open("_di_priv_check");
|
|
5864
|
+
req.onerror = () => reject(new Error("blocked"));
|
|
5865
|
+
req.onsuccess = () => {
|
|
5866
|
+
req.result.close();
|
|
5867
|
+
resolve(true);
|
|
5868
|
+
};
|
|
5869
|
+
}), 1000);
|
|
5776
5870
|
}
|
|
5777
5871
|
catch {
|
|
5778
5872
|
return {
|
|
5779
5873
|
is_private: true,
|
|
5780
|
-
confidence: "medium",
|
|
5781
|
-
method: "
|
|
5874
|
+
confidence: isFirefox ? "medium" : "low",
|
|
5875
|
+
method: "indexeddb"
|
|
5782
5876
|
};
|
|
5783
5877
|
}
|
|
5784
5878
|
return {
|
|
@@ -5932,7 +6026,7 @@ ZwIDAQAB
|
|
|
5932
6026
|
return getAllCollectors();
|
|
5933
6027
|
}
|
|
5934
6028
|
|
|
5935
|
-
var version = "1.1.
|
|
6029
|
+
var version = "1.1.3";
|
|
5936
6030
|
var packageJson = {
|
|
5937
6031
|
version: version};
|
|
5938
6032
|
|