mdefender-pro 1.2.6 → 1.2.7

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 (2) hide show
  1. package/client.mjs +78 -3
  2. package/package.json +1 -1
package/client.mjs CHANGED
@@ -481,6 +481,20 @@ export function initWaf(options = {}) {
481
481
  const backendUrl = (options.backendUrl || 'http://localhost:8000').replace(/\/+$/, '');
482
482
  const domain = options.domain || window.location.hostname || 'localhost';
483
483
 
484
+ // 0. Auto-inspect current page URL on load (e.g. ?q=<script> or ?id=1 UNION SELECT)
485
+ try {
486
+ const currentUrlParams = (window.location.search || '') + ' ' + (window.location.hash || '');
487
+ if (currentUrlParams.trim()) {
488
+ const normUrl = normalizeInput(currentUrlParams);
489
+ for (const pattern of ATTACK_PATTERNS) {
490
+ if (pattern.regex.test(normUrl)) {
491
+ renderOfficialBlockPage(pattern.type, null, domain);
492
+ return;
493
+ }
494
+ }
495
+ }
496
+ } catch (e) {}
497
+
484
498
  // 1. Wrap window.fetch for outgoing requests
485
499
  const originalFetch = window.fetch;
486
500
  window.fetch = async (input, init, ...args) => {
@@ -491,6 +505,7 @@ export function initWaf(options = {}) {
491
505
  else if (url.includes('#')) targetParamStr = url.substring(url.indexOf('#'));
492
506
  } catch (e) {}
493
507
 
508
+ // Inspect URL
494
509
  if (targetParamStr) {
495
510
  const normParams = normalizeInput(targetParamStr);
496
511
  for (const pattern of ATTACK_PATTERNS) {
@@ -501,14 +516,36 @@ export function initWaf(options = {}) {
501
516
  }
502
517
  }
503
518
 
519
+ // Inspect Body if POST/PUT
520
+ if (init && init.body) {
521
+ try {
522
+ const bodyStr = typeof init.body === 'string' ? init.body : JSON.stringify(init.body);
523
+ const normBody = normalizeInput(bodyStr);
524
+ for (const pattern of ATTACK_PATTERNS) {
525
+ if (pattern.regex.test(normBody)) {
526
+ renderOfficialBlockPage(pattern.type, null, domain);
527
+ throw new Error(`[MDefender WAF] Outgoing payload attack blocked: ${pattern.type}`);
528
+ }
529
+ }
530
+ } catch (e) {}
531
+ }
532
+
504
533
  try {
505
534
  const response = await originalFetch(input, init, ...args);
506
535
  if (response.status === 403) {
507
536
  const clone = response.clone();
508
537
  try {
509
538
  const text = await clone.text();
510
- if (text.includes('403') && (text.includes('MDefender') || text.includes('Access Denied'))) {
511
- renderOfficialBlockPage('Cross-Site Scripting (XSS)', null, domain);
539
+ const statusHeader = response.headers.get('x-mdefender-status');
540
+ const attackHeader = response.headers.get('x-mdefender-attack-type');
541
+ const refHeader = response.headers.get('x-mdefender-ref');
542
+ if (statusHeader === 'blocked' || text.includes('403') || text.includes('MDefender') || text.includes('Access Denied')) {
543
+ if (text.includes('<!DOCTYPE html>') || text.includes('<html')) {
544
+ try { window.stop(); } catch (e) {}
545
+ document.documentElement.innerHTML = text;
546
+ } else {
547
+ renderOfficialBlockPage(attackHeader || 'Cross-Site Scripting (XSS)', refHeader, domain);
548
+ }
512
549
  }
513
550
  } catch (e) {}
514
551
  }
@@ -518,7 +555,7 @@ export function initWaf(options = {}) {
518
555
  }
519
556
  };
520
557
 
521
- // 2. Wrap XMLHttpRequest
558
+ // 2. Wrap XMLHttpRequest (Used by Axios, Redux Toolkit Query, etc.)
522
559
  const originalOpen = XMLHttpRequest.prototype.open;
523
560
  XMLHttpRequest.prototype.open = function (method, url, ...rest) {
524
561
  let targetParamStr = '';
@@ -538,6 +575,44 @@ export function initWaf(options = {}) {
538
575
  }
539
576
  return originalOpen.apply(this, [method, url, ...rest]);
540
577
  };
578
+
579
+ const originalSend = XMLHttpRequest.prototype.send;
580
+ XMLHttpRequest.prototype.send = function (body) {
581
+ if (body) {
582
+ try {
583
+ const bodyStr = typeof body === 'string' ? body : JSON.stringify(body);
584
+ const normBody = normalizeInput(bodyStr);
585
+ for (const pattern of ATTACK_PATTERNS) {
586
+ if (pattern.regex.test(normBody)) {
587
+ renderOfficialBlockPage(pattern.type, null, domain);
588
+ throw new Error(`[MDefender WAF] XHR payload attack blocked: ${pattern.type}`);
589
+ }
590
+ }
591
+ } catch (e) {}
592
+ }
593
+
594
+ // Intercept 403 Forbidden responses from backend WAF
595
+ this.addEventListener('load', function () {
596
+ if (this.status === 403) {
597
+ try {
598
+ const resp = this.responseText || '';
599
+ const statusHeader = this.getResponseHeader('X-MDefender-Status');
600
+ const attackHeader = this.getResponseHeader('X-MDefender-Attack-Type');
601
+ const refHeader = this.getResponseHeader('X-MDefender-Ref');
602
+ if (statusHeader === 'blocked' || resp.includes('403') || resp.includes('MDefender') || resp.includes('Access Denied')) {
603
+ if (resp.includes('<!DOCTYPE html>') || resp.includes('<html')) {
604
+ try { window.stop(); } catch (e) {}
605
+ document.documentElement.innerHTML = resp;
606
+ } else {
607
+ renderOfficialBlockPage(attackHeader || 'Security Rule Violation', refHeader, domain);
608
+ }
609
+ }
610
+ } catch (e) {}
611
+ }
612
+ });
613
+
614
+ return originalSend.apply(this, arguments);
615
+ };
541
616
  }
542
617
 
543
618
  export default { initWaf, renderOfficialBlockPage };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mdefender-pro",
3
- "version": "1.2.6",
3
+ "version": "1.2.7",
4
4
  "description": "MDefender Pro - Web Application Firewall middleware for Node.js/Express. Protects against XSS, SQLi, CSRF, and other OWASP Top 10 attacks.",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",