@svrnsec/pulse 0.6.0 → 0.8.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.
Files changed (48) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +883 -622
  3. package/SECURITY.md +86 -86
  4. package/bin/svrnsec-pulse.js +7 -7
  5. package/dist/{pulse.cjs.js → pulse.cjs} +6379 -6420
  6. package/dist/pulse.cjs.map +1 -0
  7. package/dist/pulse.esm.js +6380 -6421
  8. package/dist/pulse.esm.js.map +1 -1
  9. package/index.d.ts +895 -846
  10. package/package.json +185 -165
  11. package/pkg/pulse_core.js +174 -173
  12. package/src/analysis/audio.js +213 -213
  13. package/src/analysis/authenticityAudit.js +408 -390
  14. package/src/analysis/coherence.js +502 -502
  15. package/src/analysis/coordinatedBehavior.js +825 -0
  16. package/src/analysis/heuristic.js +428 -428
  17. package/src/analysis/jitter.js +446 -446
  18. package/src/analysis/llm.js +473 -472
  19. package/src/analysis/populationEntropy.js +404 -403
  20. package/src/analysis/provider.js +248 -248
  21. package/src/analysis/refraction.js +392 -0
  22. package/src/analysis/trustScore.js +356 -356
  23. package/src/cli/args.js +36 -36
  24. package/src/cli/commands/scan.js +192 -192
  25. package/src/cli/runner.js +157 -157
  26. package/src/collector/adaptive.js +200 -200
  27. package/src/collector/bio.js +297 -287
  28. package/src/collector/canvas.js +247 -239
  29. package/src/collector/dram.js +203 -203
  30. package/src/collector/enf.js +311 -311
  31. package/src/collector/entropy.js +195 -195
  32. package/src/collector/gpu.js +248 -245
  33. package/src/collector/idleAttestation.js +480 -480
  34. package/src/collector/sabTimer.js +189 -191
  35. package/src/fingerprint.js +475 -475
  36. package/src/index.js +342 -342
  37. package/src/integrations/react-native.js +462 -459
  38. package/src/integrations/react.js +184 -185
  39. package/src/middleware/express.js +155 -155
  40. package/src/middleware/next.js +174 -175
  41. package/src/proof/challenge.js +249 -249
  42. package/src/proof/engagementToken.js +426 -394
  43. package/src/proof/fingerprint.js +268 -268
  44. package/src/proof/validator.js +83 -143
  45. package/src/registry/serializer.js +349 -349
  46. package/src/terminal.js +263 -263
  47. package/src/update-notifier.js +259 -264
  48. package/dist/pulse.cjs.js.map +0 -1
package/src/cli/runner.js CHANGED
@@ -1,157 +1,157 @@
1
- /**
2
- * @svrnsec/pulse CLI — main entry point
3
- *
4
- * Commands:
5
- * scan run the full probe locally
6
- * challenge generate a signed challenge nonce
7
- * version show version and check for updates
8
- * help show this help text
9
- */
10
-
11
- import { parseArgs } from './args.js';
12
- import { printBanner, checkForUpdate, CURRENT_VERSION } from '../update-notifier.js';
13
-
14
- const isTTY = () => process.stderr.isTTY && !process.env.NO_COLOR;
15
- const A = { reset:'\x1b[0m', gray:'\x1b[90m', bcyan:'\x1b[96m',
16
- bwhite:'\x1b[97m', bmagenta:'\x1b[95m', bgreen:'\x1b[92m', byellow:'\x1b[93m' };
17
- const c = (code, s) => isTTY() ? `${code}${s}${A.reset}` : s;
18
- const gy = (s) => c(A.gray, s);
19
- const cy = (s) => c(A.bcyan, s);
20
- const wh = (s) => c(A.bwhite, s);
21
- const gr = (s) => c(A.bgreen, s);
22
- const ye = (s) => c(A.byellow, s);
23
- const mg = (s) => c(A.bmagenta, s);
24
-
25
- function help() {
26
- process.stderr.write(`
27
- ${mg('SVRN')}${wh(':PULSE')} ${gy(`v${CURRENT_VERSION}`)} ${gy('Physical Turing Test')}
28
-
29
- ${wh('Usage')}
30
- ${cy('npx svrnsec-pulse')} ${gy('<command>')} ${gy('[options]')}
31
-
32
- ${wh('Commands')}
33
- ${cy('scan')} Run the full probe locally and show a TrustScore
34
- ${cy('challenge')} Generate a signed HMAC challenge nonce
35
- ${cy('version')} Show version and check for updates
36
- ${cy('help')} Show this help text
37
-
38
- ${wh('Scan options')}
39
- ${gy('--json')} Output raw JSON (pipe-friendly)
40
- ${gy('--iterations')} Override probe iteration count ${gy('(default: 200)')}
41
- ${gy('--no-banner')} Suppress the banner
42
-
43
- ${wh('Challenge options')}
44
- ${gy('--secret')} Server secret for HMAC signing ${gy('(or set PULSE_SECRET env)')}
45
- ${gy('--ttl')} Challenge TTL in seconds ${gy('(default: 300)')}
46
- ${gy('--json')} Output raw JSON
47
-
48
- ${wh('Examples')}
49
- ${gy('$')} ${cy('npx svrnsec-pulse scan')}
50
- ${gy('$')} ${cy('npx svrnsec-pulse scan --json | jq .trustScore.score')}
51
- ${gy('$')} ${cy('npx svrnsec-pulse challenge --secret $PULSE_SECRET')}
52
- ${gy('$')} ${cy('PULSE_SECRET=mysecret npx svrnsec-pulse challenge --json')}
53
-
54
- ${wh('Environment')}
55
- ${gy('PULSE_SECRET')} Default server secret for challenge signing
56
- ${gy('NO_COLOR')} Disable ANSI colors
57
- ${gy('PULSE_NO_UPDATE')} Disable update notifications
58
-
59
- ${gy(' Docs: https://github.com/ayronny14-alt/Svrn-Pulse-Security#readme')}
60
- `);
61
- }
62
-
63
- async function cmdChallenge(args) {
64
- const secret = args.get('secret') ?? process.env.PULSE_SECRET;
65
- if (!secret) {
66
- process.stderr.write(
67
- ye('') + 'No secret provided.\n' +
68
- gy(' Pass --secret <value> or set PULSE_SECRET env var.\n') +
69
- gy(' Generate one: ') + cy('npx svrnsec-pulse challenge --generate-secret\n')
70
- );
71
- process.exit(1);
72
- }
73
-
74
- if (args.has('generate-secret')) {
75
- const { generateSecret } = await import('../proof/challenge.js');
76
- const s = generateSecret();
77
- if (args.has('json')) {
78
- process.stdout.write(JSON.stringify({ secret: s }) + '\n');
79
- } else {
80
- process.stderr.write(gr('Generated secret (store in env):\n'));
81
- process.stdout.write(s + '\n');
82
- }
83
- return;
84
- }
85
-
86
- const { createChallenge } = await import('../proof/challenge.js');
87
- const ttlMs = (parseInt(args.get('ttl', '300'), 10) || 300) * 1000;
88
- const challenge = createChallenge(secret, { ttlMs });
89
-
90
- if (args.has('json')) {
91
- process.stdout.write(JSON.stringify(challenge, null, 2) + '\n');
92
- } else {
93
- process.stderr.write('\n');
94
- process.stderr.write(gy(' nonce ') + wh(challenge.nonce) + '\n');
95
- process.stderr.write(gy(' issuedAt ') + gy(new Date(challenge.issuedAt).toISOString()) + '\n');
96
- process.stderr.write(gy(' expiresAt ') + gy(new Date(challenge.expiresAt).toISOString()) + '\n');
97
- process.stderr.write(gy(' sig ') + cy(challenge.sig) + '\n\n');
98
- }
99
- }
100
-
101
- async function cmdVersion(args) {
102
- if (args.has('json')) {
103
- const { latest, updateAvailable } = await checkForUpdate({ silent: true });
104
- process.stdout.write(JSON.stringify({ version: CURRENT_VERSION, latest, updateAvailable }) + '\n');
105
- return;
106
- }
107
-
108
- process.stderr.write(`\n${mg('SVRN')}${wh(':PULSE')} ${gy('v' + CURRENT_VERSION)}\n\n`);
109
- const { latest, updateAvailable } = await checkForUpdate({ silent: true });
110
- if (updateAvailable) {
111
- process.stderr.write(ye(` Update available: ${latest}\n`));
112
- process.stderr.write(gy(` Run: `) + cy('npm i @svrnsec/pulse@latest') + '\n');
113
- } else if (latest) {
114
- process.stderr.write(gr(' Up to date.\n'));
115
- }
116
- process.stderr.write('\n');
117
- }
118
-
119
- export async function run(argv = process.argv.slice(2)) {
120
- const args = parseArgs(argv);
121
- const cmd = args.command ?? 'help';
122
-
123
- try {
124
- switch (cmd) {
125
- case 'scan': {
126
- const { runScan } = await import('./commands/scan.js');
127
- await runScan(args);
128
- break;
129
- }
130
- case 'challenge':
131
- case 'ch':
132
- await cmdChallenge(args);
133
- break;
134
- case 'version':
135
- case '-v':
136
- case '--version':
137
- await cmdVersion(args);
138
- break;
139
- case 'help':
140
- case '-h':
141
- case '--help':
142
- help();
143
- break;
144
- default:
145
- process.stderr.write(ye(`Unknown command: ${cmd}\n`));
146
- help();
147
- process.exit(1);
148
- }
149
- } catch (err) {
150
- process.stderr.write(
151
- c(A.bmagenta + '\x1b[1m', 'SVRN:PULSE error') + '\n' +
152
- gy(err.message) + '\n'
153
- );
154
- if (process.env.DEBUG) process.stderr.write(err.stack + '\n');
155
- process.exit(1);
156
- }
157
- }
1
+ /**
2
+ * @svrnsec/pulse CLI — main entry point
3
+ *
4
+ * Commands:
5
+ * scan run the full probe locally
6
+ * challenge generate a signed challenge nonce
7
+ * version show version and check for updates
8
+ * help show this help text
9
+ */
10
+
11
+ import { parseArgs } from './args.js';
12
+ import { printBanner, checkForUpdate, CURRENT_VERSION } from '../update-notifier.js';
13
+
14
+ const isTTY = () => process.stderr.isTTY && !process.env.NO_COLOR;
15
+ const A = { reset:'\x1b[0m', gray:'\x1b[90m', bcyan:'\x1b[96m',
16
+ bwhite:'\x1b[97m', bmagenta:'\x1b[95m', bgreen:'\x1b[92m', byellow:'\x1b[93m' };
17
+ const c = (code, s) => isTTY() ? `${code}${s}${A.reset}` : s;
18
+ const gy = (s) => c(A.gray, s);
19
+ const cy = (s) => c(A.bcyan, s);
20
+ const wh = (s) => c(A.bwhite, s);
21
+ const gr = (s) => c(A.bgreen, s);
22
+ const ye = (s) => c(A.byellow, s);
23
+ const mg = (s) => c(A.bmagenta, s);
24
+
25
+ function help() {
26
+ process.stderr.write(`
27
+ ${mg('SVRN')}${wh(':PULSE')} ${gy(`v${CURRENT_VERSION}`)} ${gy('Physical Turing Test')}
28
+
29
+ ${wh('Usage')}
30
+ ${cy('npx svrnsec-pulse')} ${gy('<command>')} ${gy('[options]')}
31
+
32
+ ${wh('Commands')}
33
+ ${cy('scan')} Run the full probe locally and show a TrustScore
34
+ ${cy('challenge')} Generate a signed HMAC challenge nonce
35
+ ${cy('version')} Show version and check for updates
36
+ ${cy('help')} Show this help text
37
+
38
+ ${wh('Scan options')}
39
+ ${gy('--json')} Output raw JSON (pipe-friendly)
40
+ ${gy('--iterations')} Override probe iteration count ${gy('(default: 200)')}
41
+ ${gy('--no-banner')} Suppress the banner
42
+
43
+ ${wh('Challenge options')}
44
+ ${gy('--secret')} Server secret for HMAC signing ${gy('(or set PULSE_SECRET env)')}
45
+ ${gy('--ttl')} Challenge TTL in seconds ${gy('(default: 300)')}
46
+ ${gy('--json')} Output raw JSON
47
+
48
+ ${wh('Examples')}
49
+ ${gy('$')} ${cy('npx svrnsec-pulse scan')}
50
+ ${gy('$')} ${cy('npx svrnsec-pulse scan --json | jq .trustScore.score')}
51
+ ${gy('$')} ${cy('npx svrnsec-pulse challenge --secret $PULSE_SECRET')}
52
+ ${gy('$')} ${cy('PULSE_SECRET=mysecret npx svrnsec-pulse challenge --json')}
53
+
54
+ ${wh('Environment')}
55
+ ${gy('PULSE_SECRET')} Default server secret for challenge signing
56
+ ${gy('NO_COLOR')} Disable ANSI colors
57
+ ${gy('PULSE_NO_UPDATE')} Disable update notifications
58
+
59
+ ${gy(' Docs: https://github.com/ayronny14-alt/Svrn-Pulse-Security#readme')}
60
+ `);
61
+ }
62
+
63
+ async function cmdChallenge(args) {
64
+ if (args.has('generate-secret')) {
65
+ const { generateSecret } = await import('../proof/challenge.js');
66
+ const s = generateSecret();
67
+ if (args.has('json')) {
68
+ process.stdout.write(JSON.stringify({ secret: s }) + '\n');
69
+ } else {
70
+ process.stderr.write(gr('Generated secret (store in env):\n'));
71
+ process.stdout.write(s + '\n');
72
+ }
73
+ return;
74
+ }
75
+
76
+ const secret = args.get('secret') ?? process.env.PULSE_SECRET;
77
+ if (!secret) {
78
+ process.stderr.write(
79
+ ye('⚠ ') + 'No secret provided.\n' +
80
+ gy(' Pass --secret <value> or set PULSE_SECRET env var.\n') +
81
+ gy(' Generate one: ') + cy('npx svrnsec-pulse challenge --generate-secret\n')
82
+ );
83
+ process.exit(1);
84
+ }
85
+
86
+ const { createChallenge } = await import('../proof/challenge.js');
87
+ const ttlMs = (parseInt(args.get('ttl', '300'), 10) || 300) * 1000;
88
+ const challenge = createChallenge(secret, { ttlMs });
89
+
90
+ if (args.has('json')) {
91
+ process.stdout.write(JSON.stringify(challenge, null, 2) + '\n');
92
+ } else {
93
+ process.stderr.write('\n');
94
+ process.stderr.write(gy(' nonce ') + wh(challenge.nonce) + '\n');
95
+ process.stderr.write(gy(' issuedAt ') + gy(new Date(challenge.issuedAt).toISOString()) + '\n');
96
+ process.stderr.write(gy(' expiresAt ') + gy(new Date(challenge.expiresAt).toISOString()) + '\n');
97
+ process.stderr.write(gy(' sig ') + cy(challenge.sig) + '\n\n');
98
+ }
99
+ }
100
+
101
+ async function cmdVersion(args) {
102
+ if (args.has('json')) {
103
+ const { latest, updateAvailable } = await checkForUpdate({ silent: true });
104
+ process.stdout.write(JSON.stringify({ version: CURRENT_VERSION, latest, updateAvailable }) + '\n');
105
+ return;
106
+ }
107
+
108
+ process.stderr.write(`\n${mg('SVRN')}${wh(':PULSE')} ${gy('v' + CURRENT_VERSION)}\n\n`);
109
+ const { latest, updateAvailable } = await checkForUpdate({ silent: true });
110
+ if (updateAvailable) {
111
+ process.stderr.write(ye(` Update available: ${latest}\n`));
112
+ process.stderr.write(gy(` Run: `) + cy('npm i @svrnsec/pulse@latest') + '\n');
113
+ } else if (latest) {
114
+ process.stderr.write(gr(' Up to date.\n'));
115
+ }
116
+ process.stderr.write('\n');
117
+ }
118
+
119
+ export async function run(argv = process.argv.slice(2)) {
120
+ const args = parseArgs(argv);
121
+ const cmd = args.command ?? 'help';
122
+
123
+ try {
124
+ switch (cmd) {
125
+ case 'scan': {
126
+ const { runScan } = await import('./commands/scan.js');
127
+ await runScan(args);
128
+ break;
129
+ }
130
+ case 'challenge':
131
+ case 'ch':
132
+ await cmdChallenge(args);
133
+ break;
134
+ case 'version':
135
+ case '-v':
136
+ case '--version':
137
+ await cmdVersion(args);
138
+ break;
139
+ case 'help':
140
+ case '-h':
141
+ case '--help':
142
+ help();
143
+ break;
144
+ default:
145
+ process.stderr.write(ye(`Unknown command: ${cmd}\n`));
146
+ help();
147
+ process.exit(1);
148
+ }
149
+ } catch (err) {
150
+ process.stderr.write(
151
+ c(A.bmagenta + '\x1b[1m', 'SVRN:PULSE error') + '\n' +
152
+ gy(err.message) + '\n'
153
+ );
154
+ if (process.env.DEBUG) process.stderr.write(err.stack + '\n');
155
+ process.exit(1);
156
+ }
157
+ }