faultmesh 1.0.0 → 1.1.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 (43) hide show
  1. package/README.md +61 -19
  2. package/dist/dashboard/app.js +1906 -139
  3. package/dist/dashboard/index.html +393 -99
  4. package/dist/dashboard/styles.css +1191 -23
  5. package/dist/engine/ControlApi.d.ts +13 -1
  6. package/dist/engine/ControlApi.js +304 -14
  7. package/dist/engine/FaultMeshProxy.d.ts +2 -0
  8. package/dist/engine/FaultMeshProxy.js +41 -5
  9. package/dist/engine/TelemetryHub.d.ts +1 -0
  10. package/dist/engine/TelemetryHub.js +3 -0
  11. package/dist/engine/ToxicPipeline.d.ts +5 -4
  12. package/dist/engine/ToxicPipeline.js +17 -4
  13. package/dist/healer/AiHealer.d.ts +30 -0
  14. package/dist/healer/AiHealer.js +188 -0
  15. package/dist/healer/AutoHealer.d.ts +24 -0
  16. package/dist/healer/AutoHealer.js +503 -0
  17. package/dist/healer/DiffGenerator.d.ts +10 -0
  18. package/dist/healer/DiffGenerator.js +63 -0
  19. package/dist/healer/DiffUtil.d.ts +6 -0
  20. package/dist/healer/DiffUtil.js +67 -0
  21. package/dist/healer/transformers/ExpressTransformers.d.ts +43 -0
  22. package/dist/healer/transformers/ExpressTransformers.js +228 -0
  23. package/dist/healer/transformers/FastApiTransformers.d.ts +19 -0
  24. package/dist/healer/transformers/FastApiTransformers.js +93 -0
  25. package/dist/healer/transformers/GoTransformers.d.ts +19 -0
  26. package/dist/healer/transformers/GoTransformers.js +106 -0
  27. package/dist/healer/types.d.ts +59 -0
  28. package/dist/healer/types.js +1 -0
  29. package/dist/redteam/EccAgentBridge.d.ts +20 -0
  30. package/dist/redteam/EccAgentBridge.js +303 -0
  31. package/dist/redteam/RedTeamEngine.d.ts +56 -0
  32. package/dist/redteam/RedTeamEngine.js +709 -0
  33. package/dist/redteam/types.d.ts +117 -0
  34. package/dist/redteam/types.js +5 -0
  35. package/dist/scorer/ResilienceScorer.d.ts +5 -0
  36. package/dist/scorer/ResilienceScorer.js +323 -7
  37. package/dist/scorer/SecurityAuditor.d.ts +8 -0
  38. package/dist/scorer/SecurityAuditor.js +471 -69
  39. package/dist/scorer/TrafficStormAuditor.d.ts +5 -0
  40. package/dist/scorer/TrafficStormAuditor.js +328 -69
  41. package/dist/server.js +17 -10
  42. package/dist/types.d.ts +7 -3
  43. package/package.json +2 -1
@@ -0,0 +1,117 @@
1
+ /**
2
+ * FaultMesh — Autonomous Red Chaos Team Engine
3
+ * Type definitions for multi-wave adversarial campaigns and agent personas.
4
+ */
5
+ export type RedTeamSpecialty = 'security' | 'silent-failure' | 'performance' | 'adversarial-gan' | 'transport';
6
+ export interface RedTeamPersona {
7
+ id: string;
8
+ name: string;
9
+ callSign: string;
10
+ description: string;
11
+ model: string;
12
+ tools: string[];
13
+ specialty: RedTeamSpecialty;
14
+ huntTargets: string[];
15
+ systemPromptExcerpt: string;
16
+ sourceFile: string;
17
+ }
18
+ export type CampaignPhase = 'idle' | 'recon' | 'weaponize' | 'assault' | 'debrief' | 'complete' | 'aborted';
19
+ export type CampaignIntensity = 'stealth' | 'sustained' | 'avalanche';
20
+ export interface AssaultWaveToxic {
21
+ type: string;
22
+ config: Record<string, any>;
23
+ direction?: 'upstream' | 'downstream';
24
+ pathPattern?: string;
25
+ }
26
+ export interface AssaultWave {
27
+ id: string;
28
+ waveNumber: number;
29
+ name: string;
30
+ personaId: string;
31
+ personaName: string;
32
+ callSign: string;
33
+ category: 'security' | 'resilience' | 'storm';
34
+ targetEndpoint: string;
35
+ toxicsToInject: AssaultWaveToxic[];
36
+ probesCount: number;
37
+ concurrency: number;
38
+ description: string;
39
+ }
40
+ export interface BreachProof {
41
+ method: string;
42
+ url: string;
43
+ requestHeaders?: Record<string, string>;
44
+ requestBody?: any;
45
+ responseStatus: number;
46
+ responseDurationMs: number;
47
+ snippet: string;
48
+ activeToxics?: string[];
49
+ }
50
+ export interface BreachFinding {
51
+ id: string;
52
+ personaId: string;
53
+ personaName: string;
54
+ callSign: string;
55
+ severity: 'critical' | 'high' | 'medium' | 'low';
56
+ category: string;
57
+ title: string;
58
+ impact: string;
59
+ endpoint: string;
60
+ proofOfBreach: BreachProof;
61
+ remediation: string;
62
+ autoHealCheckKey: string;
63
+ }
64
+ export interface CampaignLog {
65
+ timestamp: number;
66
+ waveNumber: number;
67
+ persona: string;
68
+ message: string;
69
+ type: 'info' | 'warn' | 'breach' | 'pass';
70
+ }
71
+ export interface CampaignReport {
72
+ campaignId: string;
73
+ targetUrl: string;
74
+ intensity: CampaignIntensity;
75
+ phase: CampaignPhase;
76
+ startTime: number;
77
+ endTime: number;
78
+ durationMs: number;
79
+ totalWaves: number;
80
+ completedWaves: number;
81
+ totalProbes: number;
82
+ breachesFound: BreachFinding[];
83
+ survivabilityScore: number;
84
+ survivabilityGrade: 'A' | 'B' | 'C' | 'D' | 'F';
85
+ activePersonas: string[];
86
+ logs: CampaignLog[];
87
+ }
88
+ export interface CampaignStatus {
89
+ active: boolean;
90
+ campaignId: string | null;
91
+ phase: CampaignPhase;
92
+ progressPercent: number;
93
+ currentWave: number;
94
+ totalWaves: number;
95
+ currentWaveName: string;
96
+ activePersona: string;
97
+ probesSent: number;
98
+ breachesCount: {
99
+ critical: number;
100
+ high: number;
101
+ medium: number;
102
+ low: number;
103
+ total: number;
104
+ };
105
+ latestLog?: CampaignLog;
106
+ }
107
+ export interface CampaignOptions {
108
+ targetUrl?: string;
109
+ intensity?: CampaignIntensity;
110
+ personaIds?: string[];
111
+ aiConfig?: {
112
+ provider: string;
113
+ endpoint?: string;
114
+ apiKey?: string;
115
+ model?: string;
116
+ };
117
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * FaultMesh — Autonomous Red Chaos Team Engine
3
+ * Type definitions for multi-wave adversarial campaigns and agent personas.
4
+ */
5
+ export {};
@@ -10,4 +10,9 @@ export declare class ResilienceScorer {
10
10
  private testConnectionCut;
11
11
  private testPayloadCorruption;
12
12
  private testServiceOutage;
13
+ private testPacketJitter;
14
+ private testCircuitBreakerRecovery;
15
+ private testSocketStarvation;
16
+ private testZombieConnectionLeak;
17
+ private testPartialTransferTruncation;
13
18
  }
@@ -10,6 +10,43 @@ export class ResilienceScorer {
10
10
  const recommendations = [];
11
11
  // Ensure clean pipeline initially
12
12
  this.pipeline.clearRules();
13
+ // Pre-flight check: verify upstream target is reachable through the proxy
14
+ let upstreamReachable = true;
15
+ let preflightError = '';
16
+ try {
17
+ const probe = await fetch(`${this.proxyUrl}/api/health`, { signal: AbortSignal.timeout(2500) });
18
+ if (probe.status === 502) {
19
+ upstreamReachable = false;
20
+ preflightError = 'Upstream target API returned HTTP 502 Bad Gateway. The configured target server is unreachable or offline.';
21
+ }
22
+ }
23
+ catch (err) {
24
+ upstreamReachable = false;
25
+ preflightError = `Target connection failed (${err.message}). Ensure target server is online.`;
26
+ }
27
+ if (!upstreamReachable) {
28
+ const failedPlaceholders = [
29
+ { name: 'Response Delay Handling (300ms)', description: 'Checks delay handling.', toxicUsed: 'latency', passed: false, latencyMs: 0, details: preflightError },
30
+ { name: 'Slow Connection Throughput (16 kbps)', description: 'Checks low bandwidth.', toxicUsed: 'bandwidth', passed: false, latencyMs: 0, details: preflightError },
31
+ { name: 'Abrupt Disconnection Mid-Transfer', description: 'Checks disconnection.', toxicUsed: 'cut', passed: false, latencyMs: 0, details: preflightError },
32
+ { name: 'Malformed JSON Handling', description: 'Checks data corruption.', toxicUsed: 'corrupt', passed: false, latencyMs: 0, details: preflightError },
33
+ { name: 'HTTP 503 Service Outage Handling', description: 'Checks 503 outage.', toxicUsed: 'status', passed: false, latencyMs: 0, details: preflightError },
34
+ { name: 'Packet Jitter & Latency Variance', description: 'Checks latency jitter.', toxicUsed: 'jitter', passed: false, latencyMs: 0, details: preflightError },
35
+ { name: 'Half-Open Circuit Breaker Recovery', description: 'Checks recovery after downtime.', toxicUsed: 'circuit-breaker', passed: false, latencyMs: 0, details: preflightError },
36
+ { name: 'Downstream Socket Starvation & Slow Read', description: 'Checks connection pool health.', toxicUsed: 'starvation', passed: false, latencyMs: 0, details: preflightError },
37
+ { name: 'Zombie Connection Leak Probe', description: 'Checks client abort socket handling.', toxicUsed: 'zombie-leak', passed: false, latencyMs: 0, details: preflightError },
38
+ { name: 'Payload Truncation & Partial Transfer', description: 'Checks partial response handling.', toxicUsed: 'truncation', passed: false, latencyMs: 0, details: preflightError },
39
+ ];
40
+ return {
41
+ score: 0,
42
+ grade: 'F',
43
+ timestamp: Date.now(),
44
+ totalAttacks: 10,
45
+ passedAttacks: 0,
46
+ results: failedPlaceholders,
47
+ recommendations: ['Target server is unreachable. Check the Target API field in the header and verify your backend server is running.'],
48
+ };
49
+ }
13
50
  // 1. Test 1: Latency Spike
14
51
  const r1 = await this.testLatencySpike(profile);
15
52
  results.push(r1);
@@ -40,10 +77,40 @@ export class ResilienceScorer {
40
77
  if (!r5.passed) {
41
78
  recommendations.push('Handle 5xx server errors gracefully and display a friendly retry prompt to the user.');
42
79
  }
80
+ // 6. Test 6: Packet Jitter & Latency Variance
81
+ const r6 = await this.testPacketJitter(profile);
82
+ results.push(r6);
83
+ if (!r6.passed) {
84
+ recommendations.push('Configure dynamic client timeouts with jitter buffers to absorb fluctuating network delay spikes.');
85
+ }
86
+ // 7. Test 7: Half-Open Circuit Breaker Recovery
87
+ const r7 = await this.testCircuitBreakerRecovery(profile);
88
+ results.push(r7);
89
+ if (!r7.passed) {
90
+ recommendations.push('Implement a half-open state transition in your circuit breaker to allow canary probes through after transient outages.');
91
+ }
92
+ // 8. Test 8: Downstream Socket Starvation & Slow Read
93
+ const r8 = await this.testSocketStarvation(profile);
94
+ results.push(r8);
95
+ if (!r8.passed) {
96
+ recommendations.push('Tune connection pool sizes and enforce request write timeouts to prevent slow clients from exhausting worker threads.');
97
+ }
98
+ // 9. Test 9: Zombie Connection Leak Probe
99
+ const r9 = await this.testZombieConnectionLeak(profile);
100
+ results.push(r9);
101
+ if (!r9.passed) {
102
+ recommendations.push('Listen for request "close" events server-side and abort downstream database queries when clients disconnect prematurely.');
103
+ }
104
+ // 10. Test 10: Payload Truncation & Partial Transfer
105
+ const r10 = await this.testPartialTransferTruncation(profile);
106
+ results.push(r10);
107
+ if (!r10.passed) {
108
+ recommendations.push('Verify Content-Length or chunked stream terminator before parsing payloads to prevent partial data corruption.');
109
+ }
43
110
  // Cleanup pipeline after tests
44
111
  this.pipeline.clearRules();
45
- // Calculate weighted score
46
- const weights = [20, 15, 25, 20, 20];
112
+ // Calculate weighted score (10 checks, 10 points each = 100 points)
113
+ const weights = [10, 10, 10, 10, 10, 10, 10, 10, 10, 10];
47
114
  let score = 0;
48
115
  let passedCount = 0;
49
116
  results.forEach((r, idx) => {
@@ -62,7 +129,7 @@ export class ResilienceScorer {
62
129
  else if (score >= 45)
63
130
  grade = 'D';
64
131
  if (recommendations.length === 0) {
65
- recommendations.push('Your application handled all 5 simulated network failure scenarios smoothly.');
132
+ recommendations.push('Your application handled all 10 simulated network failure and resilience scenarios smoothly.');
66
133
  }
67
134
  return {
68
135
  score,
@@ -101,7 +168,7 @@ export class ResilienceScorer {
101
168
  const res = await fetch(`${this.proxyUrl}/api/health`, { signal: controller.signal });
102
169
  clearTimeout(timeout);
103
170
  const duration = Date.now() - start;
104
- const passed = res.status === 200 && duration >= 180 && duration <= 600;
171
+ const passed = res.status === 200 && duration >= 180 && duration <= 650;
105
172
  return {
106
173
  name: 'Response Delay Handling (300ms)',
107
174
  description: 'Checks whether the client handles delayed network responses without freezing or hanging.',
@@ -282,7 +349,7 @@ export class ResilienceScorer {
282
349
  async testServiceOutage(profile) {
283
350
  if (profile === 'fragile') {
284
351
  return {
285
- name: 'HTTP 503 Service Unavailable Handling',
352
+ name: 'HTTP 503 Service Outage Handling',
286
353
  description: 'Tests if the application properly receives and reacts to temporary server downtime.',
287
354
  toxicUsed: 'status',
288
355
  passed: false,
@@ -304,7 +371,7 @@ export class ResilienceScorer {
304
371
  const res = await fetch(`${this.proxyUrl}/api/data`);
305
372
  const passed = res.status === 503;
306
373
  return {
307
- name: 'HTTP 503 Service Unavailable Handling',
374
+ name: 'HTTP 503 Service Outage Handling',
308
375
  description: 'Tests if the application properly receives and reacts to temporary server downtime.',
309
376
  toxicUsed: 'status',
310
377
  passed,
@@ -314,7 +381,7 @@ export class ResilienceScorer {
314
381
  }
315
382
  catch (err) {
316
383
  return {
317
- name: 'HTTP 503 Service Unavailable Handling',
384
+ name: 'HTTP 503 Service Outage Handling',
318
385
  description: 'Tests if the application properly receives and reacts to temporary server downtime.',
319
386
  toxicUsed: 'status',
320
387
  passed: false,
@@ -327,4 +394,253 @@ export class ResilienceScorer {
327
394
  this.pipeline.removeRule('test_rule_503');
328
395
  }
329
396
  }
397
+ async testPacketJitter(profile) {
398
+ if (profile === 'fragile') {
399
+ return {
400
+ name: 'Packet Jitter & Latency Variance',
401
+ description: 'Injects high latency variance (100ms-350ms) to test jitter buffering.',
402
+ toxicUsed: 'jitter',
403
+ passed: false,
404
+ latencyMs: 380,
405
+ errorCaught: 'PacketOrderingException: stream arrival out of sequence',
406
+ details: 'Client experienced connection timeout or buffer stall under high network jitter',
407
+ };
408
+ }
409
+ this.pipeline.addRule({
410
+ id: 'test_rule_jitter',
411
+ name: 'Simulated High Jitter',
412
+ type: 'latency',
413
+ direction: 'downstream',
414
+ enabled: true,
415
+ config: { latencyMs: 100, jitterMs: 70 },
416
+ });
417
+ const start = Date.now();
418
+ try {
419
+ const res = await fetch(`${this.proxyUrl}/api/health`, { signal: AbortSignal.timeout(1200) });
420
+ const duration = Date.now() - start;
421
+ const passed = res.status === 200 && duration >= 30;
422
+ return {
423
+ name: 'Packet Jitter & Latency Variance',
424
+ description: 'Injects high latency variance (100ms-350ms) to test jitter buffering.',
425
+ toxicUsed: 'jitter',
426
+ passed,
427
+ latencyMs: duration,
428
+ details: passed ? `Handled packet jitter smoothly in ${duration}ms` : `Unstable response latency (${duration}ms)`,
429
+ };
430
+ }
431
+ catch (err) {
432
+ return {
433
+ name: 'Packet Jitter & Latency Variance',
434
+ description: 'Injects high latency variance (100ms-350ms) to test jitter buffering.',
435
+ toxicUsed: 'jitter',
436
+ passed: false,
437
+ latencyMs: Date.now() - start,
438
+ errorCaught: err.message,
439
+ details: `Jitter probe failed: ${err.message}`,
440
+ };
441
+ }
442
+ finally {
443
+ this.pipeline.removeRule('test_rule_jitter');
444
+ }
445
+ }
446
+ async testCircuitBreakerRecovery(profile) {
447
+ if (profile === 'fragile') {
448
+ return {
449
+ name: 'Half-Open Circuit Breaker Recovery',
450
+ description: 'Tests if service recovers immediately after a transient 503 outage.',
451
+ toxicUsed: 'circuit-breaker',
452
+ passed: false,
453
+ latencyMs: 12,
454
+ errorCaught: 'CircuitBreakerOpenError: service remained latched open after outage cleared',
455
+ details: 'Subsequent requests failed because circuit breaker failed to transition to half-open state',
456
+ };
457
+ }
458
+ // Step 1: inject transient 503 outage
459
+ this.pipeline.addRule({
460
+ id: 'test_rule_transient_503',
461
+ name: 'Transient Outage',
462
+ type: 'status',
463
+ direction: 'downstream',
464
+ enabled: true,
465
+ config: { statusCode: 503, statusMessage: 'Transient Down' },
466
+ });
467
+ const start = Date.now();
468
+ try {
469
+ await fetch(`${this.proxyUrl}/api/health`);
470
+ // Step 2: clear outage
471
+ this.pipeline.removeRule('test_rule_transient_503');
472
+ // Step 3: verify recovery request succeeds
473
+ const recoveryRes = await fetch(`${this.proxyUrl}/api/health`, { signal: AbortSignal.timeout(1500) });
474
+ const duration = Date.now() - start;
475
+ const passed = recoveryRes.status === 200;
476
+ return {
477
+ name: 'Half-Open Circuit Breaker Recovery',
478
+ description: 'Tests if service recovers immediately after a transient 503 outage.',
479
+ toxicUsed: 'circuit-breaker',
480
+ passed,
481
+ latencyMs: duration,
482
+ details: passed ? 'Service cleanly resumed serving traffic immediately after outage lifted' : 'Service failed to recover after transient 503',
483
+ };
484
+ }
485
+ catch (err) {
486
+ return {
487
+ name: 'Half-Open Circuit Breaker Recovery',
488
+ description: 'Tests if service recovers immediately after a transient 503 outage.',
489
+ toxicUsed: 'circuit-breaker',
490
+ passed: false,
491
+ latencyMs: Date.now() - start,
492
+ errorCaught: err.message,
493
+ details: `Recovery probe failed: ${err.message}`,
494
+ };
495
+ }
496
+ finally {
497
+ this.pipeline.removeRule('test_rule_transient_503');
498
+ }
499
+ }
500
+ async testSocketStarvation(profile) {
501
+ if (profile === 'fragile') {
502
+ return {
503
+ name: 'Downstream Socket Starvation & Slow Read',
504
+ description: 'Tests if connection pool continues serving healthy clients when one connection is throttled.',
505
+ toxicUsed: 'starvation',
506
+ passed: false,
507
+ latencyMs: 950,
508
+ errorCaught: 'ConnectionPoolExhausted: worker pool starved by slow connection',
509
+ details: 'Server blocked all incoming requests while servicing a single throttled downstream client',
510
+ };
511
+ }
512
+ this.pipeline.addRule({
513
+ id: 'test_rule_slow_client',
514
+ name: 'Throttled Downstream Client',
515
+ type: 'bandwidth',
516
+ direction: 'downstream',
517
+ enabled: true,
518
+ config: { rateKbps: 8 },
519
+ pathPattern: '/api/data',
520
+ });
521
+ const start = Date.now();
522
+ try {
523
+ // Start slow download in background
524
+ const slowPromise = fetch(`${this.proxyUrl}/api/data`).catch(() => { });
525
+ // Concurrent probe to /api/health should not be blocked or starved
526
+ const healthRes = await fetch(`${this.proxyUrl}/api/health`, { signal: AbortSignal.timeout(1200) });
527
+ const duration = Date.now() - start;
528
+ const passed = healthRes.status === 200 && duration < 800;
529
+ await slowPromise;
530
+ return {
531
+ name: 'Downstream Socket Starvation & Slow Read',
532
+ description: 'Tests if connection pool continues serving healthy clients when one connection is throttled.',
533
+ toxicUsed: 'starvation',
534
+ passed,
535
+ latencyMs: duration,
536
+ details: passed ? `Concurrent probe completed in ${duration}ms without pool starvation` : `Health probe stalled (${duration}ms)`,
537
+ };
538
+ }
539
+ catch (err) {
540
+ return {
541
+ name: 'Downstream Socket Starvation & Slow Read',
542
+ description: 'Tests if connection pool continues serving healthy clients when one connection is throttled.',
543
+ toxicUsed: 'starvation',
544
+ passed: false,
545
+ latencyMs: Date.now() - start,
546
+ errorCaught: err.message,
547
+ details: `Starvation test failed: ${err.message}`,
548
+ };
549
+ }
550
+ finally {
551
+ this.pipeline.removeRule('test_rule_slow_client');
552
+ }
553
+ }
554
+ async testZombieConnectionLeak(profile) {
555
+ if (profile === 'fragile') {
556
+ return {
557
+ name: 'Zombie Connection Leak Probe',
558
+ description: 'Tests if backend frees sockets and server resources when client aborts prematurely.',
559
+ toxicUsed: 'zombie-leak',
560
+ passed: false,
561
+ latencyMs: 15,
562
+ errorCaught: 'SocketDescriptorLeak: unreleased TCP handle retained in LISTEN queue',
563
+ details: 'Aborted client connections remained open as zombie handles and leaked system file descriptors',
564
+ };
565
+ }
566
+ const start = Date.now();
567
+ try {
568
+ // Abort connection quickly
569
+ const controller = new AbortController();
570
+ const abortedFetch = fetch(`${this.proxyUrl}/api/data`, { signal: controller.signal }).catch(() => { });
571
+ setTimeout(() => controller.abort(), 10);
572
+ await abortedFetch;
573
+ // Ensure target is immediately responsive for new connection
574
+ const checkRes = await fetch(`${this.proxyUrl}/api/health`, { signal: AbortSignal.timeout(1000) });
575
+ const duration = Date.now() - start;
576
+ const passed = checkRes.status === 200;
577
+ return {
578
+ name: 'Zombie Connection Leak Probe',
579
+ description: 'Tests if backend frees sockets and server resources when client aborts prematurely.',
580
+ toxicUsed: 'zombie-leak',
581
+ passed,
582
+ latencyMs: duration,
583
+ details: passed ? 'Aborted request was cleanly recycled without leaking socket resources' : 'Target unresponsive after abort',
584
+ };
585
+ }
586
+ catch (err) {
587
+ return {
588
+ name: 'Zombie Connection Leak Probe',
589
+ description: 'Tests if backend frees sockets and server resources when client aborts prematurely.',
590
+ toxicUsed: 'zombie-leak',
591
+ passed: false,
592
+ latencyMs: Date.now() - start,
593
+ errorCaught: err.message,
594
+ details: `Zombie leak probe failed: ${err.message}`,
595
+ };
596
+ }
597
+ }
598
+ async testPartialTransferTruncation(profile) {
599
+ if (profile === 'fragile') {
600
+ return {
601
+ name: 'Payload Truncation & Partial Transfer',
602
+ description: 'Evaluates client detection when server terminates transmission mid-body.',
603
+ toxicUsed: 'truncation',
604
+ passed: false,
605
+ latencyMs: 22,
606
+ errorCaught: 'CorruptStateError: client processed incomplete byte stream without boundary check',
607
+ details: 'Client accepted truncated data stream as complete response without validating payload integrity',
608
+ };
609
+ }
610
+ this.pipeline.addRule({
611
+ id: 'test_rule_partial_cut',
612
+ name: 'Partial Body Cut',
613
+ type: 'cut',
614
+ direction: 'downstream',
615
+ enabled: true,
616
+ config: { cutAfterBytes: 30 },
617
+ });
618
+ const start = Date.now();
619
+ try {
620
+ const res = await fetch(`${this.proxyUrl}/api/data`);
621
+ await res.text();
622
+ return {
623
+ name: 'Payload Truncation & Partial Transfer',
624
+ description: 'Evaluates client detection when server terminates transmission mid-body.',
625
+ toxicUsed: 'truncation',
626
+ passed: false,
627
+ latencyMs: Date.now() - start,
628
+ details: 'Stream did not truncate as expected',
629
+ };
630
+ }
631
+ catch (err) {
632
+ return {
633
+ name: 'Payload Truncation & Partial Transfer',
634
+ description: 'Evaluates client detection when server terminates transmission mid-body.',
635
+ toxicUsed: 'truncation',
636
+ passed: true,
637
+ latencyMs: Date.now() - start,
638
+ errorCaught: err.message,
639
+ details: `Partial stream termination detected safely: ${err.message}`,
640
+ };
641
+ }
642
+ finally {
643
+ this.pipeline.removeRule('test_rule_partial_cut');
644
+ }
645
+ }
330
646
  }
@@ -2,6 +2,7 @@ import { SecurityScorecard } from '../types.js';
2
2
  export declare class SecurityAuditor {
3
3
  private targetUrl;
4
4
  constructor(targetUrl: string);
5
+ setTargetUrl(url: string): void;
5
6
  runAudit(profile?: 'secure' | 'vulnerable'): Promise<SecurityScorecard>;
6
7
  private auditDefensiveHeaders;
7
8
  private auditCorsConfiguration;
@@ -10,4 +11,11 @@ export declare class SecurityAuditor {
10
11
  private auditErrorDisclosure;
11
12
  private auditPathTraversal;
12
13
  private auditInertCanaryInjection;
14
+ private auditHostHeaderPoisoning;
15
+ private auditClientIpSpoofing;
16
+ private auditParameterPollution;
17
+ private auditCacheControlHeaders;
18
+ private auditBrokenAuthHeaders;
19
+ private auditTimingAttacks;
20
+ private auditMetadataLeakage;
13
21
  }