mdefender-pro 1.2.0 → 1.2.2

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/index.js CHANGED
@@ -1,40 +1,84 @@
1
1
  'use strict';
2
2
 
3
- const axios = require('axios');
3
+ const http = require('http');
4
+ const https = require('https');
4
5
  const path = require('path');
5
6
  const fs = require('fs');
6
7
  const crypto = require('crypto');
8
+ const { URL } = require('url');
9
+
10
+ const httpAgent = new http.Agent({ keepAlive: true, timeout: 5000 });
11
+ const httpsAgent = new https.Agent({ keepAlive: true, timeout: 5000 });
12
+
13
+ // In-memory cache for bundled block page with mtime check
14
+ let cachedDefaultTemplate = null;
15
+ let cachedTemplateMtime = 0;
16
+
17
+ function getDefaultTemplate() {
18
+ try {
19
+ const templatePath = path.join(__dirname, 'block-page.html');
20
+ if (fs.existsSync(templatePath)) {
21
+ const stats = fs.statSync(templatePath);
22
+ if (cachedDefaultTemplate === null || stats.mtimeMs > cachedTemplateMtime) {
23
+ cachedDefaultTemplate = fs.readFileSync(templatePath, 'utf8');
24
+ cachedTemplateMtime = stats.mtimeMs;
25
+ }
26
+ } else {
27
+ cachedDefaultTemplate = '';
28
+ }
29
+ } catch (e) {
30
+ if (!cachedDefaultTemplate) cachedDefaultTemplate = '';
31
+ }
32
+ return cachedDefaultTemplate;
33
+ }
7
34
 
8
35
  const DEFAULT_CONFIG = {
9
36
  apiKey: '',
10
37
  domain: '',
11
- apiEndpoint: 'https://mdefender-pro.onrender.com',
38
+ apiEndpoint: 'http://127.0.0.1:8000',
12
39
  mode: 'block', // 'block' | 'monitor' | 'off'
13
40
  blockStatusCode: 403,
14
41
  timeout: 5000,
15
42
  maxBodySize: 1024 * 1024, // 1MB
16
43
  logBlocked: true,
17
- customBlockPage: null, // path to custom HTML file
44
+ customBlockPage: null,
18
45
  skipPaths: ['/health', '/favicon.ico'],
19
46
  skipUserAgents: [],
20
47
  skipMethods: [],
21
- headers: true, // forward original headers
22
- onError: 'allow', // 'allow' | 'block' - what to do if API is unreachable
48
+ headers: true,
49
+ onError: 'allow', // 'allow' | 'block'
23
50
  };
24
51
 
25
52
  function loadConfig(overrides = {}) {
26
53
  let fileConfig = {};
27
54
 
55
+ // Try config-loader
56
+ try {
57
+ const configLoader = require('./config-loader');
58
+ const loaded = configLoader.load();
59
+ if (loaded) fileConfig = loaded;
60
+ } catch (e) {}
61
+
28
62
  // Try mdefender.config.js
29
- const jsPath = path.resolve(process.cwd(), 'mdefender.config.js');
30
- if (fs.existsSync(jsPath)) {
31
- fileConfig = require(jsPath);
63
+ if (!fileConfig.apiKey) {
64
+ const jsPath = path.resolve(process.cwd(), 'mdefender.config.js');
65
+ if (fs.existsSync(jsPath)) {
66
+ try {
67
+ fileConfig = require(jsPath);
68
+ } catch (e) {
69
+ console.warn('[MDefender] Failed reading mdefender.config.js:', e.message);
70
+ }
71
+ }
32
72
  }
33
73
 
34
74
  // Try mdefender.json
35
- const jsonPath = path.resolve(process.cwd(), 'mdefender.json');
36
- if (!fileConfig.apiKey && fs.existsSync(jsonPath)) {
37
- fileConfig = JSON.parse(fs.readFileSync(jsonPath, 'utf-8'));
75
+ if (!fileConfig.apiKey) {
76
+ const jsonPath = path.resolve(process.cwd(), 'mdefender.json');
77
+ if (fs.existsSync(jsonPath)) {
78
+ try {
79
+ fileConfig = JSON.parse(fs.readFileSync(jsonPath, 'utf-8'));
80
+ } catch (e) {}
81
+ }
38
82
  }
39
83
 
40
84
  // Try package.json "mdefender" key
@@ -45,154 +89,154 @@ function loadConfig(overrides = {}) {
45
89
  } catch (e) {}
46
90
  }
47
91
 
48
- return { ...DEFAULT_CONFIG, ...fileConfig, ...overrides };
92
+ // Environment variables
93
+ const envConfig = {};
94
+ if (process.env.MDEFENDER_API_KEY) envConfig.apiKey = process.env.MDEFENDER_API_KEY;
95
+ if (process.env.MDEFENDER_DOMAIN) envConfig.domain = process.env.MDEFENDER_DOMAIN;
96
+ if (process.env.MDEFENDER_API_ENDPOINT || process.env.MDEFENDER_ENDPOINT) {
97
+ envConfig.apiEndpoint = process.env.MDEFENDER_API_ENDPOINT || process.env.MDEFENDER_ENDPOINT;
98
+ }
99
+ if (process.env.MDEFENDER_MODE) envConfig.mode = process.env.MDEFENDER_MODE;
100
+
101
+ return { ...DEFAULT_CONFIG, ...fileConfig, ...envConfig, ...overrides };
49
102
  }
50
103
 
51
104
  function getClientIP(req) {
52
- return req.headers['x-forwarded-for']?.split(',')[0]?.trim()
53
- || req.headers['x-real-ip']
105
+ const forwarded = req.headers['x-forwarded-for'];
106
+ if (forwarded) {
107
+ return forwarded.split(',')[0].trim();
108
+ }
109
+ return req.headers['x-real-ip']
54
110
  || req.connection?.remoteAddress
55
111
  || req.socket?.remoteAddress
56
- || 'unknown';
112
+ || '127.0.0.1';
57
113
  }
58
114
 
59
- function buildPayload(req, config) {
60
- const ip = getClientIP(req);
61
- const originalUrl = req.originalUrl || req.url;
62
- const parsedUrl = new URL(originalUrl, `http://${req.headers.host || 'localhost'}`);
63
-
64
- const payload = {
65
- domain: config.domain,
66
- method: req.method,
67
- url: parsedUrl.pathname,
68
- query_string: parsedUrl.search || '',
69
- query_params: Object.fromEntries(parsedUrl.searchParams),
70
- ip: ip,
71
- headers: config.headers ? req.headers : {},
72
- user_agent: req.headers['user-agent'] || '',
73
- referer: req.headers['referer'] || req.headers['referrer'] || '',
74
- content_type: req.headers['content-type'] || '',
75
- body: '',
76
- body_fields: {},
77
- body_field_values: '',
78
- timestamp: new Date().toISOString(),
79
- };
115
+ function extractBody(req) {
116
+ if (['GET', 'HEAD', 'OPTIONS'].includes(req.method)) {
117
+ return { raw: '', fields: {}, values: '' };
118
+ }
119
+
120
+ if (req.body) {
121
+ if (typeof req.body === 'object') {
122
+ try {
123
+ const raw = JSON.stringify(req.body);
124
+ const values = Object.values(req.body).map(v => typeof v === 'object' ? JSON.stringify(v) : String(v)).join(' ');
125
+ return { raw, fields: req.body, values };
126
+ } catch (e) {
127
+ return { raw: '', fields: {}, values: '' };
128
+ }
129
+ }
130
+ const str = String(req.body);
131
+ return { raw: str, fields: {}, values: str };
132
+ }
80
133
 
81
- return payload;
134
+ return { raw: '', fields: {}, values: '' };
82
135
  }
83
136
 
84
- function readBody(req, maxBytes) {
85
- return new Promise((resolve) => {
86
- if (req.body) {
87
- if (typeof req.body === 'string') return resolve(req.body);
88
- if (typeof req.body === 'object') return resolve(JSON.stringify(req.body));
89
- return resolve(String(req.body));
90
- }
137
+ function sendAnalyzeRequest(endpointUrl, apiKey, data, timeoutMs = 5000) {
138
+ return new Promise((resolve, reject) => {
139
+ try {
140
+ const parsed = new URL('/api/analyze', endpointUrl);
141
+ const postData = JSON.stringify(data);
142
+ const isHttps = parsed.protocol === 'https:';
143
+ const transport = isHttps ? https : http;
144
+ const agent = isHttps ? httpsAgent : httpAgent;
91
145
 
92
- let body = '';
93
- maxBytes = maxBytes || 1024 * 1024;
94
- let bytesRead = 0;
95
-
96
- const onData = (chunk) => {
97
- bytesRead += chunk.length;
98
- if (bytesRead > maxBytes) {
99
- req.removeListener('data', onData);
100
- req.removeListener('end', onEnd);
101
- resolve(body);
102
- return;
103
- }
104
- body += chunk.toString();
105
- };
106
-
107
- const onEnd = () => {
108
- req.removeListener('data', onData);
109
- resolve(body);
110
- };
111
-
112
- req.on('data', onData);
113
- req.on('end', onEnd);
114
-
115
- // If stream already ended
116
- if (req.readableEnded || req.complete) {
117
- req.removeListener('data', onData);
118
- req.removeListener('end', onEnd);
119
- resolve(body);
146
+ const options = {
147
+ hostname: parsed.hostname,
148
+ port: parsed.port || (isHttps ? 443 : 80),
149
+ path: parsed.pathname,
150
+ method: 'POST',
151
+ agent: agent,
152
+ timeout: timeoutMs,
153
+ headers: {
154
+ 'Content-Type': 'application/json',
155
+ 'Content-Length': Buffer.byteLength(postData),
156
+ 'Authorization': `Bearer ${apiKey}`,
157
+ 'X-MDefender-Version': '1.1.0'
158
+ }
159
+ };
160
+
161
+ const req = transport.request(options, (res) => {
162
+ let body = '';
163
+ res.setEncoding('utf8');
164
+ res.on('data', (chunk) => { body += chunk; });
165
+ res.on('end', () => {
166
+ try {
167
+ const json = JSON.parse(body);
168
+ resolve({ statusCode: res.statusCode, data: json });
169
+ } catch (e) {
170
+ resolve({ statusCode: res.statusCode, data: { status: res.statusCode === 200 ? 'allowed' : 'error', raw: body } });
171
+ }
172
+ });
173
+ });
174
+
175
+ req.on('error', (err) => {
176
+ reject(err);
177
+ });
178
+
179
+ req.on('timeout', () => {
180
+ req.destroy(new Error(`WAF request timed out after ${timeoutMs}ms`));
181
+ });
182
+
183
+ req.write(postData);
184
+ req.end();
185
+ } catch (err) {
186
+ reject(err);
120
187
  }
121
188
  });
122
189
  }
123
190
 
124
- function parseBody(body, contentType) {
125
- if (!body) return { fields: {}, values: '' };
126
-
127
- try {
128
- if (contentType && contentType.includes('application/json')) {
129
- const parsed = JSON.parse(body);
130
- return { fields: parsed, values: Object.values(parsed).join(' ') };
131
- }
132
- if (contentType && contentType.includes('application/x-www-form-urlencoded')) {
133
- const params = new URLSearchParams(body);
134
- const fields = Object.fromEntries(params);
135
- return { fields, values: Object.values(fields).join(' ') };
136
- }
137
- } catch (e) {}
138
-
139
- return { fields: {}, values: body };
191
+ function escapeHtml(str) {
192
+ if (!str) return '';
193
+ return String(str)
194
+ .replace(/&/g, '&')
195
+ .replace(/</g, '&lt;')
196
+ .replace(/>/g, '&gt;')
197
+ .replace(/"/g, '&quot;')
198
+ .replace(/'/g, '&#039;');
140
199
  }
141
200
 
142
- function renderBlockPage(config, result) {
143
- if (config.customBlockPage && fs.existsSync(config.customBlockPage)) {
144
- return fs.readFileSync(config.customBlockPage, 'utf-8');
201
+ function renderBlockPage(config, result, req) {
202
+ let template = '';
203
+ if (config.customBlockPage) {
204
+ try {
205
+ if (fs.existsSync(config.customBlockPage)) {
206
+ template = fs.readFileSync(config.customBlockPage, 'utf-8');
207
+ }
208
+ } catch (e) {}
145
209
  }
146
210
 
147
- const attackType = result.attack_type || 'Unknown';
148
- const confidence = result.confidence || 0;
149
- const referenceId = result.reference_id || crypto.randomUUID();
150
-
151
- return `<!DOCTYPE html>
152
- <html lang="en">
153
- <head>
154
- <meta charset="UTF-8">
155
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
156
- <title>Access Blocked - MDefender Pro</title>
157
- <style>
158
- * { margin: 0; padding: 0; box-sizing: border-box; }
159
- body { min-height: 100vh; display: flex; align-items: center; justify-content: center;
160
- background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
161
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
162
- color: #fff; }
163
- .container { text-align: center; padding: 40px; max-width: 600px; }
164
- .icon { font-size: 80px; margin-bottom: 20px; opacity: 0.9; }
165
- h1 { font-size: 28px; margin-bottom: 12px; font-weight: 700; }
166
- .subtitle { font-size: 16px; color: rgba(255,255,255,0.7); margin-bottom: 30px; }
167
- .details { background: rgba(255,255,255,0.08); border-radius: 12px; padding: 20px;
168
- text-align: left; margin-bottom: 30px; backdrop-filter: blur(10px); }
169
- .details p { font-size: 14px; color: rgba(255,255,255,0.8); margin: 8px 0; }
170
- .details strong { color: #e74c3c; }
171
- .footer { font-size: 12px; color: rgba(255,255,255,0.4); }
172
- </style>
173
- </head>
174
- <body>
175
- <div class="container">
176
- <div class="icon">&#x1F6AB;</div>
177
- <h1>Access Blocked</h1>
178
- <p class="subtitle">This request has been blocked by MDefender Pro WAF</p>
179
- <div class="details">
180
- <p><strong>Attack Type:</strong> ${attackType}</p>
181
- <p><strong>Confidence:</strong> ${(confidence * 100).toFixed(1)}%</p>
182
- <p><strong>Reference ID:</strong> ${referenceId}</p>
183
- <p><strong>Time:</strong> ${new Date().toISOString()}</p>
184
- </div>
185
- <p class="footer">MDefender Pro - Web Application Firewall</p>
186
- </div>
187
- </body>
188
- </html>`;
211
+ if (!template) {
212
+ template = getDefaultTemplate();
213
+ }
214
+
215
+ const attackType = result.attack_type || 'Malicious Payload Detected';
216
+ const clientIp = getClientIP(req);
217
+ const reason = result.reason || result.message || 'Request blocked by MDefender Pro WAF security policies.';
218
+ const referenceId = result.reference_id || ('MDF-' + crypto.randomBytes(4).toString('hex').toUpperCase());
219
+ const timestamp = new Date().toISOString().replace('T', ' ').slice(0, 19);
220
+ const siteName = config.domain || req.headers.host || 'Protected Website';
221
+
222
+ if (!template) {
223
+ return `<!DOCTYPE html><html><head><title>403 Access Denied</title></head><body style="background:#060913;color:#fff;font-family:sans-serif;padding:40px;text-align:center"><h1>403 Forbidden</h1><p>${escapeHtml(reason)}</p><p>Incident ID: ${escapeHtml(referenceId)}</p></body></html>`;
224
+ }
225
+
226
+ return template
227
+ .replace(/\{\{(REFERENCE_ID|reference_id)\}\}/g, escapeHtml(referenceId))
228
+ .replace(/\{\{(ATTACK_TYPE|attack_type)\}\}/g, escapeHtml(attackType))
229
+ .replace(/\{\{(CLIENT_IP|client_ip)\}\}/g, escapeHtml(clientIp))
230
+ .replace(/\{\{(SITE_NAME|site_name|website_name)\}\}/g, escapeHtml(siteName))
231
+ .replace(/\{\{(TIMESTAMP|timestamp)\}\}/g, escapeHtml(timestamp))
232
+ .replace(/\{\{(REASON|reason)\}\}/g, escapeHtml(reason));
189
233
  }
190
234
 
191
235
  function mdefender(overrides = {}) {
192
236
  const config = loadConfig(overrides);
193
237
 
194
238
  if (!config.apiKey) {
195
- console.error('[MDefender] ERROR: No API key provided. Set it in mdefender.config.js or pass it as an option.');
239
+ console.error('[MDefender] WARNING: No API key provided. Running in bypass mode.');
196
240
  return (req, res, next) => next();
197
241
  }
198
242
 
@@ -200,92 +244,89 @@ function mdefender(overrides = {}) {
200
244
  return (req, res, next) => next();
201
245
  }
202
246
 
203
- const client = axios.create({
204
- baseURL: config.apiEndpoint,
205
- timeout: config.timeout,
206
- headers: {
207
- 'Authorization': `Bearer ${config.apiKey}`,
208
- 'Content-Type': 'application/json',
209
- 'X-MDefender-Version': '1.2.0',
210
- },
211
- });
247
+ console.log(`[MDefender] WAF active & protecting domain: "${config.domain || 'default'}" via ${config.apiEndpoint}`);
248
+
249
+ // Warm-up in-memory block page template cache on initialization
250
+ getDefaultTemplate();
212
251
 
213
252
  return async function mdefenderMiddleware(req, res, next) {
214
- // Skip certain paths
215
- if (config.skipPaths.some(p => req.url.startsWith(p))) return next();
253
+ // 1. Skip paths
254
+ const urlPath = (req.originalUrl || req.url || '').split('?')[0];
255
+ if (config.skipPaths.some(p => urlPath.startsWith(p))) return next();
216
256
 
217
- // Skip certain methods
257
+ // 2. Skip methods
218
258
  if (config.skipMethods.includes(req.method)) return next();
219
259
 
220
- // Skip certain user agents
260
+ // 3. Skip user agents
221
261
  const ua = req.headers['user-agent'] || '';
222
262
  if (config.skipUserAgents.some(s => ua.toLowerCase().includes(s.toLowerCase()))) return next();
223
263
 
224
264
  try {
225
- // Read and parse request body
226
- const rawBody = await readBody(req, config.maxBodySize);
227
- const contentType = req.headers['content-type'] || '';
228
- const { fields, values } = parseBody(rawBody, contentType);
229
-
230
- // Build payload
231
- const payload = buildPayload(req, config);
232
- payload.body = rawBody;
233
- payload.body_fields = fields;
234
- payload.body_field_values = values;
235
-
236
- // Send to MDefender API
237
- const response = await client.post('/api/analyze', {
238
- request: payload,
239
- domain: config.domain,
240
- });
241
-
242
- const result = response.data;
265
+ const clientIp = getClientIP(req);
266
+ const host = req.headers.host || 'localhost';
267
+ const parsedUrl = new URL(req.originalUrl || req.url, `http://${host}`);
268
+ const bodyInfo = extractBody(req);
243
269
 
244
- if (result.status === 'blocked') {
245
- if (config.mode === 'monitor') {
246
- // Monitor mode: log but don't block
247
- console.log(`[MDefender] MONITOR: ${req.method} ${req.url} - ${result.attack_type} (${(result.confidence * 100).toFixed(1)}%) - ALLOWED (monitor mode)`);
248
- req.mdefender = {
249
- status: 'monitor',
250
- threat_score: result.confidence || 0,
251
- attack_type: result.attack_type || null,
252
- request_id: result.reference_id || null,
253
- };
254
- return next();
270
+ const payload = {
271
+ domain: config.domain || host.split(':')[0],
272
+ request: {
273
+ method: req.method,
274
+ url: parsedUrl.pathname,
275
+ query_string: parsedUrl.search || '',
276
+ query_params: Object.fromEntries(parsedUrl.searchParams),
277
+ ip: clientIp,
278
+ headers: req.headers || {},
279
+ user_agent: ua,
280
+ referer: req.headers['referer'] || req.headers['referrer'] || '',
281
+ content_type: req.headers['content-type'] || '',
282
+ body: bodyInfo.raw,
283
+ body_fields: bodyInfo.fields,
284
+ body_field_values: bodyInfo.values,
285
+ timestamp: new Date().toISOString(),
255
286
  }
287
+ };
256
288
 
257
- // Block mode: block the request
289
+ const resp = await sendAnalyzeRequest(config.apiEndpoint, config.apiKey, payload, config.timeout);
290
+ const result = resp.data;
291
+
292
+ if (result.status === 'blocked') {
258
293
  if (config.logBlocked) {
259
- console.log(`[MDefender] BLOCKED ${req.method} ${req.url} - ${result.attack_type} (${(result.confidence * 100).toFixed(1)}%)`);
294
+ console.warn(`[MDefender] BLOCKED ${req.method} ${parsedUrl.pathname} - ${result.attack_type || 'Malicious Payload'} (${((result.confidence || 0.95) * 100).toFixed(0)}%) [IP: ${clientIp}]`);
260
295
  }
261
296
 
262
- const blockPage = result.block_page || renderBlockPage(config, result);
263
- res.writeHead(config.blockStatusCode, {
297
+ const blockPage = result.block_page || renderBlockPage(config, result, req);
298
+ const headers = {
264
299
  'Content-Type': 'text/html; charset=utf-8',
265
300
  'X-MDefender-Status': 'blocked',
266
301
  'X-MDefender-Attack-Type': result.attack_type || 'unknown',
267
- });
302
+ 'X-MDefender-Ref': result.reference_id || 'MDF-BLOCKED',
303
+ };
304
+ if (req.headers.origin) {
305
+ headers['Access-Control-Allow-Origin'] = req.headers.origin;
306
+ headers['Access-Control-Allow-Credentials'] = 'true';
307
+ }
308
+ res.writeHead(config.blockStatusCode || 403, headers);
268
309
  return res.end(blockPage);
269
310
  }
270
311
 
271
- // Request is safe - attach result to request for downstream use
312
+ // Safe request
272
313
  req.mdefender = {
273
314
  status: 'allowed',
274
315
  threat_score: result.threat_score || 0,
275
- request_id: result.request_id || null,
316
+ reference_id: result.reference_id || null,
276
317
  };
277
318
 
278
319
  return next();
279
320
 
280
321
  } catch (error) {
281
- console.error(`[MDefender] API Error: ${error.message}`);
322
+ console.error(`[MDefender] WAF Warning: ${error.message}`);
282
323
 
283
324
  if (config.onError === 'block') {
284
325
  res.writeHead(503, { 'Content-Type': 'text/html' });
285
- return res.end('<html><body><h1>Service Temporarily Unavailable</h1><p>WAF service is currently unreachable. Please try again later.</p></body></html>');
326
+ return res.end('<h1>503 Service Unavailable</h1><p>WAF security service unreachable.</p>');
286
327
  }
287
328
 
288
- // Default: allow request on error
329
+ // Default: Fail-safe allow legitimate traffic to continue
289
330
  return next();
290
331
  }
291
332
  };
@@ -293,5 +334,6 @@ function mdefender(overrides = {}) {
293
334
 
294
335
  mdefender.loadConfig = loadConfig;
295
336
  mdefender.DEFAULT_CONFIG = DEFAULT_CONFIG;
337
+ mdefender.sendAnalyzeRequest = sendAnalyzeRequest;
296
338
 
297
339
  module.exports = mdefender;
package/package.json CHANGED
@@ -1,46 +1,57 @@
1
- {
2
- "name": "mdefender-pro",
3
- "version": "1.2.0",
4
- "description": "MDefender Pro - Web Application Firewall middleware for Node.js/Express. Protects against XSS, SQLi, CSRF, and other OWASP Top 10 attacks.",
5
- "main": "index.js",
6
- "types": "index.d.ts",
7
- "keywords": [
8
- "waf",
9
- "web-application-firewall",
10
- "security",
11
- "firewall",
12
- "xss",
13
- "sqli",
14
- "csrf",
15
- "protection",
16
- "middleware",
17
- "express",
18
- "nodejs",
19
- "owasp"
20
- ],
21
- "author": "MDefender Pro",
22
- "license": "MIT",
23
- "engines": {
24
- "node": ">=14.0.0"
25
- },
26
- "dependencies": {
27
- "axios": "^1.6.0"
28
- },
29
- "peerDependencies": {
30
- "express": ">=4.0.0"
31
- },
32
- "files": [
33
- "index.js",
34
- "index.d.ts",
35
- "README.md",
36
- "config-loader.js"
37
- ],
38
- "repository": {
39
- "type": "git",
40
- "url": "https://github.com/mahabub251595/mdefender-pro.git"
41
- },
42
- "homepage": "https://mdefender-pro-6e3r.onrender.com",
43
- "bugs": {
44
- "url": "https://github.com/mahabub251595/mdefender-pro/issues"
45
- }
46
- }
1
+ {
2
+ "name": "mdefender-pro",
3
+ "version": "1.2.2",
4
+ "description": "MDefender Pro - Web Application Firewall middleware for Node.js/Express. Protects against XSS, SQLi, CSRF, and other OWASP Top 10 attacks.",
5
+ "main": "index.js",
6
+ "types": "index.d.ts",
7
+ "keywords": [
8
+ "waf",
9
+ "web-application-firewall",
10
+ "security",
11
+ "firewall",
12
+ "xss",
13
+ "sqli",
14
+ "csrf",
15
+ "protection",
16
+ "middleware",
17
+ "express",
18
+ "nodejs",
19
+ "owasp"
20
+ ],
21
+ "author": "MDefender Pro",
22
+ "license": "MIT",
23
+ "engines": {
24
+ "node": ">=14.0.0"
25
+ },
26
+ "bin": {
27
+ "mdefender-pro": "bin/mdefender.js"
28
+ },
29
+ "dependencies": {},
30
+ "peerDependencies": {
31
+ "express": "^4.18.0 || ^5.0.0"
32
+ },
33
+ "files": [
34
+ "index.js",
35
+ "index.d.ts",
36
+ "README.md",
37
+ "config-loader.js",
38
+ "client.mjs",
39
+ "vite.js",
40
+ "vite.mjs",
41
+ "block-page.html",
42
+ "bin"
43
+ ],
44
+ "exports": {
45
+ ".": "./index.js",
46
+ "./client": "./client.mjs",
47
+ "./vite": "./vite.mjs"
48
+ },
49
+ "repository": {
50
+ "type": "git",
51
+ "url": "git+https://github.com/mdefender/mdefender.git"
52
+ },
53
+ "homepage": "https://mdefender-pro-6e3r.onrender.com",
54
+ "bugs": {
55
+ "url": "https://github.com/mdefender/mdefender/issues"
56
+ }
57
+ }
package/vite.js ADDED
@@ -0,0 +1,18 @@
1
+ // vite.js - Official MDefender Pro Vite Plugin (CJS)
2
+ const mdefender = require('./index.js');
3
+
4
+ function mdefenderVite(options = {}) {
5
+ const middleware = mdefender(options);
6
+ return {
7
+ name: 'mdefender-vite-plugin',
8
+ configureServer(server) {
9
+ server.middlewares.use(middleware);
10
+ },
11
+ configurePreviewServer(server) {
12
+ server.middlewares.use(middleware);
13
+ }
14
+ };
15
+ }
16
+
17
+ module.exports = { mdefenderVite };
18
+ module.exports.default = mdefenderVite;