@svrnsec/pulse 0.8.0 → 0.9.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.
package/SECURITY.md CHANGED
@@ -1,86 +1,91 @@
1
- # Security Policy
2
-
3
- ## Overview
4
-
5
- `@svrnsec/pulse` is a hardware-physics fingerprinting library used as a security layer.
6
- We take vulnerabilities seriously and will respond promptly.
7
-
8
- ## Supported Versions
9
-
10
- | Version | Supported |
11
- | ------- | ------------------ |
12
- | 0.1.x | Current release |
13
- | < 0.1 | No longer supported |
14
-
15
- ## Threat Model
16
-
17
- **What pulse protects against:**
18
- - Automated bots running in cloud VMs / Docker containers with no real hardware
19
- - Headless browser automation (Puppeteer, Playwright) on virtual machines
20
- - Credential-stuffing and account-takeover attacks from datacenter IP ranges
21
-
22
- **What pulse does NOT claim to protect against:**
23
- - A determined human attacker on real consumer hardware
24
- - A physical device farm (phones/laptops in a room)
25
- - Kernel-level hooks that spoof `performance.now()` at nanosecond precision
26
- - Server-side replay attacks when `checkNonce` is not wired (always wire it)
27
-
28
- ## Reporting a Vulnerability
29
-
30
- **Please do NOT open a public GitHub issue for security vulnerabilities.**
31
-
32
- Report security issues via email:
33
-
34
- > **security@sovereign.dev** *(or open a private GitHub Security Advisory)*
35
-
36
- ### What to include
37
-
38
- 1. A description of the vulnerability and the expected vs. actual behavior
39
- 2. Steps to reproduce (PoC code, scripts, or screenshots)
40
- 3. The impact what can an attacker achieve?
41
- 4. Any suggested mitigation or fix
42
-
43
- ### Response SLA
44
-
45
- | Severity | Initial response | Target fix |
46
- |----------|-----------------|------------|
47
- | Critical | 24 hours | 7 days |
48
- | High | 48 hours | 14 days |
49
- | Medium | 5 business days | 30 days |
50
- | Low | 10 business days| Next minor |
51
-
52
- We follow [coordinated disclosure](https://en.wikipedia.org/wiki/Coordinated_vulnerability_disclosure).
53
- You will receive credit in the changelog unless you prefer to remain anonymous.
54
-
55
- ## Cryptographic Primitives
56
-
57
- - **Hashing**: BLAKE3 via `@noble/hashes` — audited, constant-time implementation
58
- - **Nonce generation**: `crypto.getRandomValues()` / Node.js `webcrypto` — 256 bits of entropy
59
- - **Webhook signatures**: HMAC-SHA256 standard authenticated integrity check
60
-
61
- ## Known Limitations & Design Decisions
62
-
63
- ### Score, not binary gate
64
- The jitter score is a continuous value `[0, 1]`. Applications must choose their own
65
- threshold (`minJitterScore`). A score of `0.55` (default) is conservative; financial
66
- applications may want `0.70+`.
67
-
68
- ### No raw data leaves the browser
69
- The server receives only a ~1.6 KB statistical summary (means, variances, percentiles).
70
- Raw timing arrays and mouse coordinates stay on device. This is intentional — it
71
- limits what a compromised server can learn about the client.
72
-
73
- ### Registry is additive
74
- The VM classification registry (which vendor a VM is from) is separate from detection.
75
- A VM can be detected by physics even if its vendor is not in the registry.
76
-
77
- ## Secure Deployment Checklist
78
-
79
- - [ ] Set `NODE_ENV=production` to disable verbose error messages
80
- - [ ] Wire `checkNonce` to a Redis `SET NX` with TTL to prevent replay attacks
81
- - [ ] Set `PULSE_WEBHOOK_SECRET` to a cryptographically random 32+ character string
82
- - [ ] Put the API server behind TLS (nginx / Caddy / ALB)
83
- - [ ] Set `PULSE_CORS_ORIGINS` to your exact domain not `*`
84
- - [ ] Set `minJitterScore` 0.65 for high-value endpoints
85
- - [ ] Monitor `riskFlags` in webhook payloads for anomaly detection
86
- - [ ] Rotate `PULSE_API_KEYS` regularly; use different keys per environment
1
+ # Security Policy
2
+
3
+ ## Overview
4
+
5
+ `@svrnsec/pulse` is a hardware-physics fingerprinting library used as a security layer.
6
+ We take vulnerabilities seriously and will respond promptly.
7
+
8
+ ## Supported Versions
9
+
10
+ | Version | Supported |
11
+ | ------- | ------------------ |
12
+ | 0.8.x | :white_check_mark: Security fixes + active development |
13
+ | < 0.8 | :x: Deprecated critical security vulnerabilities. Upgrade immediately. |
14
+
15
+ ## Threat Model
16
+
17
+ **What pulse protects against:**
18
+ - Automated bots running in cloud VMs / Docker containers with no real hardware
19
+ - Headless browser automation (Puppeteer, Playwright) on virtual machines
20
+ - Credential-stuffing and account-takeover attacks from datacenter IP ranges
21
+ - Click farms (via proof-of-idle thermal cooling analysis)
22
+ - LLM-controlled browser agents (via behavioral biometrics)
23
+ - Coordinated inauthentic behavior (via population-level statistical analysis)
24
+ - Proof replay attacks (via HMAC-signed challenges + atomic nonce consumption)
25
+ - Payload tampering (via BLAKE3 commitment integrity)
26
+
27
+ **What pulse does NOT claim to protect against:**
28
+ - A determined human attacker on real consumer hardware
29
+ - A physical device farm where each device genuinely cools between interactions
30
+ - Kernel-level hooks that spoof `performance.now()` at nanosecond precision
31
+ - Server-side replay attacks when `checkNonce` is not wired (always wire it)
32
+ - GPU passthrough VMs with native hardware clock access
33
+
34
+ ## Reporting a Vulnerability
35
+
36
+ **Please do NOT open a public GitHub issue for security vulnerabilities.**
37
+
38
+ Report security issues via email:
39
+
40
+ > **security@sovereign.dev** *(or open a private GitHub Security Advisory)*
41
+
42
+ ### What to include
43
+
44
+ 1. A description of the vulnerability and the expected vs. actual behavior
45
+ 2. Steps to reproduce (PoC code, scripts, or screenshots)
46
+ 3. The impact — what can an attacker achieve?
47
+ 4. Any suggested mitigation or fix
48
+
49
+ ### Response SLA
50
+
51
+ | Severity | Initial response | Target fix |
52
+ |----------|-----------------|------------|
53
+ | Critical | 24 hours | 7 days |
54
+ | High | 48 hours | 14 days |
55
+ | Medium | 5 business days | 30 days |
56
+ | Low | 10 business days| Next minor |
57
+
58
+ We follow [coordinated disclosure](https://en.wikipedia.org/wiki/Coordinated_vulnerability_disclosure).
59
+ You will receive credit in the changelog unless you prefer to remain anonymous.
60
+
61
+ ## Cryptographic Primitives
62
+
63
+ - **Hashing**: BLAKE3 via `@noble/hashes` — audited, constant-time implementation
64
+ - **Challenge signing**: HMAC-SHA256 over `nonce|issuedAt|expiresAt` with timing-safe comparison
65
+ - **Engagement tokens**: HMAC-SHA256 over fraud-relevant fields with 30-second TTL
66
+ - **Nonce generation**: `crypto.getRandomValues()` / Node.js `webcrypto` — 256 bits of entropy
67
+ - **Webhook signatures**: HMAC-SHA256 — standard authenticated integrity check
68
+ - **API key comparison**: `crypto.timingSafeEqual` constant-time to prevent timing attacks
69
+
70
+ ## Privacy
71
+
72
+ - Raw timing arrays never leave the device — server receives only a ~1.6 KB statistical summary
73
+ - Mouse coordinates are never stored — only timing deltas between events
74
+ - Keystrokes capture only dwell/flight times key labels are discarded immediately
75
+ - `hardwareId()` is a 128-bit BLAKE3 hash stable per device, not reversible, not cross-origin linkable
76
+ - No IP addresses are logged by default — integrators should implement their own IP handling policy
77
+
78
+ ## Secure Deployment Checklist
79
+
80
+ - [ ] Set `NODE_ENV=production` to enforce secret validation at startup
81
+ - [ ] Set `PULSE_CHALLENGE_SECRET` to a cryptographically random 64-char hex string
82
+ - [ ] Set `WEBHOOK_SECRET` to a separate cryptographically random string
83
+ - [ ] Wire `checkNonce` to Redis `DEL` (returns 1 on first use) for atomic replay prevention
84
+ - [ ] Set `REDIS_URL` for multi-instance deployments (in-memory store is single-instance only)
85
+ - [ ] Put the API server behind TLS (nginx / Caddy / ALB)
86
+ - [ ] Set `CORS_ORIGINS` to your exact domain not `*`
87
+ - [ ] Set `minJitterScore` >= 0.65 for high-value endpoints
88
+ - [ ] Monitor `riskFlags` in webhook payloads for anomaly detection
89
+ - [ ] Use `/health/ready` (not `/health`) for load balancer health checks
90
+ - [ ] Rotate `PULSE_API_KEYS` regularly; use different keys per environment
91
+ - [ ] Review `webhook.dead_letter` log events for delivery failures
package/dist/pulse.cjs CHANGED
@@ -6313,6 +6313,61 @@ function renderInlineUpdateHint(latest) {
6313
6313
  );
6314
6314
  }
6315
6315
 
6316
+ /**
6317
+ * @svrnsec/pulse — Structured Error Codes
6318
+ *
6319
+ * Use these constants instead of string matching for type-safe error handling.
6320
+ */
6321
+ const PulseErrorCode = Object.freeze({
6322
+ // Structural
6323
+ INVALID_PAYLOAD_STRUCTURE: 'INVALID_PAYLOAD_STRUCTURE',
6324
+ PROTOTYPE_POLLUTION_ATTEMPT: 'PROTOTYPE_POLLUTION_ATTEMPT',
6325
+ MISSING_REQUIRED_FIELD: 'MISSING_REQUIRED_FIELD',
6326
+ INVALID_TYPE: 'INVALID_TYPE',
6327
+ TIMESTAMP_OUT_OF_RANGE: 'TIMESTAMP_OUT_OF_RANGE',
6328
+ UNSUPPORTED_PROOF_VERSION: 'UNSUPPORTED_PROOF_VERSION',
6329
+
6330
+ // Hash integrity
6331
+ INVALID_HASH_FORMAT: 'INVALID_HASH_FORMAT',
6332
+ HASH_MISMATCH_PAYLOAD_TAMPERED: 'HASH_MISMATCH_PAYLOAD_TAMPERED',
6333
+
6334
+ // Timestamp / freshness
6335
+ PROOF_EXPIRED: 'PROOF_EXPIRED',
6336
+ PROOF_FROM_FUTURE: 'PROOF_FROM_FUTURE',
6337
+ NONCE_INVALID_OR_REPLAYED: 'NONCE_INVALID_OR_REPLAYED',
6338
+
6339
+ // Jitter / scoring
6340
+ JITTER_SCORE_TOO_LOW: 'JITTER_SCORE_TOO_LOW',
6341
+ DYNAMIC_THRESHOLD_NOT_MET: 'DYNAMIC_THRESHOLD_NOT_MET',
6342
+
6343
+ // Heuristic / coherence overrides
6344
+ HEURISTIC_HARD_OVERRIDE: 'HEURISTIC_HARD_OVERRIDE',
6345
+ COHERENCE_HARD_OVERRIDE: 'COHERENCE_HARD_OVERRIDE',
6346
+
6347
+ // Renderer
6348
+ SOFTWARE_RENDERER_DETECTED: 'SOFTWARE_RENDERER_DETECTED',
6349
+ BLOCKLISTED_RENDERER: 'BLOCKLISTED_RENDERER',
6350
+
6351
+ // Bio activity
6352
+ NO_BIO_ACTIVITY_DETECTED: 'NO_BIO_ACTIVITY_DETECTED',
6353
+
6354
+ // Cross-signal forgery detection
6355
+ FORGED_SIGNAL_CV_SCORE_IMPOSSIBLE: 'FORGED_SIGNAL:CV_SCORE_IMPOSSIBLE',
6356
+ FORGED_SIGNAL_AUTOCORR_SCORE_IMPOSSIBLE: 'FORGED_SIGNAL:AUTOCORR_SCORE_IMPOSSIBLE',
6357
+ FORGED_SIGNAL_QE_SCORE_IMPOSSIBLE: 'FORGED_SIGNAL:QE_SCORE_IMPOSSIBLE',
6358
+
6359
+ // Risk flags (informational, not rejection reasons)
6360
+ NONCE_FRESHNESS_NOT_CHECKED: 'NONCE_FRESHNESS_NOT_CHECKED',
6361
+ CANVAS_UNAVAILABLE: 'CANVAS_UNAVAILABLE',
6362
+ ZERO_BIO_SAMPLES: 'ZERO_BIO_SAMPLES',
6363
+ NEGATIVE_INTERFERENCE_COEFFICIENT: 'NEGATIVE_INTERFERENCE_COEFFICIENT',
6364
+ INCONSISTENCY_LOW_CV_BUT_HIGH_SCORE: 'INCONSISTENCY:LOW_CV_BUT_HIGH_SCORE',
6365
+ SUSPICIOUS_ZERO_TIMER_GRANULARITY: 'SUSPICIOUS_ZERO_TIMER_GRANULARITY',
6366
+ INCONSISTENCY_FLAT_THERMAL_BUT_HIGH_SCORE: 'INCONSISTENCY:FLAT_THERMAL_BUT_HIGH_SCORE',
6367
+ EXTREME_HURST: 'EXTREME_HURST',
6368
+ AUDIO_JITTER_TOO_FLAT: 'AUDIO_JITTER_TOO_FLAT',
6369
+ });
6370
+
6316
6371
  /**
6317
6372
  * @svrnsec/pulse
6318
6373
  *
@@ -6785,6 +6840,7 @@ var pulse_core = /*#__PURE__*/Object.freeze({
6785
6840
 
6786
6841
  exports.CURRENT_VERSION = CURRENT_VERSION;
6787
6842
  exports.Fingerprint = Fingerprint;
6843
+ exports.PulseErrorCode = PulseErrorCode;
6788
6844
  exports.checkForUpdate = checkForUpdate;
6789
6845
  exports.collectDramTimings = collectDramTimings;
6790
6846
  exports.collectEnfTimings = collectEnfTimings;