mdefender-pro 1.2.3 → 1.2.5

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/auto.js ADDED
@@ -0,0 +1,62 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * MDefender Pro Zero-Code Auto-Instrumentation Agent.
5
+ *
6
+ * Automatically hooks into Express and Node.js native http/https servers
7
+ * to provide full WAF protection without changing a single line of application code.
8
+ */
9
+
10
+ const http = require('http');
11
+ const https = require('https');
12
+ const path = require('path');
13
+ const fs = require('fs');
14
+
15
+ let isHooked = false;
16
+
17
+ function autoHook() {
18
+ if (isHooked) return;
19
+ isHooked = true;
20
+
21
+ const mdefender = require('./index');
22
+
23
+ // Load environment variables from .env if present
24
+ const envPath = path.resolve(process.cwd(), '.env');
25
+ if (fs.existsSync(envPath)) {
26
+ try {
27
+ const dotenv = require('dotenv');
28
+ dotenv.config({ path: envPath });
29
+ } catch (e) {}
30
+ }
31
+
32
+ const activeConfig = mdefender.loadConfig();
33
+
34
+ if (activeConfig.mode === 'off') {
35
+ return;
36
+ }
37
+
38
+ console.log(`\x1b[36m\x1b[1m🛡️ [MDefender Pro] Zero-Code WAF Protection Active\x1b[0m`);
39
+ console.log(`\x1b[90m Inspection Endpoint: ${activeConfig.apiEndpoint}\x1b[0m`);
40
+ console.log(`\x1b[90m Protected Domain : ${activeConfig.domain || 'auto-detect'}\x1b[0m`);
41
+ console.log(`\x1b[90m Defense Mode : ${activeConfig.mode.toUpperCase()}\x1b[0m\n`);
42
+
43
+ // 1. Hook Express if loaded
44
+ try {
45
+ const express = require('express');
46
+ if (express && express.application && typeof express.application.lazyrouter === 'function') {
47
+ const origLazyRouter = express.application.lazyrouter;
48
+ express.application.lazyrouter = function () {
49
+ origLazyRouter.apply(this, arguments);
50
+ if (!this._mdefenderAttached && this._router) {
51
+ this._mdefenderAttached = true;
52
+ this._router.use(mdefender(activeConfig));
53
+ }
54
+ };
55
+ }
56
+ } catch (e) {}
57
+ }
58
+
59
+ // Automatically activate when loaded via -r / --require
60
+ autoHook();
61
+
62
+ module.exports = { autoHook };
package/bin/mdefender.js CHANGED
@@ -3,15 +3,38 @@
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
5
  const readline = require('readline');
6
+ const { spawn } = require('child_process');
6
7
 
7
8
  const args = process.argv.slice(2);
8
- const command = args[0] || 'init';
9
+ const command = args[0] || 'help';
9
10
 
10
- console.log(`\n\x1b[36m\x1b[1m========================================\x1b[0m`);
11
- console.log(`\x1b[36m\x1b[1m MDefender Pro - Quick Setup CLI \x1b[0m`);
12
- console.log(`\x1b[36m\x1b[1m========================================\x1b[0m\n`);
11
+ console.log(`\n\x1b[36m\x1b[1m====================================================\x1b[0m`);
12
+ console.log(`\x1b[36m\x1b[1m 🛡️ MDefender Pro — Zero-Code WAF CLI \x1b[0m`);
13
+ console.log(`\x1b[36m\x1b[1m====================================================\x1b[0m\n`);
13
14
 
14
- if (command === 'init' || command === 'setup') {
15
+ if (command === 'run' || command === 'start') {
16
+ const targetScript = args[1] || 'index.js';
17
+ const scriptPath = path.resolve(process.cwd(), targetScript);
18
+
19
+ if (!fs.existsSync(scriptPath)) {
20
+ console.error(`\x1b[31m[!] Target file not found: ${targetScript}\x1b[0m`);
21
+ process.exit(1);
22
+ }
23
+
24
+ console.log(`\x1b[32m[+] Launching "${targetScript}" with zero-code MDefender WAF protection...\x1b[0m\n`);
25
+
26
+ const preloadModule = path.resolve(__dirname, '..', 'auto.js');
27
+ const child = spawn(process.execPath, ['-r', preloadModule, scriptPath], {
28
+ stdio: 'inherit',
29
+ env: process.env,
30
+ cwd: process.cwd(),
31
+ });
32
+
33
+ child.on('exit', (code) => {
34
+ process.exit(code || 0);
35
+ });
36
+
37
+ } else if (command === 'init' || command === 'setup') {
15
38
  const targetConfig = path.join(process.cwd(), 'mdefender.config.js');
16
39
 
17
40
  if (fs.existsSync(targetConfig)) {
@@ -25,56 +48,84 @@ if (command === 'init' || command === 'setup') {
25
48
  });
26
49
 
27
50
  rl.question('\x1b[32mEnter your MDefender Pro API Key (from Dashboard): \x1b[0m', (apiKey) => {
28
- rl.question('\x1b[32mEnter your Website Domain (e.g. myapp.com or default): \x1b[0m', (domain) => {
29
- const trimmedKey = (apiKey || 'YOUR_API_KEY_HERE').trim();
30
- const trimmedDomain = (domain || 'default').trim();
51
+ rl.question('\x1b[32mEnter your Website Domain (e.g. localhost or mysite.com): \x1b[0m', (domain) => {
52
+ rl.question('\x1b[32mEnter MDefender API Endpoint (default http://localhost:8000): \x1b[0m', (endpoint) => {
53
+ const trimmedKey = (apiKey || 'YOUR_API_KEY_HERE').trim();
54
+ const trimmedDomain = (domain || 'localhost').trim();
55
+ const trimmedEndpoint = (endpoint || 'http://localhost:8000').trim();
31
56
 
32
- const configTemplate = `/**
57
+ const configTemplate = `/**
33
58
  * MDefender Pro Web Application Firewall Configuration
34
- * Generated automatically by @mdefender/pro CLI
35
59
  */
36
60
  module.exports = {
37
61
  // Your Secret Website API Key from MDefender Dashboard
38
62
  apiKey: process.env.MDEFENDER_API_KEY || '${trimmedKey}',
39
63
 
40
64
  // Domain registered in MDefender Pro
41
- domain: '${trimmedDomain}',
65
+ domain: process.env.MDEFENDER_DOMAIN || '${trimmedDomain}',
42
66
 
43
- // Cloud / Self-hosted Inspection Endpoint
44
- apiEndpoint: process.env.MDEFENDER_API_ENDPOINT || 'https://mdefender-pro-6e3r.onrender.com',
67
+ // Inspection Backend Endpoint
68
+ apiEndpoint: process.env.MDEFENDER_API_ENDPOINT || '${trimmedEndpoint}',
45
69
 
46
- // Mode: 'block' (active defense), 'monitor' (log-only), or 'off'
70
+ // Defense Mode: 'block' (active protection), 'monitor' (log-only), or 'off'
47
71
  mode: 'block',
48
72
 
49
- // Request timeout for cloud inspection in milliseconds (fails open safely)
50
- timeout: 3000,
73
+ // Request timeout in ms (fails open safely if cloud unreachable)
74
+ timeout: 5000,
51
75
 
52
- // Paths to bypass from inspection (e.g. static assets)
76
+ // Paths to bypass from inspection
53
77
  skipPaths: ['/favicon.ico', '/robots.txt', '/static', '/assets', '/health'],
54
78
 
55
- // HTTP methods to bypass
56
- skipMethods: ['OPTIONS'],
57
-
58
79
  // Log blocked attacks in console
59
80
  logBlocked: true
60
81
  };
61
82
  `;
62
83
 
63
- fs.writeFileSync(targetConfig, configTemplate, 'utf8');
64
- console.log(`\n\x1b[32m[+] Created mdefender.config.js successfully!\x1b[0m`);
65
- console.log(`\n\x1b[36mHow to use in your Express app:\x1b[0m`);
66
- console.log(`\x1b[90m---------------------------------------------------\x1b[0m`);
67
- console.log(` const express = require('express');`);
68
- console.log(` const mdefender = require('mdefender-pro');`);
69
- console.log(` `);
70
- console.log(` const app = express();`);
71
- console.log(` app.use(express.json());`);
72
- console.log(` app.use(mdefender()); // Connects WAF with bundled block page`);
73
- console.log(`\x1b[90m---------------------------------------------------\x1b[0m\n`);
74
- rl.close();
84
+ fs.writeFileSync(targetConfig, configTemplate, 'utf8');
85
+ console.log(`\n\x1b[32m[+] Created mdefender.config.js successfully!\x1b[0m`);
86
+ console.log(`\n\x1b[36mZero-Code Run Command:\x1b[0m`);
87
+ console.log(`\x1b[90m---------------------------------------------------\x1b[0m`);
88
+ console.log(` npx mdefender-pro run index.js`);
89
+ console.log(` OR`);
90
+ console.log(` node -r mdefender-pro index.js`);
91
+ console.log(`\x1b[90m---------------------------------------------------\x1b[0m\n`);
92
+ rl.close();
93
+ });
75
94
  });
76
95
  });
96
+
97
+ } else if (command === 'test') {
98
+ const mdefender = require('../index');
99
+ const config = mdefender.loadConfig();
100
+
101
+ console.log(`\x1b[33mTesting WAF connection to ${config.apiEndpoint}...\x1b[0m`);
102
+
103
+ const testPayload = {
104
+ domain: config.domain || 'localhost',
105
+ request: {
106
+ method: 'GET',
107
+ url: '/test-probe?id=1%20UNION/**/SELECT%20password%20FROM%20users',
108
+ ip: '127.0.0.1',
109
+ headers: { 'User-Agent': 'MDefender-Test-Probe' },
110
+ body: ''
111
+ }
112
+ };
113
+
114
+ mdefender.sendAnalyzeRequest(config.apiEndpoint, config.apiKey, testPayload, 5000)
115
+ .then((res) => {
116
+ console.log(`\x1b[32m[+] WAF Connection Successful!\x1b[0m`);
117
+ console.log(` Status Code : ${res.statusCode}`);
118
+ console.log(` Decision : \x1b[31m${res.data?.decision || res.data?.status}\x1b[0m`);
119
+ console.log(` Attack Type : ${res.data?.attack_type || res.data?.reason}`);
120
+ console.log(` Reference ID: ${res.data?.reference_id}\n`);
121
+ })
122
+ .catch((err) => {
123
+ console.error(`\x1b[31m[-] WAF Connection Failed: ${err.message}\x1b[0m\n`);
124
+ });
125
+
77
126
  } else {
78
- console.log(`Usage: npx mdefender-pro init`);
79
- process.exit(0);
127
+ console.log(`Commands:`);
128
+ console.log(` \x1b[32mnpx mdefender-pro run <file>\x1b[0m Run any Node.js server with ZERO code changes`);
129
+ console.log(` \x1b[32mnpx mdefender-pro init\x1b[0m Generate mdefender.config.js interactively`);
130
+ console.log(` \x1b[32mnpx mdefender-pro test\x1b[0m Test connection to MDefender inspection engine\n`);
80
131
  }
package/block-page.html CHANGED
@@ -43,10 +43,10 @@
43
43
  box-shadow: 0 8px 25px -4px rgba(0, 0, 0, 0.04), 0 4px 6px -2px rgba(0, 0, 0, 0.02);
44
44
  padding: 24px 28px;
45
45
  text-align: center;
46
- animation: fadeIn 0.35s cubic-bezier(0.16, 1, 0.3, 1) forwards;
46
+ animation: fadeIn 0.2s cubic-bezier(0.16, 1, 0.3, 1) forwards;
47
47
  }
48
48
  @keyframes fadeIn {
49
- from { opacity: 0; transform: translateY(10px); }
49
+ from { opacity: 0; transform: translateY(6px); }
50
50
  to { opacity: 1; transform: translateY(0); }
51
51
  }
52
52
  .logo-wrap {
@@ -102,7 +102,6 @@
102
102
  color: var(--text-secondary);
103
103
  margin-bottom: 12px;
104
104
  }
105
-
106
105
  .table-container {
107
106
  border: 1px solid var(--border-color);
108
107
  border-radius: 7px;
@@ -140,7 +139,6 @@
140
139
  padding: 7px 12px;
141
140
  vertical-align: middle;
142
141
  }
143
-
144
142
  .threat-row td {
145
143
  background-color: #fff5f5 !important;
146
144
  color: #991b1b !important;
@@ -168,7 +166,6 @@
168
166
  text-decoration: none;
169
167
  white-space: nowrap;
170
168
  }
171
-
172
169
  .ip-wrap {
173
170
  display: flex;
174
171
  align-items: center;
@@ -178,138 +175,93 @@
178
175
  border: 1px solid rgba(0, 0, 0, 0.08);
179
176
  border-radius: 2px;
180
177
  box-shadow: 0 1px 2px rgba(0,0,0,0.05);
181
- display: inline-block;
182
- vertical-align: middle;
183
178
  }
184
-
185
179
  .why-header {
186
180
  font-size: 12.5px;
187
181
  font-weight: 700;
188
182
  color: #0f172a;
189
- margin-top: 10px;
190
- margin-bottom: 3px;
183
+ text-align: left;
184
+ margin-bottom: 4px;
191
185
  }
192
186
  .why-desc {
193
- font-size: 10.5px;
187
+ font-size: 11px;
194
188
  color: var(--text-secondary);
195
189
  line-height: 1.45;
196
- max-width: 560px;
197
- margin: 0 auto 10px;
190
+ text-align: left;
191
+ margin-bottom: 12px;
198
192
  }
199
-
200
193
  .ref-box {
201
- background: #f8fafc;
194
+ background: #f1f5f9;
202
195
  border: 1px dashed var(--border-color);
203
196
  border-radius: 5px;
204
- padding: 6px 14px;
197
+ padding: 7px;
205
198
  font-family: var(--font-mono);
206
- font-size: 11.5px;
199
+ font-size: 11px;
207
200
  font-weight: 700;
208
- color: #0f172a;
209
- display: inline-block;
210
- margin-bottom: 12px;
211
- letter-spacing: 0.5px;
201
+ color: #334155;
202
+ letter-spacing: 0.6px;
203
+ margin-bottom: 14px;
212
204
  cursor: pointer;
213
- transition: all 0.2s ease;
205
+ transition: all 0.2s;
214
206
  }
215
207
  .ref-box:hover {
216
- background: #f1f5f9;
217
- border-color: #94a3b8;
218
- transform: scale(1.01);
208
+ border-color: var(--brand-1);
209
+ color: var(--brand-1);
210
+ background: #eef2ff;
219
211
  }
220
-
221
212
  .actions {
222
213
  display: flex;
214
+ align-items: center;
223
215
  justify-content: center;
224
- gap: 8px;
225
- margin-bottom: 8px;
216
+ gap: 10px;
217
+ margin-bottom: 12px;
226
218
  flex-wrap: wrap;
227
219
  }
228
220
  .btn {
229
221
  display: inline-flex;
230
222
  align-items: center;
231
223
  justify-content: center;
232
- padding: 7px 16px;
233
- border-radius: 6px;
234
- font-size: 11.5px;
224
+ gap: 6px;
225
+ font-family: var(--font-main);
226
+ font-size: 11px;
235
227
  font-weight: 600;
228
+ padding: 7px 14px;
229
+ border-radius: 5px;
236
230
  text-decoration: none;
231
+ transition: all 0.15s ease;
237
232
  cursor: pointer;
238
- transition: all 0.2s ease;
239
- gap: 6px;
240
- box-shadow: 0 1px 3px rgba(0,0,0,0.02);
241
- border: none;
242
233
  }
243
234
  .btn-primary {
244
- background-color: #1e3a8a;
235
+ background-color: #0f172a;
245
236
  color: #ffffff;
237
+ border: 1px solid #0f172a;
246
238
  }
247
239
  .btn-primary:hover {
248
- background-color: #172554;
249
- transform: translateY(-1px);
250
- box-shadow: 0 3px 5px rgba(0,0,0,0.06);
251
- color: #ffffff;
240
+ background-color: #1e293b;
241
+ border-color: #1e293b;
242
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
252
243
  }
253
244
  .btn-secondary {
254
245
  background-color: #ffffff;
255
- color: #475569;
246
+ color: #334155;
256
247
  border: 1px solid var(--border-color);
257
248
  }
258
249
  .btn-secondary:hover {
259
250
  background-color: #f8fafc;
260
- color: #0f172a;
261
251
  border-color: #94a3b8;
262
- transform: translateY(-1px);
263
- box-shadow: 0 3px 5px rgba(0,0,0,0.04);
252
+ color: #0f172a;
264
253
  }
265
-
266
254
  .footer {
267
- font-size: 10.5px;
255
+ font-size: 10px;
268
256
  color: var(--text-muted);
269
- border-top: 1px solid var(--border-color);
270
- padding-top: 12px;
271
- margin-top: 10px;
272
- }
273
-
274
- @media (max-width: 768px) {
275
- .container { padding: 24px; }
276
- .details-table th, .details-table td {
277
- display: block;
278
- width: 100%;
279
- border-right: none;
280
- }
281
- .details-table th {
282
- background-color: #f8fafc;
283
- padding-bottom: 6px;
284
- }
285
- .details-table td {
286
- padding-top: 6px;
287
- border-bottom: none;
288
- }
289
- .details-table tr {
290
- border-bottom: 1px solid var(--border-color);
291
- display: block;
292
- }
293
- .threat-cell-content {
294
- flex-direction: column;
295
- align-items: flex-start;
296
- gap: 8px;
297
- }
298
- .actions {
299
- flex-direction: column;
300
- width: 100%;
301
- max-width: 400px;
302
- margin: 0 auto 12px;
303
- }
304
- .btn {
305
- width: 100%;
306
- }
257
+ border-top: 1px solid #f1f5f9;
258
+ padding-top: 8px;
307
259
  }
308
260
  </style>
309
261
  </head>
310
262
  <body>
311
263
  <div class="container">
312
- <!-- Logo Header -->
264
+ <!-- Shield Logo Header -->
313
265
  <div class="logo-wrap">
314
266
  <svg class="logo-svg" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg">
315
267
  <defs>
@@ -328,11 +280,8 @@
328
280
  <stop offset="0%" stop-color="#1e293b" />
329
281
  <stop offset="100%" stop-color="#0f172a" />
330
282
  </linearGradient>
331
- <filter id="dropShadow" x="-10%" y="-10%" width="120%" height="120%">
332
- <feDropShadow dx="0" dy="4" stdDeviation="4" flood-color="#0f172a" flood-opacity="0.15" />
333
- </filter>
334
283
  </defs>
335
- <path d="M60 10 C85 22 98 25 98 48 C98 75 75 98 60 108 C45 98 22 75 22 48 C22 25 35 22 60 10 Z" fill="url(#shieldBg)" stroke="url(#shieldBorder)" stroke-width="3" filter="url(#dropShadow)" />
284
+ <path d="M60 10 C85 22 98 25 98 48 C98 75 75 98 60 108 C45 98 22 75 22 48 C22 25 35 22 60 10 Z" fill="url(#shieldBg)" stroke="url(#shieldBorder)" stroke-width="3" />
336
285
  <path d="M60 18 C80 28 90 30 90 48 C90 70 70 90 60 98 C50 90 30 70 30 48 C30 30 40 28 60 18 Z" fill="none" stroke="#ffffff" stroke-width="1.5" stroke-opacity="0.8" stroke-dasharray="3 2" />
337
286
  <path d="M60 28 C58 35 52 38 48 38 C44 38 43 42 45 44 C47 46 51 44 51 48 C51 52 46 55 42 53 C38 51 36 54 38 58 C40 62 45 60 48 64 C50 66 48 70 44 72 C48 74 54 75 58 72 C58 68 55 65 57 62 C59 59 63 61 65 58 C67 55 66 52 62 50 C64 45 68 46 70 42 C72 38 67 36 65 32 C63 28 61 25 60 28 Z" fill="url(#dragonColor)" />
338
287
  </svg>
@@ -363,18 +312,18 @@
363
312
  <th>Origin Client IP</th>
364
313
  <td>
365
314
  <div class="ip-wrap">
366
- <img id="flag-img" src="https://flagcdn.com/w40/bd.png" srcset="https://flagcdn.com/w80/bd.png 2x" width="20" height="15" alt="Flag" class="flag-img" style="display: inline-block;">
367
- <span><span id="client-ip-display" style="font-family: var(--font-mono); font-weight: 700;">{{CLIENT_IP}}</span><span id="geo-text" style="color: #64748b;"> (GeoIP: Dhaka, Bangladesh)</span></span>
315
+ <img id="flag-img" src="https://flagcdn.com/w40/bd.png" width="20" height="15" alt="Flag" class="flag-img" style="display: inline-block;">
316
+ <span><span id="client-ip-display" style="font-family: var(--font-mono); font-weight: 700;">{{CLIENT_IP}}</span><span id="geo-text" style="color: #64748b;"> (GeoIP: BD)</span></span>
368
317
  </div>
369
318
  </td>
370
319
  </tr>
371
320
  <tr>
372
321
  <th>Event Timestamp</th>
373
- <td><span id="utc-time">{{TIMESTAMP}}</span> (Ref: <span id="unix-ref">1788199736</span>)</td>
322
+ <td><span id="utc-time">{{TIMESTAMP}}</span> (Ref: <span id="unix-ref">1788786632</span>)</td>
374
323
  </tr>
375
324
  <tr>
376
325
  <th>Violation Reason</th>
377
- <td>Request blocked by rule ID: <span id="rule-id-val">96565</span> (Ref: {{ATTACK_TYPE}})</td>
326
+ <td>Request blocked by rule ID: <span id="rule-id-val">99236</span> (Ref: {{ATTACK_TYPE}})</td>
378
327
  </tr>
379
328
  <tr>
380
329
  <th>Protocol Details</th>
@@ -387,7 +336,6 @@
387
336
  </table>
388
337
  </div>
389
338
 
390
- <!-- Bottom Explanatory Section -->
391
339
  <h3 class="why-header">Why did this happen?</h3>
392
340
  <p class="why-desc">
393
341
  To maintain system integrity, suspicious requests are automatically analyzed and filtered. If you believe this is a valid corporate action, please share the Reference ID below with your local IT/Security operations. Regular users should clear cache or contact support.
@@ -397,27 +345,14 @@
397
345
  REFERENCE ID: {{REFERENCE_ID}}
398
346
  </div>
399
347
 
400
- <!-- Actions Button Section -->
401
348
  <div class="actions">
402
- <a id="email-link" href="#" class="btn btn-primary">
403
- <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
404
- <path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path>
405
- <polyline points="22,6 12,13 2,6"></polyline>
406
- </svg>
349
+ <a href="#" class="btn btn-primary" onclick="alert('Reference ID: {{REFERENCE_ID}}\\nContact your IT security team.'); return false;">
407
350
  Contact Security Operations
408
351
  </a>
409
352
  <a href="/" class="btn btn-secondary">
410
- <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
411
- <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path>
412
- <polyline points="9 22 9 12 15 12 15 22"></polyline>
413
- </svg>
414
353
  Return to Homepage
415
354
  </a>
416
355
  <button type="button" class="btn btn-secondary" onclick="copyRef()">
417
- <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
418
- <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
419
- <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
420
- </svg>
421
356
  Copy Reference ID
422
357
  </button>
423
358
  </div>
@@ -428,112 +363,23 @@
428
363
  </div>
429
364
 
430
365
  <script>
431
- // Fetch GeoIP and real IP details
432
- const serverDetectedIp = "{{CLIENT_IP}}";
433
- const ipSpan = document.getElementById('client-ip-display');
434
- const flagImg = document.getElementById('flag-img');
435
- const geoText = document.getElementById('geo-text');
436
-
437
- function updateGeoDisplay(ip, countryCode, city, country) {
438
- if (ipSpan && ip) {
439
- ipSpan.innerText = ip;
440
- }
441
- if (flagImg && countryCode) {
442
- const cc = countryCode.toLowerCase();
443
- flagImg.src = `https://flagcdn.com/w40/${cc}.png`;
444
- flagImg.srcset = `https://flagcdn.com/w80/${cc}.png 2x`;
445
- flagImg.alt = countryCode.toUpperCase();
446
- flagImg.style.display = 'inline-block';
447
- }
448
- if (geoText) {
449
- let geoInfo = '';
450
- if (city && country) geoInfo = ` (GeoIP: ${city}, ${country})`;
451
- else if (country) geoInfo = ` (GeoIP: ${country})`;
452
- geoText.innerText = geoInfo;
453
- }
454
- }
455
-
456
- if (serverDetectedIp && serverDetectedIp !== '127.0.0.1' && serverDetectedIp !== '::1' && serverDetectedIp !== 'localhost' && serverDetectedIp !== 'unknown') {
457
- // Real public IP forwarded by server
458
- fetch(`https://ip-api.com/json/${serverDetectedIp}?fields=status,country,city,countryCode,query`)
459
- .then(r => r.json())
460
- .then(data => {
461
- if (data.status === 'success') {
462
- updateGeoDisplay(data.query || serverDetectedIp, data.countryCode, data.city, data.country);
463
- }
464
- }).catch(err => {
465
- updateGeoDisplay(serverDetectedIp, 'BD', 'Dhaka', 'Bangladesh');
466
- });
467
- } else {
468
- // Local / private IP - discover visitor's public GeoIP from browser client
469
- fetch('https://ipapi.co/json/')
470
- .then(r => r.json())
471
- .then(data => {
472
- if (data && data.ip) {
473
- updateGeoDisplay(data.ip, data.country_code, data.city, data.country_name);
474
- }
475
- })
476
- .catch(() => {
477
- fetch('https://api.country.is')
478
- .then(r => r.json())
479
- .then(cData => {
480
- if (cData && cData.country) {
481
- updateGeoDisplay(cData.ip || serverDetectedIp, cData.country, '', cData.country);
482
- }
483
- })
484
- .catch(() => {});
485
- });
486
- }
487
-
488
- // Convert Timestamp to UTC format
489
- const timestampStr = "{{TIMESTAMP}}";
490
- try {
491
- const dt = new Date(timestampStr.replace(' ', 'T'));
492
- if (!isNaN(dt.getTime())) {
493
- document.getElementById('utc-time').innerText = dt.toISOString().replace('T', ' ').slice(0, 19) + ' UTC';
494
- document.getElementById('unix-ref').innerText = Math.floor(dt.getTime() / 1000);
495
- }
496
- } catch (e) {}
497
-
498
- // Dynamically compute rule ID from attack type
499
- const attackType = "{{ATTACK_TYPE}}";
500
- let hash = 0;
501
- for (let i = 0; i < attackType.length; i++) {
502
- hash = attackType.charCodeAt(i) + ((hash << 5) - hash);
503
- }
504
- const ruleId = Math.abs(hash % 10000) + 90000;
505
- const ruleEl = document.getElementById('rule-id-val');
506
- if (ruleEl) ruleEl.innerText = ruleId;
507
-
508
- // Server host lookup
509
- const srvHost = document.getElementById('server-host');
510
- if (srvHost && (!srvHost.innerText || srvHost.innerText === 'Protected Website' || srvHost.innerText.includes('{{'))) {
511
- srvHost.innerText = window.location.hostname || "protected.local";
512
- }
513
-
514
- // Setup email operations mailto link
515
366
  const refId = "{{REFERENCE_ID}}";
516
- const emailLink = document.getElementById('email-link');
517
- if (emailLink) {
518
- emailLink.href = `mailto:security@mdefender-pro.io?subject=WAF Block Reference: ${refId}&body=Hello Security Team,%0D%0A%0D%0AMy request was blocked by the corporate WAF. Details below:%0D%0A- Reference ID: ${refId}%0D%0A- IP: ${clientIp}%0D%0A- Host: ${window.location.host}%0D%0A- Reason: ${attackType}`;
519
- }
520
-
521
367
  function copyRef() {
522
368
  navigator.clipboard.writeText(refId).then(() => {
523
369
  const refBox = document.getElementById('ref-id-box');
524
- const originalHtml = refBox.innerHTML;
525
- refBox.innerHTML = 'COPIED TO CLIPBOARD! ✅';
526
- refBox.style.color = '#10b981';
527
- refBox.style.borderColor = '#10b981';
528
- refBox.style.background = '#ecfdf5';
529
- setTimeout(() => {
530
- refBox.innerHTML = originalHtml;
531
- refBox.style.color = '';
532
- refBox.style.borderColor = '';
533
- refBox.style.background = '';
534
- }, 2000);
535
- }).catch(err => {
536
- console.error('Could not copy Reference ID: ', err);
370
+ if (refBox) {
371
+ const originalHtml = refBox.innerHTML;
372
+ refBox.innerHTML = 'COPIED TO CLIPBOARD! ✅';
373
+ refBox.style.color = '#10b981';
374
+ refBox.style.borderColor = '#10b981';
375
+ refBox.style.background = '#ecfdf5';
376
+ setTimeout(() => {
377
+ refBox.innerHTML = originalHtml;
378
+ refBox.style.color = '';
379
+ refBox.style.borderColor = '';
380
+ refBox.style.background = '';
381
+ }, 2000);
382
+ }
537
383
  });
538
384
  }
539
385
  </script>