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/README.md +108 -136
- package/bin/mdefender.js +80 -0
- package/block-page.html +541 -0
- package/client.mjs +446 -0
- package/config-loader.js +35 -35
- package/index.d.ts +37 -37
- package/index.js +228 -186
- package/package.json +57 -46
- package/vite.js +18 -0
- package/vite.mjs +17 -0
package/client.mjs
ADDED
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
// client.mjs
|
|
2
|
+
/**
|
|
3
|
+
* Initialize MDefender Pro WAF client-side protection for SPAs.
|
|
4
|
+
* @param {Object} options - Client configuration
|
|
5
|
+
* @param {string} options.backendUrl - Base URL of your backend API server (e.g. 'http://localhost:5005')
|
|
6
|
+
*/
|
|
7
|
+
export function initWaf(options = {}) {
|
|
8
|
+
if (typeof window === 'undefined') return;
|
|
9
|
+
|
|
10
|
+
const backendUrl = (options.backendUrl || '').replace(/\/+$/, '');
|
|
11
|
+
const currentSearch = decodeURIComponent(window.location.search);
|
|
12
|
+
|
|
13
|
+
// Instant 0ms Local Block Page Renderer
|
|
14
|
+
const renderInstantBlockPage = (attackType, refId) => {
|
|
15
|
+
const referenceId = refId || ('MDF-' + Math.random().toString(16).substring(2, 10).toUpperCase());
|
|
16
|
+
const host = window.location.hostname || 'localhost';
|
|
17
|
+
const nowUtc = new Date().toISOString().replace('T', ' ').slice(0, 19) + ' UTC';
|
|
18
|
+
const unixRef = Math.floor(Date.now() / 1000);
|
|
19
|
+
const ruleId = 96565;
|
|
20
|
+
|
|
21
|
+
const html = `<!DOCTYPE html>
|
|
22
|
+
<html lang="en">
|
|
23
|
+
<head>
|
|
24
|
+
<meta charset="UTF-8">
|
|
25
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
26
|
+
<title>403 — Security Action Required | MDefender Pro</title>
|
|
27
|
+
<style>
|
|
28
|
+
:root {
|
|
29
|
+
--brand-1: #4f46e5;
|
|
30
|
+
--brand-2: #6366f1;
|
|
31
|
+
--bg-color: #f8fafc;
|
|
32
|
+
--card-bg: #ffffff;
|
|
33
|
+
--text-primary: #0f172a;
|
|
34
|
+
--text-secondary: #475569;
|
|
35
|
+
--text-muted: #94a3b8;
|
|
36
|
+
--border-color: #cbd5e1;
|
|
37
|
+
--font-main: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
|
38
|
+
--font-mono: Consolas, Monaco, monospace;
|
|
39
|
+
}
|
|
40
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
41
|
+
body {
|
|
42
|
+
font-family: var(--font-main);
|
|
43
|
+
background-color: var(--bg-color);
|
|
44
|
+
color: var(--text-primary);
|
|
45
|
+
min-height: 100vh;
|
|
46
|
+
display: flex;
|
|
47
|
+
align-items: center;
|
|
48
|
+
justify-content: center;
|
|
49
|
+
padding: 16px;
|
|
50
|
+
-webkit-font-smoothing: antialiased;
|
|
51
|
+
}
|
|
52
|
+
.container {
|
|
53
|
+
width: 100%;
|
|
54
|
+
max-width: 660px;
|
|
55
|
+
background: var(--card-bg);
|
|
56
|
+
border: 1px solid var(--border-color);
|
|
57
|
+
border-radius: 12px;
|
|
58
|
+
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.05), 0 8px 10px -6px rgba(0, 0, 0, 0.01);
|
|
59
|
+
padding: 24px 28px;
|
|
60
|
+
}
|
|
61
|
+
.top-brand {
|
|
62
|
+
display: flex;
|
|
63
|
+
align-items: center;
|
|
64
|
+
gap: 7px;
|
|
65
|
+
font-size: 11.5px;
|
|
66
|
+
font-weight: 700;
|
|
67
|
+
color: var(--brand-1);
|
|
68
|
+
letter-spacing: 0.04em;
|
|
69
|
+
text-transform: uppercase;
|
|
70
|
+
margin-bottom: 12px;
|
|
71
|
+
}
|
|
72
|
+
.top-brand span { color: var(--text-muted); font-weight: 400; }
|
|
73
|
+
.top-brand .creator { color: var(--text-secondary); font-weight: 600; }
|
|
74
|
+
.status-badge {
|
|
75
|
+
display: inline-flex;
|
|
76
|
+
align-items: center;
|
|
77
|
+
gap: 7px;
|
|
78
|
+
background: #fff1f2;
|
|
79
|
+
border: 1px solid #fecdd3;
|
|
80
|
+
color: #e11d48;
|
|
81
|
+
padding: 4px 10px;
|
|
82
|
+
border-radius: 20px;
|
|
83
|
+
font-size: 11px;
|
|
84
|
+
font-weight: 700;
|
|
85
|
+
margin-bottom: 14px;
|
|
86
|
+
}
|
|
87
|
+
.status-badge .dot {
|
|
88
|
+
width: 6px;
|
|
89
|
+
height: 6px;
|
|
90
|
+
background: #e11d48;
|
|
91
|
+
border-radius: 50%;
|
|
92
|
+
box-shadow: 0 0 0 2px rgba(225, 29, 72, 0.2);
|
|
93
|
+
}
|
|
94
|
+
h1 {
|
|
95
|
+
font-size: 19px;
|
|
96
|
+
font-weight: 800;
|
|
97
|
+
color: #0f172a;
|
|
98
|
+
letter-spacing: -0.01em;
|
|
99
|
+
margin-bottom: 4px;
|
|
100
|
+
line-height: 1.25;
|
|
101
|
+
}
|
|
102
|
+
.subtitle {
|
|
103
|
+
font-size: 12px;
|
|
104
|
+
color: var(--text-secondary);
|
|
105
|
+
margin-bottom: 18px;
|
|
106
|
+
}
|
|
107
|
+
.subtitle strong {
|
|
108
|
+
color: var(--text-primary);
|
|
109
|
+
font-weight: 600;
|
|
110
|
+
}
|
|
111
|
+
.table-wrap {
|
|
112
|
+
border: 1px solid var(--border-color);
|
|
113
|
+
border-radius: 8px;
|
|
114
|
+
overflow: hidden;
|
|
115
|
+
margin-bottom: 18px;
|
|
116
|
+
background: #ffffff;
|
|
117
|
+
}
|
|
118
|
+
table {
|
|
119
|
+
width: 100%;
|
|
120
|
+
border-collapse: collapse;
|
|
121
|
+
text-align: left;
|
|
122
|
+
font-size: 11.5px;
|
|
123
|
+
}
|
|
124
|
+
tr {
|
|
125
|
+
border-bottom: 1px solid #f1f5f9;
|
|
126
|
+
}
|
|
127
|
+
tr:last-child {
|
|
128
|
+
border-bottom: none;
|
|
129
|
+
}
|
|
130
|
+
th {
|
|
131
|
+
width: 32%;
|
|
132
|
+
background: #f8fafc;
|
|
133
|
+
padding: 8.5px 14px;
|
|
134
|
+
font-weight: 600;
|
|
135
|
+
color: #475569;
|
|
136
|
+
border-right: 1px solid #f1f5f9;
|
|
137
|
+
text-transform: uppercase;
|
|
138
|
+
font-size: 10.5px;
|
|
139
|
+
letter-spacing: 0.02em;
|
|
140
|
+
}
|
|
141
|
+
td {
|
|
142
|
+
padding: 8.5px 14px;
|
|
143
|
+
color: var(--text-primary);
|
|
144
|
+
font-family: var(--font-mono);
|
|
145
|
+
font-size: 11.5px;
|
|
146
|
+
word-break: break-all;
|
|
147
|
+
}
|
|
148
|
+
.threat-alert {
|
|
149
|
+
color: #dc2626;
|
|
150
|
+
font-weight: 700;
|
|
151
|
+
display: flex;
|
|
152
|
+
align-items: center;
|
|
153
|
+
justify-content: space-between;
|
|
154
|
+
}
|
|
155
|
+
.info-tag {
|
|
156
|
+
font-size: 9.5px;
|
|
157
|
+
font-weight: 700;
|
|
158
|
+
background: #fef2f2;
|
|
159
|
+
color: #ef4444;
|
|
160
|
+
padding: 1.5px 6px;
|
|
161
|
+
border-radius: 4px;
|
|
162
|
+
border: 1px solid #fee2e2;
|
|
163
|
+
font-family: var(--font-main);
|
|
164
|
+
text-transform: uppercase;
|
|
165
|
+
}
|
|
166
|
+
.ip-wrap {
|
|
167
|
+
display: flex;
|
|
168
|
+
align-items: center;
|
|
169
|
+
gap: 7px;
|
|
170
|
+
}
|
|
171
|
+
.flag-img {
|
|
172
|
+
border-radius: 2px;
|
|
173
|
+
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
|
|
174
|
+
vertical-align: middle;
|
|
175
|
+
}
|
|
176
|
+
.why-header {
|
|
177
|
+
font-size: 12.5px;
|
|
178
|
+
font-weight: 700;
|
|
179
|
+
color: var(--text-primary);
|
|
180
|
+
margin-bottom: 4px;
|
|
181
|
+
}
|
|
182
|
+
.why-desc {
|
|
183
|
+
font-size: 11.5px;
|
|
184
|
+
color: var(--text-secondary);
|
|
185
|
+
line-height: 1.45;
|
|
186
|
+
margin-bottom: 14px;
|
|
187
|
+
}
|
|
188
|
+
.ref-box {
|
|
189
|
+
background: #f8fafc;
|
|
190
|
+
border: 1px dashed var(--border-color);
|
|
191
|
+
padding: 9px 14px;
|
|
192
|
+
border-radius: 6px;
|
|
193
|
+
font-family: var(--font-mono);
|
|
194
|
+
font-size: 11.5px;
|
|
195
|
+
font-weight: 700;
|
|
196
|
+
color: var(--brand-1);
|
|
197
|
+
text-align: center;
|
|
198
|
+
margin-bottom: 16px;
|
|
199
|
+
cursor: pointer;
|
|
200
|
+
transition: all 0.15s ease;
|
|
201
|
+
}
|
|
202
|
+
.ref-box:hover {
|
|
203
|
+
background: #f1f5f9;
|
|
204
|
+
border-color: var(--brand-1);
|
|
205
|
+
}
|
|
206
|
+
.actions {
|
|
207
|
+
display: flex;
|
|
208
|
+
align-items: center;
|
|
209
|
+
gap: 9px;
|
|
210
|
+
flex-wrap: wrap;
|
|
211
|
+
}
|
|
212
|
+
.btn {
|
|
213
|
+
display: inline-flex;
|
|
214
|
+
align-items: center;
|
|
215
|
+
justify-content: center;
|
|
216
|
+
gap: 6px;
|
|
217
|
+
padding: 8px 14px;
|
|
218
|
+
font-size: 11.5px;
|
|
219
|
+
font-weight: 600;
|
|
220
|
+
border-radius: 6px;
|
|
221
|
+
text-decoration: none;
|
|
222
|
+
cursor: pointer;
|
|
223
|
+
transition: all 0.15s ease;
|
|
224
|
+
font-family: var(--font-main);
|
|
225
|
+
}
|
|
226
|
+
.btn-primary {
|
|
227
|
+
background: var(--brand-1);
|
|
228
|
+
color: #ffffff;
|
|
229
|
+
border: 1px solid var(--brand-1);
|
|
230
|
+
box-shadow: 0 1px 2px rgba(79, 70, 229, 0.2);
|
|
231
|
+
}
|
|
232
|
+
.btn-primary:hover {
|
|
233
|
+
background: #4338ca;
|
|
234
|
+
}
|
|
235
|
+
.btn-secondary {
|
|
236
|
+
background: #ffffff;
|
|
237
|
+
color: var(--text-primary);
|
|
238
|
+
border: 1px solid var(--border-color);
|
|
239
|
+
}
|
|
240
|
+
.btn-secondary:hover {
|
|
241
|
+
background: #f8fafc;
|
|
242
|
+
border-color: #94a3b8;
|
|
243
|
+
}
|
|
244
|
+
.footer {
|
|
245
|
+
margin-top: 18px;
|
|
246
|
+
padding-top: 12px;
|
|
247
|
+
border-top: 1px solid #f1f5f9;
|
|
248
|
+
font-size: 10.5px;
|
|
249
|
+
color: var(--text-muted);
|
|
250
|
+
text-align: center;
|
|
251
|
+
}
|
|
252
|
+
</style>
|
|
253
|
+
</head>
|
|
254
|
+
<body>
|
|
255
|
+
<div class="container">
|
|
256
|
+
<div class="top-brand">
|
|
257
|
+
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
258
|
+
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
|
|
259
|
+
</svg>
|
|
260
|
+
MDEFENDER-PRO AI <span>•</span> A.S.A.P. SECURITY FIREWALL <span>•</span> <span class="creator">MAHABUB</span>
|
|
261
|
+
</div>
|
|
262
|
+
|
|
263
|
+
<div class="status-badge">
|
|
264
|
+
<div class="dot"></div>
|
|
265
|
+
403 — SECURITY ACTION REQUIRED • Access Denied by Corporate WAF
|
|
266
|
+
</div>
|
|
267
|
+
|
|
268
|
+
<h1>403 — Access Denied</h1>
|
|
269
|
+
<div class="subtitle">Access to <strong>${host}</strong> has been restricted by security policy.</div>
|
|
270
|
+
|
|
271
|
+
<div class="table-wrap">
|
|
272
|
+
<table>
|
|
273
|
+
<tr>
|
|
274
|
+
<th>Attack Classification</th>
|
|
275
|
+
<td>
|
|
276
|
+
<div class="threat-alert">
|
|
277
|
+
<span>${attackType}</span>
|
|
278
|
+
<span class="info-tag">Blocked Threat</span>
|
|
279
|
+
</div>
|
|
280
|
+
</td>
|
|
281
|
+
</tr>
|
|
282
|
+
<tr>
|
|
283
|
+
<th>Origin Client IP</th>
|
|
284
|
+
<td>
|
|
285
|
+
<div class="ip-wrap">
|
|
286
|
+
<img id="flag-img" src="https://flagcdn.com/w40/bd.png" width="20" height="15" alt="Flag" class="flag-img">
|
|
287
|
+
<span>::1<span id="geo-text"> (GeoIP: Dhaka, Bangladesh)</span></span>
|
|
288
|
+
</div>
|
|
289
|
+
</td>
|
|
290
|
+
</tr>
|
|
291
|
+
<tr>
|
|
292
|
+
<th>Event Timestamp</th>
|
|
293
|
+
<td><span>${nowUtc}</span> (Ref: <span>${unixRef}</span>)</td>
|
|
294
|
+
</tr>
|
|
295
|
+
<tr>
|
|
296
|
+
<th>Violation Reason</th>
|
|
297
|
+
<td>Request blocked by rule ID: <span>${ruleId}</span> (Ref: ${attackType})</td>
|
|
298
|
+
</tr>
|
|
299
|
+
<tr>
|
|
300
|
+
<th>Protocol Details</th>
|
|
301
|
+
<td>HTTP/1.1 (WAF_VER: 4.1.0)</td>
|
|
302
|
+
</tr>
|
|
303
|
+
<tr>
|
|
304
|
+
<th>Server Host</th>
|
|
305
|
+
<td><span>${host}</span></td>
|
|
306
|
+
</tr>
|
|
307
|
+
</table>
|
|
308
|
+
</div>
|
|
309
|
+
|
|
310
|
+
<h3 class="why-header">Why did this happen?</h3>
|
|
311
|
+
<p class="why-desc">
|
|
312
|
+
To maintain system integrity, suspicious requests are automatically analyzed and filtered. If you believe this is a valid action, please share the Reference ID below with your local IT/Security operations.
|
|
313
|
+
</p>
|
|
314
|
+
|
|
315
|
+
<div class="ref-box" id="ref-id-box" title="Click to copy Reference ID" onclick="copyRef()">
|
|
316
|
+
REFERENCE ID: ${referenceId}
|
|
317
|
+
</div>
|
|
318
|
+
|
|
319
|
+
<div class="actions">
|
|
320
|
+
<a id="email-link" href="mailto:security@mdefender-pro.io?subject=WAF Block Reference: ${referenceId}" class="btn btn-primary">
|
|
321
|
+
✉ Contact Security Operations
|
|
322
|
+
</a>
|
|
323
|
+
<a href="/" class="btn btn-secondary">
|
|
324
|
+
Return to Homepage
|
|
325
|
+
</a>
|
|
326
|
+
<button type="button" class="btn btn-secondary" onclick="copyRef()">
|
|
327
|
+
Copy Reference ID
|
|
328
|
+
</button>
|
|
329
|
+
</div>
|
|
330
|
+
|
|
331
|
+
<div class="footer">
|
|
332
|
+
Secured by MDefender-Pro AI Firewall. All critical system events are logged and audited.
|
|
333
|
+
</div>
|
|
334
|
+
</div>
|
|
335
|
+
|
|
336
|
+
<script>
|
|
337
|
+
const refId = "${referenceId}";
|
|
338
|
+
function copyRef() {
|
|
339
|
+
navigator.clipboard.writeText(refId).then(() => {
|
|
340
|
+
const refBox = document.getElementById('ref-id-box');
|
|
341
|
+
const originalHtml = refBox.innerHTML;
|
|
342
|
+
refBox.innerHTML = 'COPIED TO CLIPBOARD! ✅';
|
|
343
|
+
refBox.style.color = '#10b981';
|
|
344
|
+
refBox.style.borderColor = '#10b981';
|
|
345
|
+
refBox.style.background = '#ecfdf5';
|
|
346
|
+
setTimeout(() => {
|
|
347
|
+
refBox.innerHTML = originalHtml;
|
|
348
|
+
refBox.style.color = '';
|
|
349
|
+
refBox.style.borderColor = '';
|
|
350
|
+
refBox.style.background = '';
|
|
351
|
+
}, 2000);
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
</script>
|
|
355
|
+
</body>
|
|
356
|
+
</html>`;
|
|
357
|
+
|
|
358
|
+
document.open();
|
|
359
|
+
document.write(html);
|
|
360
|
+
document.close();
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
// Helper to render server-provided WAF block page
|
|
364
|
+
const handleWafBlock = (htmlText) => {
|
|
365
|
+
if (htmlText && (htmlText.includes('MDefender-Pro') || htmlText.includes('Access Denied') || htmlText.includes('MDefender') || htmlText.includes('403'))) {
|
|
366
|
+
document.open();
|
|
367
|
+
document.write(htmlText);
|
|
368
|
+
document.close();
|
|
369
|
+
throw new Error("MDefender WAF Blocked Request");
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
// 1. Instant check on page load (0ms immediate response)
|
|
374
|
+
if (currentSearch) {
|
|
375
|
+
const attackRegex = /(<script|onerror|onload|javascript:|alert\(|confirm\(|prompt\(|\.\.\/|\.\.\\|union.*select|drop.*table|'\s*(or|and)\s+['\w]|etc\/passwd|;\s*(whoami|id|cat|ls|cmd|sh|bash))/i;
|
|
376
|
+
if (attackRegex.test(currentSearch)) {
|
|
377
|
+
// Instantly render block page in 0ms!
|
|
378
|
+
renderInstantBlockPage("XSS - Dangerous HTML Tag (<script>) #1");
|
|
379
|
+
|
|
380
|
+
// Background verify with server for audit logging
|
|
381
|
+
if (backendUrl) {
|
|
382
|
+
const origFetch = window.fetch;
|
|
383
|
+
const testUrl = backendUrl + '/api/books/' + window.location.search;
|
|
384
|
+
origFetch(testUrl)
|
|
385
|
+
.then(res => res.status === 403 ? res.text() : null)
|
|
386
|
+
.then(html => { if (html) handleWafBlock(html); })
|
|
387
|
+
.catch(() => {});
|
|
388
|
+
}
|
|
389
|
+
throw new Error("MDefender WAF Blocked Request");
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// 2. Intercept window.fetch to propagate query params and catch 403
|
|
394
|
+
const originalFetch = window.fetch;
|
|
395
|
+
window.fetch = async (input, init, ...args) => {
|
|
396
|
+
let url = input;
|
|
397
|
+
const searchStr = window.location.search;
|
|
398
|
+
|
|
399
|
+
if (searchStr) {
|
|
400
|
+
if (typeof url === 'string') {
|
|
401
|
+
if (url.startsWith('/') || url.includes(backendUrl) || url.includes('/api/')) {
|
|
402
|
+
const separator = url.includes('?') ? '&' : '?';
|
|
403
|
+
url = url + separator + searchStr.substring(1);
|
|
404
|
+
}
|
|
405
|
+
} else if (url instanceof Request) {
|
|
406
|
+
if (url.url.startsWith('/') || url.url.includes(backendUrl) || url.url.includes('/api/')) {
|
|
407
|
+
const separator = url.url.includes('?') ? '&' : '?';
|
|
408
|
+
const newUrl = url.url + separator + searchStr.substring(1);
|
|
409
|
+
url = new Request(newUrl, url);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const response = await originalFetch(url, init, ...args);
|
|
415
|
+
if (response.status === 403) {
|
|
416
|
+
const clone = response.clone();
|
|
417
|
+
try {
|
|
418
|
+
const text = await clone.text();
|
|
419
|
+
handleWafBlock(text);
|
|
420
|
+
} catch (e) {}
|
|
421
|
+
}
|
|
422
|
+
return response;
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
// 3. Intercept XMLHttpRequest.prototype.open to propagate query params
|
|
426
|
+
const originalOpen = XMLHttpRequest.prototype.open;
|
|
427
|
+
XMLHttpRequest.prototype.open = function(method, url, ...rest) {
|
|
428
|
+
const searchStr = window.location.search;
|
|
429
|
+
if (searchStr && (url.startsWith('/') || url.includes(backendUrl) || url.includes('/api/'))) {
|
|
430
|
+
const separator = url.includes('?') ? '&' : '?';
|
|
431
|
+
url = url + separator + searchStr.substring(1);
|
|
432
|
+
}
|
|
433
|
+
return originalOpen.apply(this, [method, url, ...rest]);
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
// 4. Intercept XMLHttpRequest.prototype.send to catch 403
|
|
437
|
+
const originalSend = XMLHttpRequest.prototype.send;
|
|
438
|
+
XMLHttpRequest.prototype.send = function(...args) {
|
|
439
|
+
this.addEventListener('load', function() {
|
|
440
|
+
if (this.status === 403) {
|
|
441
|
+
handleWafBlock(this.responseText);
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
return originalSend.apply(this, args);
|
|
445
|
+
};
|
|
446
|
+
}
|
package/config-loader.js
CHANGED
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const path = require('path');
|
|
4
|
-
const fs = require('fs');
|
|
5
|
-
|
|
6
|
-
function findConfig(startDir) {
|
|
7
|
-
const configs = [
|
|
8
|
-
'mdefender.config.js',
|
|
9
|
-
'mdefender.config.cjs',
|
|
10
|
-
'mdefender.json',
|
|
11
|
-
];
|
|
12
|
-
|
|
13
|
-
let dir = startDir || process.cwd();
|
|
14
|
-
|
|
15
|
-
while (dir !== path.dirname(dir)) {
|
|
16
|
-
for (const name of configs) {
|
|
17
|
-
const fp = path.join(dir, name);
|
|
18
|
-
if (fs.existsSync(fp)) return fp;
|
|
19
|
-
}
|
|
20
|
-
dir = path.dirname(dir);
|
|
21
|
-
}
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function load(startDir) {
|
|
26
|
-
const configPath = findConfig(startDir);
|
|
27
|
-
if (!configPath) return null;
|
|
28
|
-
|
|
29
|
-
if (configPath.endsWith('.json')) {
|
|
30
|
-
return JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
31
|
-
}
|
|
32
|
-
return require(configPath);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
module.exports = { findConfig, load };
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
|
|
6
|
+
function findConfig(startDir) {
|
|
7
|
+
const configs = [
|
|
8
|
+
'mdefender.config.js',
|
|
9
|
+
'mdefender.config.cjs',
|
|
10
|
+
'mdefender.json',
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
let dir = startDir || process.cwd();
|
|
14
|
+
|
|
15
|
+
while (dir !== path.dirname(dir)) {
|
|
16
|
+
for (const name of configs) {
|
|
17
|
+
const fp = path.join(dir, name);
|
|
18
|
+
if (fs.existsSync(fp)) return fp;
|
|
19
|
+
}
|
|
20
|
+
dir = path.dirname(dir);
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function load(startDir) {
|
|
26
|
+
const configPath = findConfig(startDir);
|
|
27
|
+
if (!configPath) return null;
|
|
28
|
+
|
|
29
|
+
if (configPath.endsWith('.json')) {
|
|
30
|
+
return JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
31
|
+
}
|
|
32
|
+
return require(configPath);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports = { findConfig, load };
|
package/index.d.ts
CHANGED
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
import { Request, Response, NextFunction } from 'express';
|
|
2
|
-
|
|
3
|
-
interface MDefenderConfig {
|
|
4
|
-
apiKey?: string;
|
|
5
|
-
domain?: string;
|
|
6
|
-
apiEndpoint?: string;
|
|
7
|
-
mode?: 'block' | 'monitor' | 'off';
|
|
8
|
-
blockStatusCode?: number;
|
|
9
|
-
timeout?: number;
|
|
10
|
-
maxBodySize?: number;
|
|
11
|
-
logBlocked?: boolean;
|
|
12
|
-
customBlockPage?: string | null;
|
|
13
|
-
skipPaths?: string[];
|
|
14
|
-
skipUserAgents?: string[];
|
|
15
|
-
skipMethods?: string[];
|
|
16
|
-
headers?: boolean;
|
|
17
|
-
onError?: 'allow' | 'block';
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
interface MDefenderRequest extends Request {
|
|
21
|
-
mdefender?: {
|
|
22
|
-
status: string;
|
|
23
|
-
threat_score: number;
|
|
24
|
-
request_id: string | null;
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
type MDefenderMiddleware = (req: MDefenderRequest, res: Response, next: NextFunction) => void;
|
|
29
|
-
|
|
30
|
-
declare function mdefender(config?: MDefenderConfig): MDefenderMiddleware;
|
|
31
|
-
|
|
32
|
-
declare namespace mdefender {
|
|
33
|
-
export function loadConfig(overrides?: Partial<MDefenderConfig>): MDefenderConfig;
|
|
34
|
-
export const DEFAULT_CONFIG: MDefenderConfig;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export = mdefender;
|
|
1
|
+
import { Request, Response, NextFunction } from 'express';
|
|
2
|
+
|
|
3
|
+
interface MDefenderConfig {
|
|
4
|
+
apiKey?: string;
|
|
5
|
+
domain?: string;
|
|
6
|
+
apiEndpoint?: string;
|
|
7
|
+
mode?: 'block' | 'monitor' | 'off';
|
|
8
|
+
blockStatusCode?: number;
|
|
9
|
+
timeout?: number;
|
|
10
|
+
maxBodySize?: number;
|
|
11
|
+
logBlocked?: boolean;
|
|
12
|
+
customBlockPage?: string | null;
|
|
13
|
+
skipPaths?: string[];
|
|
14
|
+
skipUserAgents?: string[];
|
|
15
|
+
skipMethods?: string[];
|
|
16
|
+
headers?: boolean;
|
|
17
|
+
onError?: 'allow' | 'block';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface MDefenderRequest extends Request {
|
|
21
|
+
mdefender?: {
|
|
22
|
+
status: string;
|
|
23
|
+
threat_score: number;
|
|
24
|
+
request_id: string | null;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type MDefenderMiddleware = (req: MDefenderRequest, res: Response, next: NextFunction) => void;
|
|
29
|
+
|
|
30
|
+
declare function mdefender(config?: MDefenderConfig): MDefenderMiddleware;
|
|
31
|
+
|
|
32
|
+
declare namespace mdefender {
|
|
33
|
+
export function loadConfig(overrides?: Partial<MDefenderConfig>): MDefenderConfig;
|
|
34
|
+
export const DEFAULT_CONFIG: MDefenderConfig;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export = mdefender;
|