@ianmenethil/zp-devicefp 0.1.1 → 0.2.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.
@@ -1,40 +1,171 @@
1
- # Architecture
2
-
3
- ## High-level flow
4
-
5
- 1. Resolve the active signal set from `CollectOptions`.
6
- 2. Run each collector with per-signal timing and timeout handling.
7
- 3. Canonicalize each successful value into stable JSON.
8
- 4. Hash each component with SHA-256.
9
- 5. Derive a composite thumbprint from the ordered component hashes.
10
- 6. Generate an anti-spoof report from cross-signal consistency checks.
11
- 7. Compute a local confidence score from signal quality and anomaly penalties.
12
-
13
- ## Module map
14
-
15
- - `src/client.ts`: public client factory and orchestration
16
- - `src/signals/*.ts`: one module per signal collector
17
- - `src/core/canonicalize.ts`: deterministic serialization
18
- - `src/core/hash.ts`: zero-dependency SHA-256 implementation
19
- - `src/core/antiSpoof.ts`: anomaly and automation heuristics
20
- - `src/core/confidence.ts`: local signal-quality scoring
21
- - `src/core/upload.ts`: optional backend-neutral upload helper
22
- - `src/runtime/browser.ts`: runtime feature access
23
-
24
- ## Signal tiers
25
-
26
- The package splits collectors into:
27
-
28
- - Core: safe defaults that avoid permission prompts and still provide broad browser/device context.
29
- - Extended: slower or more privacy-sensitive capability probes such as permissions, media-device counts, and WebRTC codec signatures.
30
-
31
- ## Anti-spoof strategy
32
-
33
- The library does not use “secret” client tricks. It scores coherence:
34
-
35
- - user-agent vs platform contradictions
36
- - headless or webdriver hints
37
- - mobile user-agent with implausibly desktop geometry
38
- - obviously implausible hardware values
39
-
40
- This report is meant to enrich downstream server-side risk logic, not replace it.
1
+ # Architecture
2
+
3
+ ## Module structure
4
+
5
+ ```mermaid
6
+ graph TD
7
+ subgraph "Public API"
8
+ index["src/index.ts<br/>Barrel: createFingerprintClient,<br/>LIBRARY_VERSION, SCHEMA_VERSION"]
9
+ cdn["src/cdn.ts<br/>IIFE entry: attaches<br/>window.DeviceFP"]
10
+ end
11
+
12
+ subgraph "Client Orchestration"
13
+ client["src/client.ts<br/>Factory + collect / collectSync / on"]
14
+ end
15
+
16
+ subgraph "Core Modules"
17
+ options["core/options.ts<br/>Default resolution,<br/>signal set filtering"]
18
+ canonicalize["core/canonicalize.ts<br/>Deterministic JSON<br/>for hashing"]
19
+ hash["core/hash.ts<br/>Zero-dependency SHA-256"]
20
+ antiSpoof["core/antiSpoof.ts<br/>Cross-signal coherence<br/>6 anomaly + 2 automation checks"]
21
+ confidence["core/confidence.ts<br/>Weighted signal scoring,<br/>anti-spoof blend (65/35)"]
22
+ events["core/events.ts<br/>Typed FingerprintEmitter<br/>(progress/warning/complete)"]
23
+ end
24
+
25
+ subgraph "Runtime"
26
+ browser["runtime/browser.ts<br/>getBrowserEnv, now,<br/>hasDom, hasCanvas"]
27
+ end
28
+
29
+ subgraph "Signal Collectors (21)"
30
+ signalsIdx["signals/index.ts<br/>signalCollectors array +<br/>collectorMap (Map)"]
31
+ coreSigs["Core (10): ua, uaHints, locale,<br/>screen, hardware, storage, fonts,<br/>canvas, webgl, audio"]
32
+ extSigs["Extended (11): mediaDevices,<br/>permissions, webrtc, frameInfo,<br/>networkInfo, paymentSupport,<br/>referrerInfo, navigationInfo,<br/>riskSignals, adblock, geolocation"]
33
+ end
34
+
35
+ subgraph "Types"
36
+ publicT["types/public.ts<br/>All public interfaces:<br/>FingerprintResult, CollectOptions, etc."]
37
+ internalT["types/internal.ts<br/>BrowserEnv, SignalCollector,<br/>CollectorContext"]
38
+ end
39
+
40
+ subgraph "Constants"
41
+ consts["constants.ts<br/>CORE_SIGNALS, EXTENDED_SIGNALS,<br/>SYNC_SIGNALS, DEFAULT_TIMEOUT_MS,<br/>DEFAULT_FONT_LIST, etc."]
42
+ end
43
+
44
+ index --> client
45
+ cdn --> index
46
+ client --> options
47
+ client --> canonicalize
48
+ client --> hash
49
+ client --> antiSpoof
50
+ client --> confidence
51
+ client --> events
52
+ client --> browser
53
+ client --> signalsIdx
54
+ client --> consts
55
+ signalsIdx --> coreSigs
56
+ signalsIdx --> extSigs
57
+ signalsIdx --> internalT
58
+ coreSigs --> browser
59
+ extSigs --> browser
60
+ ```
61
+
62
+ ## Main runtime flow (async `collect()`)
63
+
64
+ ```mermaid
65
+ sequenceDiagram
66
+ participant User
67
+ participant Client
68
+ participant Options
69
+ participant Signals
70
+ participant Core
71
+ participant Emitter
72
+
73
+ User->>Client: collect(options?)
74
+ Client->>Options: withDefaultOptions(options)
75
+ Options-->>Client: resolved options
76
+ Client->>Options: resolveSignalSet(resolved, 'async')
77
+ Options-->>Client: signalNames[]
78
+ Client->>Client: getBrowserEnv()
79
+
80
+ par Parallel signal collection
81
+ Client->>Signals: collector.collect(ctx) + timeout race
82
+ Signals-->>Client: SignalResult (per signal)
83
+ Client->>Emitter: emit('progress', { completed, total, signal, result })
84
+ end
85
+
86
+ loop For each ok signal
87
+ Client->>Core: canonicalizeToString(value)
88
+ Core-->>Client: deterministic JSON
89
+ Client->>Core: sha256Hex(json)
90
+ Core-->>Client: component hash
91
+ end
92
+
93
+ Client->>Core: sha256Hex(sorted component hashes)
94
+ Core-->>Client: thumbprint
95
+
96
+ Client->>Core: analyseSignals(signals)
97
+ Core-->>Client: AntiSpoofReport
98
+
99
+ Client->>Core: computeConfidence(signals, antiSpoof)
100
+ Core-->>Client: confidence score
101
+
102
+ Client->>Emitter: emit('complete', { result })
103
+ Client-->>User: FingerprintResult
104
+ ```
105
+
106
+ ## Data flow: signal → hash → thumbprint
107
+
108
+ ```mermaid
109
+ flowchart LR
110
+ A["Signal value<br/>(raw JS object)"] --> B["canonicalizeToString()<br/>sorted keys, 6-decimal numbers,<br/>ISO dates, undefined → 'undefined'"]
111
+ B --> C["sha256Hex()<br/>64-char hex digest"]
112
+ C --> D["componentHashes<br/>Partial&lt;Record&lt;SignalName, string&gt;&gt;"]
113
+ D --> E["Sort by signal name,<br/>serialize as [[name, hash], ...]"]
114
+ E --> F["sha256Hex()"]
115
+ F --> G["thumbprint<br/>(composite 64-char hex)"]
116
+ ```
117
+
118
+ ## Signal tiers
119
+
120
+ The library splits collectors into two tiers:
121
+
122
+ | Tier | Count | Signals | Description |
123
+ |------|-------|---------|-------------|
124
+ | **Core** | 10 | `ua`, `uaHints`, `locale`, `screen`, `hardware`, `storage`, `fonts`, `canvas`, `webgl`, `audio` | Safe defaults — no permission prompts, broad browser/device context |
125
+ | **Extended** | 11 | `mediaDevices`, `permissions`, `webrtc`, `frameInfo`, `networkInfo`, `paymentSupport`, `referrerInfo`, `navigationInfo`, `riskSignals`, `adblock`, `geolocation` | Slower or more privacy-sensitive probes — requires `extended: true` |
126
+
127
+ ## Sync vs async support
128
+
129
+ | Type | Count | Signals |
130
+ |------|-------|---------|
131
+ | **Sync-capable** | 13 | `ua`, `locale`, `screen`, `hardware`, `storage`, `fonts`, `canvas`, `webgl`, `frameInfo`, `networkInfo`, `paymentSupport`, `referrerInfo`, `navigationInfo` |
132
+ | **Async-only** | 8 | `uaHints`, `audio`, `mediaDevices`, `permissions`, `webrtc`, `riskSignals`, `adblock`, `geolocation` |
133
+
134
+ When `collectSync()` is called, only the 13 sync-capable signals run. The 8 async-only signals are silently skipped (with a warning emitted).
135
+
136
+ ## Confidence scoring
137
+
138
+ ```mermaid
139
+ flowchart TD
140
+ subgraph "Per-signal points"
141
+ W["Signal weights (0.2–1.0)"] --> E
142
+ S["Signal status"] --> E["Earn = weight × multiplier"]
143
+ E --> E2["ok → 1.0× | unsupported/blocked → 0.35×<br/>timeout → 0.15× | error → 0×"]
144
+ end
145
+
146
+ E2 --> BASE["base = earned / available"]
147
+
148
+ subgraph "Anti-spoof blend"
149
+ AS["antiSpoof.score<br/>(0 = bot, 1 = genuine)"] --> BLEND
150
+ BASE --> BLEND["final = base × (0.65 + antiSpoof × 0.35)"]
151
+ end
152
+
153
+ BLEND --> FINAL["confidence<br/>(0–1, 3 decimal places)"]
154
+ ```
155
+
156
+ ## Anti-spoof strategy
157
+
158
+ The library does not use "secret" client tricks. It scores coherence through cross-signal consistency checks:
159
+
160
+ - **Anomalies (6 checks, -0.12 each):** platform mismatches between UA and UA-CH, touch claims without touch points, mobile UA with desktop screen, Windows UA with macOS platform, implausible device memory, tiny screen geometry.
161
+ - **Automation hints (2 checks, -0.18 each):** `navigator.webdriver` flag, headless user-agent string.
162
+
163
+ This report is meant to enrich downstream server-side risk logic, not replace it.
164
+
165
+ ## Key design decisions
166
+
167
+ - **Async first** — high-value collectors (audio, UA hints, media devices, permissions, WebRTC, risk signals) run async.
168
+ - **Explainable output** — every signal includes a status and duration. No silent failures.
169
+ - **No dangerous defaults** — no battery, sensors, arbitrary installed-app probes, or local IP harvesting.
170
+ - **Anti-spoofing** focuses on coherence checks and automation hints, not "secret" client tricks.
171
+ - **Zero runtime dependencies** — no CDN loads, no npm deps at runtime. SHA-256 is implemented in-house.
package/docs/privacy.md CHANGED
@@ -1,20 +1,20 @@
1
- # Privacy Notes
2
-
3
- This implementation follows the brief's privacy-by-design direction:
4
-
5
- - The default collector avoids permission prompts.
6
- - It does not attempt raw local IP discovery.
7
- - It does not probe battery state, arbitrary sensors, or installed apps.
8
- - Media devices are summarized by counts and kinds only.
9
- - WebRTC collection uses capability signatures derived from SDP, not network-address harvesting.
10
-
11
- ## Recommended operational posture
12
-
13
- - Treat the output as pseudonymous device intelligence, not anonymous data.
14
- - Retain raw signal payloads for a shorter window than derived hashes or risk scores.
15
- - Scope identifiers per product or tenant if cross-context correlation is not required.
16
- - Provide notice and an account- or tenant-level suppression path where appropriate.
17
-
18
- ## Jurisdiction reminder
19
-
20
- The code can support legitimate security and fraud-prevention use cases, but the legal position still depends on purpose, notice, retention, and jurisdiction. The bundle is intentionally structured so upload is optional and backend handling can enforce local policy.
1
+ # Privacy Notes
2
+
3
+ This implementation follows the brief's privacy-by-design direction:
4
+
5
+ - The default collector avoids permission prompts.
6
+ - It does not attempt raw local IP discovery.
7
+ - It does not probe battery state, arbitrary sensors, or installed apps.
8
+ - Media devices are summarized by counts and kinds only.
9
+ - WebRTC collection uses capability signatures derived from SDP, not network-address harvesting.
10
+
11
+ ## Recommended operational posture
12
+
13
+ - Treat the output as pseudonymous device intelligence, not anonymous data.
14
+ - Retain raw signal payloads for a shorter window than derived hashes or risk scores.
15
+ - Scope identifiers per product or tenant if cross-context correlation is not required.
16
+ - Provide notice and an account- or tenant-level suppression path where appropriate.
17
+
18
+ ## Jurisdiction reminder
19
+
20
+ The code can support legitimate security and fraud-prevention use cases, but the legal position still depends on purpose, notice, retention, and jurisdiction. Backend handling can enforce local policy on data retention and processing.