iri-shield 1.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.
@@ -0,0 +1,346 @@
1
+ 'use strict';
2
+
3
+ const http = require('http');
4
+ const express = require('express');
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+ const { performance } = require('perf_hooks');
8
+ const { createShield } = require('../src/index.js');
9
+
10
+ const RESULTS_DIR = path.join(__dirname, '..', 'results');
11
+ if (!fs.existsSync(RESULTS_DIR)) fs.mkdirSync(RESULTS_DIR, { recursive: true });
12
+
13
+ // Configurable workload configurations or matrix
14
+ const MATRIX_REQUESTS = process.env.BENCHMARK_REQUESTS_LIST
15
+ ? process.env.BENCHMARK_REQUESTS_LIST.split(',').map(n => parseInt(n.trim(), 10))
16
+ : [500, 1000, 2000];
17
+
18
+ const MATRIX_CONCURRENCY = process.env.BENCHMARK_CONCURRENCY_LIST
19
+ ? process.env.BENCHMARK_CONCURRENCY_LIST.split(',').map(n => parseInt(n.trim(), 10))
20
+ : [5, 15, 30];
21
+
22
+ const REPEATED_TRIALS = parseInt(process.env.BENCHMARK_TRIALS || '3', 10);
23
+ const WARMUP_REQUESTS = 150;
24
+
25
+ const ENDPOINTS = [
26
+ { path: "/api/health", method: "GET" },
27
+ { path: "/api/users/profile", method: "GET" },
28
+ { path: "/api/items?category=electronics&page=1", method: "GET" },
29
+ { path: "/api/articles/123", method: "GET" },
30
+ { path: "/api/search?q=wireless%20headphones", method: "GET" }
31
+ ];
32
+
33
+ function createBaselineApp() {
34
+ const app = express();
35
+ app.use(express.json());
36
+ app.get('/api/health', (req, res) => res.json({ status: 'ok' }));
37
+ app.get('/api/users/profile', (req, res) => res.json({ user: 'john_doe', role: 'member' }));
38
+ app.get('/api/items', (req, res) => res.json({ items: [{ id: 1, name: 'Item A' }] }));
39
+ app.get('/api/articles/:id', (req, res) => res.json({ id: req.params.id, title: 'Sample' }));
40
+ app.get('/api/search', (req, res) => res.json({ results: [] }));
41
+ return app;
42
+ }
43
+
44
+ function createShieldApp() {
45
+ const app = express();
46
+ app.use(express.json());
47
+ const shield = createShield({
48
+ appName: 'benchmark-shield',
49
+ security: 'medium',
50
+ logger: false,
51
+ dashboard: { enabled: false },
52
+ rateLimit: { max: 100000, windowMs: 60000 },
53
+ storage: { mode: 'memory' }
54
+ });
55
+ app.use(shield.middleware);
56
+ app.get('/api/health', (req, res) => res.json({ status: 'ok' }));
57
+ app.get('/api/users/profile', (req, res) => res.json({ user: 'john_doe', role: 'member' }));
58
+ app.get('/api/items', (req, res) => res.json({ items: [{ id: 1, name: 'Item A' }] }));
59
+ app.get('/api/articles/:id', (req, res) => res.json({ id: req.params.id, title: 'Sample' }));
60
+ app.get('/api/search', (req, res) => res.json({ results: [] }));
61
+ return { app, shield };
62
+ }
63
+
64
+ function makeRequest(port, idx) {
65
+ return new Promise((resolve) => {
66
+ const start = performance.now();
67
+ const target = ENDPOINTS[idx % ENDPOINTS.length];
68
+ const options = {
69
+ hostname: '127.0.0.1',
70
+ port,
71
+ path: encodeURI(target.path),
72
+ method: target.method,
73
+ headers: {
74
+ 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 BenchmarkClient/1.0',
75
+ 'x-forwarded-for': `10.0.${(idx % 100) + 1}.${(Math.floor(idx / 100) % 250) + 1}`,
76
+ 'accept': 'application/json'
77
+ }
78
+ };
79
+ const req = http.request(options, (res) => {
80
+ let data = '';
81
+ res.on('data', chunk => { data += chunk; });
82
+ res.on('end', () => {
83
+ const duration = performance.now() - start;
84
+ resolve({ statusCode: res.statusCode, duration });
85
+ });
86
+ });
87
+ req.on('error', () => {
88
+ resolve({ statusCode: 500, duration: performance.now() - start });
89
+ });
90
+ req.end();
91
+ });
92
+ }
93
+
94
+ async function sendWarmup(port, count) {
95
+ const promises = [];
96
+ for (let i = 0; i < count; i++) {
97
+ promises.push(makeRequest(port, i));
98
+ }
99
+ await Promise.all(promises);
100
+ }
101
+
102
+ async function executeLoadTrial(port, numRequests, concurrency) {
103
+ const latencies = [];
104
+ const startCpu = process.cpuUsage();
105
+ const startHeap = process.memoryUsage().heapUsed;
106
+ const overallStart = performance.now();
107
+
108
+ let reqIndex = 0;
109
+ async function worker() {
110
+ while (reqIndex < numRequests) {
111
+ const idx = reqIndex++;
112
+ const res = await makeRequest(port, idx);
113
+ latencies.push(res.duration);
114
+ }
115
+ }
116
+
117
+ const workers = [];
118
+ for (let i = 0; i < concurrency; i++) workers.push(worker());
119
+ await Promise.all(workers);
120
+
121
+ const durationSec = (performance.now() - overallStart) / 1000;
122
+ const endHeap = process.memoryUsage().heapUsed;
123
+ const cpuDiff = process.cpuUsage(startCpu);
124
+ const totalCpuMicroseconds = cpuDiff.user + cpuDiff.system;
125
+
126
+ const os = require('os');
127
+ const numCores = os.cpus().length || 1;
128
+ const coresUtilized = Number((totalCpuMicroseconds / (durationSec * 1000000)).toFixed(2));
129
+ const normalizedCpuPercent = Number(((coresUtilized / numCores) * 100).toFixed(1));
130
+ const processLoadPercent = Number((coresUtilized * 100).toFixed(1));
131
+
132
+ latencies.sort((a, b) => a - b);
133
+ const sum = latencies.reduce((a, b) => a + b, 0);
134
+ const avg = sum / latencies.length;
135
+ const median = latencies[Math.floor(latencies.length * 0.50)] || 0;
136
+ const p90 = latencies[Math.floor(latencies.length * 0.90)] || 0;
137
+ const p95 = latencies[Math.floor(latencies.length * 0.95)] || 0;
138
+ const p99 = latencies[Math.floor(latencies.length * 0.99)] || 0;
139
+
140
+ // Compute Standard Deviation (sigma)
141
+ const variance = latencies.reduce((acc, val) => acc + Math.pow(val - avg, 2), 0) / latencies.length;
142
+ const stdDev = Math.sqrt(variance);
143
+
144
+ return {
145
+ requests: numRequests,
146
+ concurrency,
147
+ durationSec: Number(durationSec.toFixed(3)),
148
+ throughput: Math.round(numRequests / durationSec),
149
+ coresUtilized,
150
+ normalizedCpuPercent,
151
+ cpuPercent: normalizedCpuPercent, // Standard system-normalized CPU
152
+ processLoadPercent, // Multi-core process load (where 100% = 1 core)
153
+ heapDeltaMb: Number(((endHeap - startHeap) / 1024 / 1024).toFixed(2)),
154
+ latency: {
155
+ avg: Number(avg.toFixed(2)),
156
+ median: Number(median.toFixed(2)),
157
+ stdDev: Number(stdDev.toFixed(2)),
158
+ p90: Number(p90.toFixed(2)),
159
+ p95: Number(p95.toFixed(2)),
160
+ p99: Number(p99.toFixed(2))
161
+ }
162
+ };
163
+ }
164
+
165
+ function aggregateTrials(trials) {
166
+ const n = trials.length;
167
+ const avgThroughput = Math.round(trials.reduce((sum, t) => sum + t.throughput, 0) / n);
168
+ const avgLatency = Number((trials.reduce((sum, t) => sum + t.latency.avg, 0) / n).toFixed(2));
169
+ const avgMedian = Number((trials.reduce((sum, t) => sum + t.latency.median, 0) / n).toFixed(2));
170
+ const avgStdDev = Number((trials.reduce((sum, t) => sum + t.latency.stdDev, 0) / n).toFixed(2));
171
+ const avgP95 = Number((trials.reduce((sum, t) => sum + t.latency.p95, 0) / n).toFixed(2));
172
+ const avgP99 = Number((trials.reduce((sum, t) => sum + t.latency.p99, 0) / n).toFixed(2));
173
+ const avgCores = Number((trials.reduce((sum, t) => sum + t.coresUtilized, 0) / n).toFixed(2));
174
+ const avgCpu = Number((trials.reduce((sum, t) => sum + t.normalizedCpuPercent, 0) / n).toFixed(1));
175
+ const avgProcessLoad = Number((trials.reduce((sum, t) => sum + t.processLoadPercent, 0) / n).toFixed(1));
176
+ const avgHeapDelta = Number((trials.reduce((sum, t) => sum + t.heapDeltaMb, 0) / n).toFixed(2));
177
+
178
+ return {
179
+ throughput: avgThroughput,
180
+ coresUtilized: avgCores,
181
+ normalizedCpuPercent: avgCpu,
182
+ cpuPercent: avgCpu,
183
+ processLoadPercent: avgProcessLoad,
184
+ heapDeltaMb: avgHeapDelta,
185
+ latency: {
186
+ avg: avgLatency,
187
+ median: avgMedian,
188
+ stdDev: avgStdDev,
189
+ p95: avgP95,
190
+ p99: avgP99
191
+ }
192
+ };
193
+ }
194
+
195
+ async function runPerformanceBenchmark() {
196
+ console.log("================================================================================");
197
+ console.log(" iri-shield Multi-Workload Scientific Performance Benchmark Matrix ");
198
+ console.log("================================================================================");
199
+ console.log(`Workloads Matrix : Requests ${JSON.stringify(MATRIX_REQUESTS)} × Concurrency ${JSON.stringify(MATRIX_CONCURRENCY)}`);
200
+ console.log(`Repeated Trials : ${REPEATED_TRIALS} runs per configuration (computing Mean, Median, StdDev, p95, p99)`);
201
+ console.log(`Warm-up Phase : ${WARMUP_REQUESTS} initial requests per server to stabilize V8 JIT\n`);
202
+
203
+ // Start Baseline App
204
+ const baselineApp = createBaselineApp();
205
+ const baselineServer = baselineApp.listen(0);
206
+ const baselinePort = baselineServer.address().port;
207
+
208
+ // Start Shield App
209
+ const { app: shieldApp } = createShieldApp();
210
+ const shieldServer = shieldApp.listen(0);
211
+ const shieldPort = shieldServer.address().port;
212
+
213
+ console.log(`[Warm-up] Warming up V8 JIT compiler on baseline (port ${baselinePort}) and shield (port ${shieldPort})...`);
214
+ await sendWarmup(baselinePort, WARMUP_REQUESTS);
215
+ await sendWarmup(shieldPort, WARMUP_REQUESTS);
216
+ console.log(`[Warm-up] Warmup completed. Beginning measured matrix execution.\n`);
217
+
218
+ const matrixResults = [];
219
+ const csvRows = [
220
+ [
221
+ 'Requests', 'Concurrency', 'Trials',
222
+ 'Baseline_Throughput_ReqSec', 'Shield_Throughput_ReqSec', 'Throughput_Delta_Pct',
223
+ 'Baseline_Avg_Latency_ms', 'Shield_Avg_Latency_ms', 'Latency_Overhead_ms',
224
+ 'Baseline_Median_ms', 'Shield_Median_ms',
225
+ 'Baseline_StdDev_ms', 'Shield_StdDev_ms',
226
+ 'Baseline_p95_ms', 'Shield_p95_ms',
227
+ 'Baseline_p99_ms', 'Shield_p99_ms',
228
+ 'Baseline_Cores_Utilized', 'Shield_Cores_Utilized',
229
+ 'Baseline_Normalized_CPU_Pct', 'Shield_Normalized_CPU_Pct',
230
+ 'Baseline_Heap_MB', 'Shield_Heap_MB'
231
+ ]
232
+ ];
233
+
234
+ for (const reqCount of MATRIX_REQUESTS) {
235
+ for (const concurrency of MATRIX_CONCURRENCY) {
236
+ process.stdout.write(`Benchmarking Workload: ${reqCount} reqs @ ${concurrency} workers (${REPEATED_TRIALS} trials)... `);
237
+
238
+ const baselineTrials = [];
239
+ const shieldTrials = [];
240
+
241
+ for (let t = 0; t < REPEATED_TRIALS; t++) {
242
+ const bRes = await executeLoadTrial(baselinePort, reqCount, concurrency);
243
+ baselineTrials.push(bRes);
244
+ const sRes = await executeLoadTrial(shieldPort, reqCount, concurrency);
245
+ shieldTrials.push(sRes);
246
+ }
247
+
248
+ const baselineAgg = aggregateTrials(baselineTrials);
249
+ const shieldAgg = aggregateTrials(shieldTrials);
250
+
251
+ const latencyOverhead = Number((shieldAgg.latency.avg - baselineAgg.latency.avg).toFixed(2));
252
+ const throughputImpact = Number((((baselineAgg.throughput - shieldAgg.throughput) / baselineAgg.throughput) * 100).toFixed(1));
253
+
254
+ const entry = {
255
+ requests: reqCount,
256
+ concurrency,
257
+ trials: REPEATED_TRIALS,
258
+ baseline: baselineAgg,
259
+ shield: shieldAgg,
260
+ comparison: {
261
+ throughputImpactPercent: throughputImpact,
262
+ latencyOverheadAvgMs: latencyOverhead,
263
+ p95DeltaMs: Number((shieldAgg.latency.p95 - baselineAgg.latency.p95).toFixed(2)),
264
+ p99DeltaMs: Number((shieldAgg.latency.p99 - baselineAgg.latency.p99).toFixed(2)),
265
+ cpuOverheadPercent: Number((shieldAgg.normalizedCpuPercent - baselineAgg.normalizedCpuPercent).toFixed(1))
266
+ }
267
+ };
268
+
269
+ matrixResults.push(entry);
270
+
271
+ csvRows.push([
272
+ reqCount, concurrency, REPEATED_TRIALS,
273
+ baselineAgg.throughput, shieldAgg.throughput, `${throughputImpact}%`,
274
+ baselineAgg.latency.avg, shieldAgg.latency.avg, latencyOverhead,
275
+ baselineAgg.latency.median, shieldAgg.latency.median,
276
+ baselineAgg.latency.stdDev, shieldAgg.latency.stdDev,
277
+ baselineAgg.latency.p95, shieldAgg.latency.p95,
278
+ baselineAgg.latency.p99, shieldAgg.latency.p99,
279
+ baselineAgg.coresUtilized, shieldAgg.coresUtilized,
280
+ `${baselineAgg.normalizedCpuPercent}%`, `${shieldAgg.normalizedCpuPercent}%`,
281
+ baselineAgg.heapDeltaMb, shieldAgg.heapDeltaMb
282
+ ]);
283
+
284
+ console.log(`Done! [Overhead: +${latencyOverhead}ms, Throughput: ${baselineAgg.throughput} vs ${shieldAgg.throughput} req/s]`);
285
+ }
286
+ }
287
+
288
+ baselineServer.close();
289
+ shieldServer.close();
290
+
291
+ console.log("\n================================================================================");
292
+ console.log(" SCIENTIFIC PERFORMANCE EVALUATION SUMMARY MATRIX ");
293
+ console.log("================================================================================");
294
+ console.log("Workload | Throughput (Req/s) | Avg Latency (ms) | p95 Latency (ms) | Host CPU (%) | Cores Utilized");
295
+ console.log("Reqs @ Concurr | Baseline | Shield | Baseline | Shield | Baseline | Shield | Base | Shield | Base | Shield");
296
+ console.log("---------------|------------|----------|-----------|-----------|-----------|----------|------|--------|-------|-------");
297
+
298
+ for (const m of matrixResults) {
299
+ const wl = `${m.requests} @ c=${m.concurrency}`.padEnd(14);
300
+ const bt = String(m.baseline.throughput).padEnd(10);
301
+ const st = String(m.shield.throughput).padEnd(8);
302
+ const bl = String(m.baseline.latency.avg + ' ms').padEnd(9);
303
+ const sl = String(m.shield.latency.avg + ' ms').padEnd(9);
304
+ const bp95 = String(m.baseline.latency.p95 + ' ms').padEnd(9);
305
+ const sp95 = String(m.shield.latency.p95 + ' ms').padEnd(8);
306
+ const bcpu = String(m.baseline.normalizedCpuPercent + '%').padEnd(4);
307
+ const scpu = String(m.shield.normalizedCpuPercent + '%').padEnd(6);
308
+ const bcores = String(m.baseline.coresUtilized).padEnd(5);
309
+ const scores = String(m.shield.coresUtilized).padEnd(6);
310
+ console.log(`${wl} | ${bt} | ${st} | ${bl} | ${sl} | ${bp95} | ${sp95} | ${bcpu} | ${scpu} | ${bcores} | ${scores}`);
311
+ }
312
+
313
+ console.log("================================================================================");
314
+ console.log("Note: Host CPU % is normalized against host logical cores (" + (require('os').cpus().length || 1) + " cores).");
315
+ console.log(" Cores Utilized represents total multi-threaded v8/libuv process core saturation.");
316
+
317
+ // Write CSV
318
+ const csvContent = csvRows.map(r => r.join(',')).join('\n');
319
+ fs.writeFileSync(path.join(RESULTS_DIR, 'performance.csv'), csvContent);
320
+
321
+ // Write JSON
322
+ const summaryJson = {
323
+ timestamp: new Date().toISOString(),
324
+ configurations: {
325
+ requests: MATRIX_REQUESTS,
326
+ concurrency: MATRIX_CONCURRENCY,
327
+ trials: REPEATED_TRIALS,
328
+ warmupRequests: WARMUP_REQUESTS
329
+ },
330
+ results: matrixResults
331
+ };
332
+
333
+ fs.writeFileSync(path.join(RESULTS_DIR, 'comparison.json'), JSON.stringify(summaryJson, null, 2));
334
+
335
+ console.log(`Reports saved to:`);
336
+ console.log(` - results/performance.csv`);
337
+ console.log(` - results/comparison.json\n`);
338
+
339
+ return summaryJson;
340
+ }
341
+
342
+ if (require.main === module) {
343
+ runPerformanceBenchmark().catch(console.error);
344
+ }
345
+
346
+ module.exports = { runPerformanceBenchmark };
@@ -0,0 +1,241 @@
1
+ 'use strict';
2
+
3
+ const http = require('http');
4
+ const express = require('express');
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+ const { createShield } = require('../src/index.js');
8
+
9
+ const DATASET_PATH = path.join(__dirname, 'datasets', 'attacks.json');
10
+ const RESULTS_DIR = path.join(__dirname, '..', 'results');
11
+
12
+ if (!fs.existsSync(RESULTS_DIR)) fs.mkdirSync(RESULTS_DIR, { recursive: true });
13
+
14
+ function startUnprotectedServer() {
15
+ const app = express();
16
+ app.use(express.json());
17
+ app.use(express.text({ type: ['text/*', 'application/xml', '*/*'] }));
18
+
19
+ app.use((req, res) => {
20
+ res.status(200).json({
21
+ status: 'success',
22
+ receivedUrl: req.url,
23
+ method: req.method,
24
+ body: req.body || null
25
+ });
26
+ });
27
+
28
+ // Express error handler
29
+ app.use((err, req, res, next) => {
30
+ res.status(400).json({ error: 'invalid_payload' });
31
+ });
32
+
33
+ const server = app.listen(0);
34
+ const port = server.address().port;
35
+ return { server, port };
36
+ }
37
+
38
+ function startShieldServer() {
39
+ const app = express();
40
+ app.use(express.json());
41
+ app.use(express.text({ type: ['text/*', 'application/xml', '*/*'] }));
42
+
43
+ const shield = createShield({
44
+ appName: 'security-eval-shield',
45
+ security: 'medium',
46
+ logger: false,
47
+ dashboard: { enabled: false },
48
+ block: { threshold: 60, enabled: true },
49
+ rateLimit: { max: 5000, windowMs: 60000 },
50
+ storage: { mode: 'memory' }
51
+ });
52
+
53
+ app.use(shield.middleware);
54
+
55
+ app.use((req, res) => {
56
+ res.status(200).json({
57
+ status: 'success',
58
+ receivedUrl: req.url,
59
+ method: req.method,
60
+ body: req.body || null
61
+ });
62
+ });
63
+
64
+ // Express error handler
65
+ app.use((err, req, res, next) => {
66
+ res.status(400).json({ error: 'invalid_payload' });
67
+ });
68
+
69
+ const server = app.listen(0);
70
+ const port = server.address().port;
71
+ return { server, port, shield };
72
+ }
73
+
74
+ function sendAttack(port, testCase) {
75
+ return new Promise((resolve) => {
76
+ const isStringBody = typeof testCase.body === 'string';
77
+ const bodyData = isStringBody ? testCase.body : (testCase.body ? JSON.stringify(testCase.body) : null);
78
+ const headers = Object.assign({}, testCase.headers || { 'user-agent': 'SecurityBaselineTester/1.0' });
79
+
80
+ if (bodyData) {
81
+ headers['content-type'] = isStringBody ? 'application/xml' : 'application/json';
82
+ headers['content-length'] = Buffer.byteLength(bodyData);
83
+ }
84
+
85
+ let parsedPath = testCase.url;
86
+ try {
87
+ parsedPath = encodeURI(testCase.url);
88
+ } catch (_) {
89
+ parsedPath = testCase.url;
90
+ }
91
+
92
+ const options = {
93
+ hostname: '127.0.0.1',
94
+ port,
95
+ path: parsedPath,
96
+ method: testCase.method || 'GET',
97
+ headers,
98
+ timeout: 3000
99
+ };
100
+
101
+ let req;
102
+ try {
103
+ req = http.request(options, (res) => {
104
+ let data = '';
105
+ res.on('data', chunk => { data += chunk; });
106
+ res.on('end', () => {
107
+ const isBlocked = res.statusCode === 403 || res.statusCode === 429;
108
+ resolve({
109
+ id: testCase.id,
110
+ category: testCase.category,
111
+ statusCode: res.statusCode,
112
+ mitigated: isBlocked
113
+ });
114
+ });
115
+ });
116
+ } catch (_) {
117
+ return resolve({
118
+ id: testCase.id,
119
+ category: testCase.category,
120
+ statusCode: 400,
121
+ mitigated: false
122
+ });
123
+ }
124
+
125
+ req.on('error', () => {
126
+ resolve({
127
+ id: testCase.id,
128
+ category: testCase.category,
129
+ statusCode: 500,
130
+ mitigated: false
131
+ });
132
+ });
133
+
134
+ if (bodyData) req.write(bodyData);
135
+ req.end();
136
+ });
137
+ }
138
+
139
+ async function runSecurityBaselineComparison() {
140
+ console.log("================================================================================");
141
+ console.log(" iri-shield Security Baseline Comparison (Vanilla Express vs. Express + Shield)");
142
+ console.log("================================================================================");
143
+
144
+ const attacks = JSON.parse(fs.readFileSync(DATASET_PATH, 'utf8'));
145
+ console.log(`Evaluating ${attacks.length} attack vectors across both server configurations...\n`);
146
+
147
+ const unprot = startUnprotectedServer();
148
+ const shld = startShieldServer();
149
+
150
+ const baselineResults = [];
151
+ const shieldResults = [];
152
+
153
+ for (const atk of attacks) {
154
+ const bRes = await sendAttack(unprot.port, atk);
155
+ baselineResults.push(bRes);
156
+ const sRes = await sendAttack(shld.port, atk);
157
+ shieldResults.push(sRes);
158
+ }
159
+
160
+ unprot.server.close();
161
+ shld.server.close();
162
+
163
+ // Aggregate category-wise comparison
164
+ const categories = {};
165
+ for (let i = 0; i < attacks.length; i++) {
166
+ const atk = attacks[i];
167
+ const b = baselineResults[i];
168
+ const s = shieldResults[i];
169
+
170
+ if (!categories[atk.category]) {
171
+ categories[atk.category] = { total: 0, baselineMitigated: 0, shieldMitigated: 0 };
172
+ }
173
+ categories[atk.category].total += 1;
174
+ if (b.mitigated) categories[atk.category].baselineMitigated += 1;
175
+ if (s.mitigated) categories[atk.category].shieldMitigated += 1;
176
+ }
177
+
178
+ console.log("================================================================================");
179
+ console.log(" SECURITY BASELINE COMPARISON MATRIX ");
180
+ console.log("================================================================================");
181
+ console.log(`Threat Category | Test Cases | Vanilla Express | Express + iri-shield | Delta (Defense Gain) `);
182
+ console.log(`---------------------|------------|-----------------|----------------------|----------------------`);
183
+
184
+ const csvRows = [
185
+ ['Threat_Category', 'Total_Cases', 'Vanilla_Express_Mitigated_Pct', 'iri_shield_Mitigated_Pct', 'Defense_Gain_Pct']
186
+ ];
187
+
188
+ let totalAttacks = attacks.length;
189
+ let totalBaseMitigated = 0;
190
+ let totalShieldMitigated = 0;
191
+
192
+ for (const [cat, stat] of Object.entries(categories)) {
193
+ const basePct = Number(((stat.baselineMitigated / stat.total) * 100).toFixed(1));
194
+ const shieldPct = Number(((stat.shieldMitigated / stat.total) * 100).toFixed(1));
195
+ const gainPct = Number((shieldPct - basePct).toFixed(1));
196
+
197
+ totalBaseMitigated += stat.baselineMitigated;
198
+ totalShieldMitigated += stat.shieldMitigated;
199
+
200
+ console.log(
201
+ `${cat.padEnd(20)} | ${String(stat.total).padEnd(10)} | ${String(basePct + '% (' + stat.baselineMitigated + ')').padEnd(15)} | ${String(shieldPct + '% (' + stat.shieldMitigated + ')').padEnd(20)} | +${gainPct}%`
202
+ );
203
+
204
+ csvRows.push([cat, stat.total, `${basePct}%`, `${shieldPct}%`, `+${gainPct}%`]);
205
+ }
206
+
207
+ const totalBasePct = Number(((totalBaseMitigated / totalAttacks) * 100).toFixed(1));
208
+ const totalShieldPct = Number(((totalShieldMitigated / totalAttacks) * 100).toFixed(1));
209
+ const totalGainPct = Number((totalShieldPct - totalBasePct).toFixed(1));
210
+
211
+ console.log("================================================================================");
212
+ console.log(`OVERALL COMPARISON | ${String(totalAttacks).padEnd(10)} | ${String(totalBasePct + '% (' + totalBaseMitigated + ')').padEnd(15)} | ${String(totalShieldPct + '% (' + totalShieldMitigated + ')').padEnd(20)} | +${totalGainPct}%`);
213
+ console.log("================================================================================");
214
+
215
+ // Write CSV
216
+ const csvContent = csvRows.map(r => r.join(',')).join('\n');
217
+ fs.writeFileSync(path.join(RESULTS_DIR, 'security_baseline_comparison.csv'), csvContent);
218
+
219
+ const jsonSummary = {
220
+ timestamp: new Date().toISOString(),
221
+ totalAttacks,
222
+ vanillaExpress: { totalMitigated: totalBaseMitigated, mitigationRatePct: totalBasePct },
223
+ iriShield: { totalMitigated: totalShieldMitigated, mitigationRatePct: totalShieldPct },
224
+ netDefenseGainPct: totalGainPct,
225
+ categories
226
+ };
227
+
228
+ fs.writeFileSync(path.join(RESULTS_DIR, 'security_baseline_comparison.json'), JSON.stringify(jsonSummary, null, 2));
229
+
230
+ console.log(`Reports saved to:`);
231
+ console.log(` - results/security_baseline_comparison.csv`);
232
+ console.log(` - results/security_baseline_comparison.json\n`);
233
+
234
+ return jsonSummary;
235
+ }
236
+
237
+ if (require.main === module) {
238
+ runSecurityBaselineComparison().catch(console.error);
239
+ }
240
+
241
+ module.exports = { runSecurityBaselineComparison };