@surbee/cipher 0.1.0 → 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.
package/test/fixtures.ts DELETED
@@ -1,627 +0,0 @@
1
- /**
2
- * Test fixtures for Cipher SDK
3
- *
4
- * Contains mock data for testing different validation scenarios.
5
- */
6
-
7
- import type {
8
- ValidationInput,
9
- ResponseInput,
10
- BehavioralMetrics,
11
- DeviceInfo,
12
- MouseMovement,
13
- KeystrokeEvent,
14
- MouseClick,
15
- ScrollEvent,
16
- FocusEvent,
17
- } from '../src/types';
18
-
19
- // =============================================================================
20
- // RESPONSE FIXTURES
21
- // =============================================================================
22
-
23
- /**
24
- * High-quality, legitimate survey responses
25
- */
26
- export const legitimateResponses: ResponseInput[] = [
27
- {
28
- question: 'What do you like most about our product?',
29
- answer: 'I really appreciate the intuitive interface and how easy it is to navigate. The customer support team has also been incredibly helpful whenever I had questions.',
30
- questionType: 'text',
31
- responseTimeMs: 45000, // 45 seconds - thoughtful
32
- questionIndex: 0,
33
- },
34
- {
35
- question: 'How would you rate our service? (1-5)',
36
- answer: '4',
37
- questionType: 'rating',
38
- responseTimeMs: 3000,
39
- questionIndex: 1,
40
- },
41
- {
42
- question: 'How likely are you to recommend us? (1-10)',
43
- answer: '8',
44
- questionType: 'scale',
45
- responseTimeMs: 4000,
46
- questionIndex: 2,
47
- },
48
- {
49
- question: 'What improvements would you suggest?',
50
- answer: 'It would be great to have a mobile app version. Also, adding dark mode would be helpful for late-night work sessions.',
51
- questionType: 'text',
52
- responseTimeMs: 38000,
53
- questionIndex: 3,
54
- },
55
- {
56
- question: 'Which features do you use most?',
57
- answer: 'Dashboard',
58
- questionType: 'multiple_choice',
59
- responseTimeMs: 5000,
60
- questionIndex: 4,
61
- },
62
- ];
63
-
64
- /**
65
- * Low-effort/spam responses
66
- */
67
- export const spamResponses: ResponseInput[] = [
68
- {
69
- question: 'What do you like most about our product?',
70
- answer: 'good',
71
- questionType: 'text',
72
- responseTimeMs: 1200, // Way too fast
73
- questionIndex: 0,
74
- },
75
- {
76
- question: 'How would you rate our service? (1-5)',
77
- answer: '5',
78
- questionType: 'rating',
79
- responseTimeMs: 500,
80
- questionIndex: 1,
81
- },
82
- {
83
- question: 'How likely are you to recommend us? (1-10)',
84
- answer: '5',
85
- questionType: 'scale',
86
- responseTimeMs: 400,
87
- questionIndex: 2,
88
- },
89
- {
90
- question: 'What improvements would you suggest?',
91
- answer: 'n/a',
92
- questionType: 'text',
93
- responseTimeMs: 800,
94
- questionIndex: 3,
95
- },
96
- {
97
- question: 'Which features do you use most?',
98
- answer: 'Dashboard',
99
- questionType: 'multiple_choice',
100
- responseTimeMs: 300,
101
- questionIndex: 4,
102
- },
103
- ];
104
-
105
- /**
106
- * Straight-lining responses (same answer for all scale questions)
107
- */
108
- export const straightLiningResponses: ResponseInput[] = [
109
- {
110
- question: 'How satisfied are you with feature A? (1-5)',
111
- answer: '3',
112
- questionType: 'rating',
113
- responseTimeMs: 2000,
114
- questionIndex: 0,
115
- },
116
- {
117
- question: 'How satisfied are you with feature B? (1-5)',
118
- answer: '3',
119
- questionType: 'rating',
120
- responseTimeMs: 1800,
121
- questionIndex: 1,
122
- },
123
- {
124
- question: 'How satisfied are you with feature C? (1-5)',
125
- answer: '3',
126
- questionType: 'rating',
127
- responseTimeMs: 1900,
128
- questionIndex: 2,
129
- },
130
- {
131
- question: 'How satisfied are you with feature D? (1-5)',
132
- answer: '3',
133
- questionType: 'rating',
134
- responseTimeMs: 1700,
135
- questionIndex: 3,
136
- },
137
- {
138
- question: 'How satisfied are you with feature E? (1-5)',
139
- answer: '3',
140
- questionType: 'rating',
141
- responseTimeMs: 1850,
142
- questionIndex: 4,
143
- },
144
- {
145
- question: 'How satisfied are you with feature F? (1-5)',
146
- answer: '3',
147
- questionType: 'rating',
148
- responseTimeMs: 1750,
149
- questionIndex: 5,
150
- },
151
- ];
152
-
153
- /**
154
- * Bot-like rapid completion
155
- */
156
- export const botResponses: ResponseInput[] = [
157
- {
158
- question: 'Describe your experience',
159
- answer: 'The experience was satisfactory and met expectations adequately.',
160
- questionType: 'text',
161
- responseTimeMs: 800, // Impossibly fast for this text length
162
- questionIndex: 0,
163
- },
164
- {
165
- question: 'Rate overall satisfaction (1-5)',
166
- answer: '4',
167
- questionType: 'rating',
168
- responseTimeMs: 100, // Way too fast
169
- questionIndex: 1,
170
- },
171
- {
172
- question: 'Additional comments',
173
- answer: 'No additional comments at this time. Thank you for the survey.',
174
- questionType: 'text',
175
- responseTimeMs: 500, // Impossibly fast
176
- questionIndex: 2,
177
- },
178
- ];
179
-
180
- // =============================================================================
181
- // BEHAVIORAL METRICS FIXTURES
182
- // =============================================================================
183
-
184
- /**
185
- * Normal human behavioral metrics
186
- */
187
- export const normalBehavior: BehavioralMetrics = {
188
- sessionId: 'test-session-001',
189
- startedAt: Date.now() - 180000,
190
- duration: 180000, // 3 minutes
191
- lastActiveAt: Date.now(),
192
- mouseMovements: generateHumanMouseMovements(100),
193
- mouseClicks: generateHumanClicks(15),
194
- mouseMovementCount: 450,
195
- avgMouseVelocity: 8.5,
196
- keystrokeDynamics: generateHumanKeystrokes(50),
197
- keypressCount: 280,
198
- backspaceCount: 28,
199
- avgKeystrokeDwell: 120,
200
- keystrokeVariance: 0.35,
201
- scrollEvents: generateHumanScrolls(10),
202
- scrollEventCount: 35,
203
- focusEvents: [{ type: 'focus', t: 0 }, { type: 'blur', t: 60000 }, { type: 'focus', t: 65000 }],
204
- tabSwitchCount: 2,
205
- totalBlurDuration: 5000,
206
- pasteEvents: 0,
207
- copyEvents: 0,
208
- hoverEvents: [],
209
- responseTime: [45000, 3000, 4000, 38000, 5000],
210
- questionStartTimes: { 'q0': 0, 'q1': 45000, 'q2': 48000, 'q3': 52000, 'q4': 90000 },
211
- };
212
-
213
- /**
214
- * Bot-like behavioral metrics
215
- */
216
- export const botBehavior: BehavioralMetrics = {
217
- sessionId: 'test-session-bot',
218
- startedAt: Date.now() - 15000,
219
- duration: 15000, // 15 seconds - way too fast
220
- lastActiveAt: Date.now(),
221
- mouseMovements: generateRoboticMouseMovements(20),
222
- mouseClicks: generateRoboticClicks(5),
223
- mouseMovementCount: 20,
224
- avgMouseVelocity: 10, // Constant
225
- keystrokeDynamics: [], // No typing
226
- keypressCount: 0,
227
- backspaceCount: 0, // No corrections
228
- avgKeystrokeDwell: 0,
229
- keystrokeVariance: 0,
230
- scrollEvents: generateRoboticScrolls(2),
231
- scrollEventCount: 2,
232
- focusEvents: [{ type: 'focus', t: 0 }],
233
- tabSwitchCount: 0,
234
- totalBlurDuration: 0,
235
- pasteEvents: 5, // All pasted
236
- copyEvents: 0,
237
- hoverEvents: [],
238
- responseTime: [800, 100, 500],
239
- questionStartTimes: { 'q0': 0, 'q1': 800, 'q2': 900 },
240
- };
241
-
242
- /**
243
- * Suspicious behavioral metrics (paste-heavy)
244
- */
245
- export const pasteHeavyBehavior: BehavioralMetrics = {
246
- sessionId: 'test-session-paste',
247
- startedAt: Date.now() - 60000,
248
- duration: 60000, // 1 minute
249
- lastActiveAt: Date.now(),
250
- mouseMovements: generateHumanMouseMovements(50),
251
- mouseClicks: generateHumanClicks(8),
252
- mouseMovementCount: 150,
253
- avgMouseVelocity: 7.2,
254
- keystrokeDynamics: generateHumanKeystrokes(10),
255
- keypressCount: 10,
256
- backspaceCount: 0,
257
- avgKeystrokeDwell: 110,
258
- keystrokeVariance: 0.28,
259
- scrollEvents: generateHumanScrolls(5),
260
- scrollEventCount: 8,
261
- focusEvents: [
262
- { type: 'focus', t: 0 },
263
- { type: 'blur', t: 5000 },
264
- { type: 'focus', t: 10000 },
265
- { type: 'blur', t: 15000 },
266
- { type: 'focus', t: 25000 },
267
- { type: 'blur', t: 30000 },
268
- { type: 'focus', t: 50000 },
269
- ],
270
- tabSwitchCount: 12, // Many tab switches
271
- totalBlurDuration: 40000, // 66% time away
272
- pasteEvents: 8, // Heavy pasting
273
- copyEvents: 0,
274
- hoverEvents: [],
275
- responseTime: [15000, 8000, 12000, 18000, 7000],
276
- questionStartTimes: { 'q0': 0, 'q1': 15000, 'q2': 23000, 'q3': 35000, 'q4': 53000 },
277
- };
278
-
279
- // =============================================================================
280
- // DEVICE INFO FIXTURES
281
- // =============================================================================
282
-
283
- /**
284
- * Normal desktop device
285
- */
286
- export const normalDevice: DeviceInfo = {
287
- userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
288
- platform: 'MacIntel',
289
- language: 'en-US',
290
- languages: ['en-US', 'en'],
291
- timezone: 'America/New_York',
292
- timezoneOffset: 300,
293
- screenWidth: 1920,
294
- screenHeight: 1080,
295
- screenAvailWidth: 1920,
296
- screenAvailHeight: 1055,
297
- colorDepth: 24,
298
- pixelRatio: 2,
299
- touchSupport: false,
300
- maxTouchPoints: 0,
301
- hardwareConcurrency: 8,
302
- deviceMemory: 16,
303
- cookiesEnabled: true,
304
- webDriver: false,
305
- automationDetected: false,
306
- canvasFingerprint: 'abc123xyz',
307
- webglVendor: 'Apple Inc.',
308
- webglRenderer: 'Apple M1',
309
- pluginCount: 5,
310
- collectedAt: Date.now(),
311
- };
312
-
313
- /**
314
- * Normal mobile device
315
- */
316
- export const mobileDevice: DeviceInfo = {
317
- userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1',
318
- platform: 'iPhone',
319
- language: 'en-US',
320
- languages: ['en-US'],
321
- timezone: 'America/Los_Angeles',
322
- timezoneOffset: 480,
323
- screenWidth: 390,
324
- screenHeight: 844,
325
- screenAvailWidth: 390,
326
- screenAvailHeight: 844,
327
- colorDepth: 24,
328
- pixelRatio: 3,
329
- touchSupport: true,
330
- maxTouchPoints: 5,
331
- hardwareConcurrency: 6,
332
- deviceMemory: 4,
333
- cookiesEnabled: true,
334
- webDriver: false,
335
- automationDetected: false,
336
- canvasFingerprint: 'mobile123',
337
- webglVendor: 'Apple Inc.',
338
- webglRenderer: 'Apple GPU',
339
- pluginCount: 0, // Normal for mobile
340
- collectedAt: Date.now(),
341
- };
342
-
343
- /**
344
- * Headless browser / bot device
345
- */
346
- export const headlessDevice: DeviceInfo = {
347
- userAgent: 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/120.0.0.0 Safari/537.36',
348
- platform: 'Linux x86_64',
349
- language: 'en-US',
350
- languages: ['en-US'],
351
- timezone: 'UTC',
352
- timezoneOffset: 0,
353
- screenWidth: 800,
354
- screenHeight: 600,
355
- screenAvailWidth: 800,
356
- screenAvailHeight: 600,
357
- colorDepth: 24,
358
- pixelRatio: 1,
359
- touchSupport: false,
360
- maxTouchPoints: 0,
361
- hardwareConcurrency: 4,
362
- deviceMemory: 8,
363
- cookiesEnabled: true,
364
- webDriver: true,
365
- automationDetected: true,
366
- canvasFingerprint: null,
367
- webglVendor: null,
368
- webglRenderer: null,
369
- pluginCount: 0,
370
- collectedAt: Date.now(),
371
- };
372
-
373
- /**
374
- * Spoofed/mismatched device
375
- */
376
- export const spoofedDevice: DeviceInfo = {
377
- userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
378
- platform: 'Win32',
379
- language: 'en-US',
380
- languages: ['en-US'],
381
- timezone: 'America/New_York',
382
- timezoneOffset: 1000, // Invalid offset
383
- screenWidth: 1920,
384
- screenHeight: 1080,
385
- screenAvailWidth: 2000, // Impossible: larger than screen
386
- screenAvailHeight: 1200,
387
- colorDepth: 24,
388
- pixelRatio: 0, // Invalid
389
- touchSupport: true,
390
- maxTouchPoints: 0, // Mismatch with touchSupport
391
- hardwareConcurrency: 256, // Impossible
392
- deviceMemory: 512, // Impossible
393
- cookiesEnabled: true,
394
- webDriver: false,
395
- automationDetected: false,
396
- canvasFingerprint: 'spoofed',
397
- webglVendor: 'Spoofed Vendor',
398
- webglRenderer: 'Spoofed Renderer',
399
- pluginCount: 0,
400
- collectedAt: Date.now(),
401
- };
402
-
403
- // =============================================================================
404
- // COMPLETE VALIDATION INPUT FIXTURES
405
- // =============================================================================
406
-
407
- /**
408
- * Legitimate survey submission
409
- */
410
- export const legitimateSubmission: ValidationInput = {
411
- responses: legitimateResponses,
412
- behavioralMetrics: normalBehavior,
413
- deviceInfo: normalDevice,
414
- context: {
415
- surveyId: 'test-survey-1',
416
- expectedMinTime: 60000, // 1 minute minimum
417
- expectedMaxTime: 600000, // 10 minutes max
418
- questionCount: 5,
419
- },
420
- };
421
-
422
- /**
423
- * Spam/low-effort submission
424
- */
425
- export const spamSubmission: ValidationInput = {
426
- responses: spamResponses,
427
- behavioralMetrics: botBehavior,
428
- deviceInfo: normalDevice,
429
- context: {
430
- surveyId: 'test-survey-1',
431
- expectedMinTime: 60000,
432
- expectedMaxTime: 600000,
433
- questionCount: 5,
434
- },
435
- };
436
-
437
- /**
438
- * Bot submission
439
- */
440
- export const botSubmission: ValidationInput = {
441
- responses: botResponses,
442
- behavioralMetrics: botBehavior,
443
- deviceInfo: headlessDevice,
444
- context: {
445
- surveyId: 'test-survey-1',
446
- expectedMinTime: 60000,
447
- expectedMaxTime: 600000,
448
- questionCount: 3,
449
- },
450
- };
451
-
452
- /**
453
- * Straight-lining submission
454
- */
455
- export const straightLiningSubmission: ValidationInput = {
456
- responses: straightLiningResponses,
457
- behavioralMetrics: normalBehavior,
458
- deviceInfo: normalDevice,
459
- context: {
460
- surveyId: 'test-survey-2',
461
- expectedMinTime: 30000,
462
- expectedMaxTime: 300000,
463
- questionCount: 6,
464
- },
465
- };
466
-
467
- /**
468
- * AI-assisted submission (paste-heavy)
469
- */
470
- export const aiAssistedSubmission: ValidationInput = {
471
- responses: legitimateResponses,
472
- behavioralMetrics: pasteHeavyBehavior,
473
- deviceInfo: normalDevice,
474
- context: {
475
- surveyId: 'test-survey-1',
476
- expectedMinTime: 60000,
477
- expectedMaxTime: 600000,
478
- questionCount: 5,
479
- },
480
- };
481
-
482
- // =============================================================================
483
- // HELPER FUNCTIONS
484
- // =============================================================================
485
-
486
- function generateHumanMouseMovements(count: number): MouseMovement[] {
487
- const movements: MouseMovement[] = [];
488
- let x = 500;
489
- let y = 400;
490
- let t = 0;
491
-
492
- for (let i = 0; i < count; i++) {
493
- // Human-like random movement with varying velocities
494
- const dx = (Math.random() - 0.5) * 100;
495
- const dy = (Math.random() - 0.5) * 100;
496
- const dt = 16 + Math.random() * 50; // 16-66ms between movements
497
-
498
- x = Math.max(0, Math.min(1920, x + dx));
499
- y = Math.max(0, Math.min(1080, y + dy));
500
- t += dt;
501
-
502
- const distance = Math.sqrt(dx * dx + dy * dy);
503
- const velocity = distance / dt;
504
-
505
- movements.push({ x, y, t, velocity });
506
- }
507
-
508
- return movements;
509
- }
510
-
511
- function generateRoboticMouseMovements(count: number): MouseMovement[] {
512
- const movements: MouseMovement[] = [];
513
- let x = 0;
514
- let y = 0;
515
- let t = 0;
516
-
517
- for (let i = 0; i < count; i++) {
518
- // Perfectly linear movement at constant speed
519
- x += 100;
520
- y += 50;
521
- t += 10; // Uniform timing
522
-
523
- movements.push({ x, y, t, velocity: 10 }); // Constant velocity
524
- }
525
-
526
- return movements;
527
- }
528
-
529
- function generateHumanKeystrokes(count: number): KeystrokeEvent[] {
530
- const keystrokes: KeystrokeEvent[] = [];
531
- let t = 0;
532
-
533
- for (let i = 0; i < count; i++) {
534
- // Human typing has variable dwell and flight times
535
- const dwell = 80 + Math.random() * 120; // 80-200ms key hold
536
- const flightTime = 100 + Math.random() * 200; // 100-300ms between keys
537
- const downAt = t;
538
- const upAt = t + dwell;
539
-
540
- keystrokes.push({
541
- key: String.fromCharCode(97 + Math.floor(Math.random() * 26)),
542
- downAt,
543
- upAt,
544
- dwell,
545
- flightTime,
546
- });
547
-
548
- t = upAt + flightTime;
549
- }
550
-
551
- return keystrokes;
552
- }
553
-
554
- function generateHumanClicks(count: number): MouseClick[] {
555
- const clicks: MouseClick[] = [];
556
- let t = 0;
557
-
558
- for (let i = 0; i < count; i++) {
559
- clicks.push({
560
- x: Math.random() * 1920,
561
- y: Math.random() * 1080,
562
- t,
563
- hadHover: Math.random() > 0.2, // 80% have hover first
564
- });
565
-
566
- t += 2000 + Math.random() * 5000; // 2-7 seconds between clicks
567
- }
568
-
569
- return clicks;
570
- }
571
-
572
- function generateRoboticClicks(count: number): MouseClick[] {
573
- const clicks: MouseClick[] = [];
574
- let t = 0;
575
-
576
- for (let i = 0; i < count; i++) {
577
- clicks.push({
578
- x: 500 + i * 100,
579
- y: 300,
580
- t,
581
- hadHover: false, // No hover before click
582
- });
583
-
584
- t += 500; // Uniform 500ms between clicks
585
- }
586
-
587
- return clicks;
588
- }
589
-
590
- function generateHumanScrolls(count: number): ScrollEvent[] {
591
- const scrolls: ScrollEvent[] = [];
592
- let y = 0;
593
- let t = 0;
594
-
595
- for (let i = 0; i < count; i++) {
596
- const deltaY = (Math.random() - 0.3) * 200;
597
- y += deltaY;
598
- scrolls.push({
599
- y,
600
- velocity: 2 + Math.random() * 8, // Variable velocity
601
- t,
602
- });
603
-
604
- t += 500 + Math.random() * 2000;
605
- }
606
-
607
- return scrolls;
608
- }
609
-
610
- function generateRoboticScrolls(count: number): ScrollEvent[] {
611
- const scrolls: ScrollEvent[] = [];
612
- let y = 0;
613
- let t = 0;
614
-
615
- for (let i = 0; i < count; i++) {
616
- y += 100;
617
- scrolls.push({
618
- y,
619
- velocity: 5, // Constant velocity
620
- t,
621
- });
622
-
623
- t += 1000; // Uniform timing
624
- }
625
-
626
- return scrolls;
627
- }
package/tsconfig.json DELETED
@@ -1,20 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "ESNext",
5
- "moduleResolution": "bundler",
6
- "lib": ["ES2022"],
7
- "strict": true,
8
- "esModuleInterop": true,
9
- "skipLibCheck": true,
10
- "forceConsistentCasingInFileNames": true,
11
- "declaration": true,
12
- "declarationMap": true,
13
- "outDir": "./dist",
14
- "rootDir": "./src",
15
- "resolveJsonModule": true,
16
- "isolatedModules": true
17
- },
18
- "include": ["src/**/*"],
19
- "exclude": ["node_modules", "dist"]
20
- }