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