webarmor 1.0.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.
package/dist/index.cjs ADDED
@@ -0,0 +1,2199 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __esm = (fn, res) => function __init() {
9
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
+ };
11
+ var __export = (target, all) => {
12
+ for (var name in all)
13
+ __defProp(target, name, { get: all[name], enumerable: true });
14
+ };
15
+ var __copyProps = (to, from, except, desc) => {
16
+ if (from && typeof from === "object" || typeof from === "function") {
17
+ for (let key of __getOwnPropNames(from))
18
+ if (!__hasOwnProp.call(to, key) && key !== except)
19
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
20
+ }
21
+ return to;
22
+ };
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
24
+ // If the importer is in node compatibility mode or this is not an ESM
25
+ // file that has been converted to a CommonJS file using a Babel-
26
+ // compatible transform (i.e. "__esModule" has not been set), then set
27
+ // "default" to the CommonJS "module.exports" for node compatibility.
28
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
29
+ mod
30
+ ));
31
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
+
33
+ // src/modules/ddos/DDoSProtection.ts
34
+ var DDoSProtection_exports = {};
35
+ __export(DDoSProtection_exports, {
36
+ DDoSProtection: () => DDoSProtection,
37
+ default: () => DDoSProtection_default
38
+ });
39
+ var DDoSProtection, DDoSProtection_default;
40
+ var init_DDoSProtection = __esm({
41
+ "src/modules/ddos/DDoSProtection.ts"() {
42
+ "use strict";
43
+ DDoSProtection = class {
44
+ config;
45
+ logger;
46
+ ipMap = /* @__PURE__ */ new Map();
47
+ cleanupInterval = null;
48
+ constructor(config, logger2) {
49
+ this.config = config;
50
+ this.logger = logger2;
51
+ this.startCleanup();
52
+ }
53
+ startCleanup() {
54
+ this.cleanupInterval = setInterval(() => {
55
+ const now = Date.now();
56
+ for (const [ip, data] of this.ipMap.entries()) {
57
+ if (data.blocked && data.blockUntil && now > data.blockUntil) {
58
+ data.blocked = false;
59
+ data.blockUntil = void 0;
60
+ this.logger.info(`IP ${ip} unblocked after DDoS protection`);
61
+ }
62
+ if (now - data.lastRequest > this.config.windowMs * 2) {
63
+ this.ipMap.delete(ip);
64
+ }
65
+ }
66
+ }, 6e4);
67
+ }
68
+ async check(data) {
69
+ const ip = data.ip || "unknown";
70
+ const now = Date.now();
71
+ let ipData = this.ipMap.get(ip);
72
+ if (!ipData) {
73
+ ipData = {
74
+ count: 0,
75
+ firstRequest: now,
76
+ lastRequest: now,
77
+ blocked: false,
78
+ score: 0
79
+ };
80
+ this.ipMap.set(ip, ipData);
81
+ }
82
+ if (ipData.blocked && ipData.blockUntil && now < ipData.blockUntil) {
83
+ return {
84
+ allowed: false,
85
+ reason: `DDoS protection: IP temporarily blocked until ${new Date(ipData.blockUntil).toISOString()}`
86
+ };
87
+ }
88
+ ipData.count++;
89
+ ipData.lastRequest = now;
90
+ const windowStart = now - this.config.windowMs;
91
+ const requestsInWindow = ipData.count;
92
+ if (requestsInWindow > this.config.maxRequests) {
93
+ ipData.blocked = true;
94
+ ipData.blockUntil = now + this.config.blockDurationMs;
95
+ ipData.score += 10;
96
+ this.logger.warn(`DDoS detected from IP ${ip}`, {
97
+ requestsInWindow,
98
+ maxRequests: this.config.maxRequests,
99
+ blocked: true
100
+ });
101
+ return {
102
+ allowed: false,
103
+ reason: "DDoS protection: Too many requests"
104
+ };
105
+ }
106
+ ipData.score = Math.max(0, ipData.score - 1);
107
+ if (ipData.score > this.config.scoreThreshold) {
108
+ ipData.blocked = true;
109
+ ipData.blockUntil = now + this.config.blockDurationMs;
110
+ this.logger.warn(`High risk score for IP ${ip}`, {
111
+ score: ipData.score,
112
+ threshold: this.config.scoreThreshold
113
+ });
114
+ return {
115
+ allowed: false,
116
+ reason: "DDoS protection: High risk score"
117
+ };
118
+ }
119
+ return { allowed: true };
120
+ }
121
+ getStats() {
122
+ const now = Date.now();
123
+ let active = 0;
124
+ let blocked = 0;
125
+ for (const data of this.ipMap.values()) {
126
+ if (!data.blocked || data.blockUntil && now < data.blockUntil) {
127
+ active++;
128
+ }
129
+ if (data.blocked) {
130
+ blocked++;
131
+ }
132
+ }
133
+ return {
134
+ totalTracked: this.ipMap.size,
135
+ activeConnections: active,
136
+ blockedIPs: blocked
137
+ };
138
+ }
139
+ middleware() {
140
+ return async (req, res, next) => {
141
+ const ip = req.ip || req.connection?.remoteAddress || req.headers?.["x-forwarded-for"] || "unknown";
142
+ const data = {
143
+ ip,
144
+ method: req.method,
145
+ url: req.url,
146
+ headers: req.headers,
147
+ body: req.body,
148
+ userAgent: req.headers?.["user-agent"]
149
+ };
150
+ const result = await this.check(data);
151
+ if (!result.allowed) {
152
+ return res.status(403).json({
153
+ error: "Forbidden",
154
+ message: result.reason
155
+ });
156
+ }
157
+ next();
158
+ };
159
+ }
160
+ destroy() {
161
+ if (this.cleanupInterval) {
162
+ clearInterval(this.cleanupInterval);
163
+ }
164
+ this.ipMap.clear();
165
+ }
166
+ };
167
+ DDoSProtection_default = DDoSProtection;
168
+ }
169
+ });
170
+
171
+ // src/modules/ratelimit/RateLimiter.ts
172
+ var RateLimiter_exports = {};
173
+ __export(RateLimiter_exports, {
174
+ RateLimiter: () => RateLimiter,
175
+ default: () => RateLimiter_default
176
+ });
177
+ var RateLimiter, RateLimiter_default;
178
+ var init_RateLimiter = __esm({
179
+ "src/modules/ratelimit/RateLimiter.ts"() {
180
+ "use strict";
181
+ RateLimiter = class {
182
+ config;
183
+ logger;
184
+ limits = /* @__PURE__ */ new Map();
185
+ refillInterval = null;
186
+ constructor(config, logger2) {
187
+ this.config = config;
188
+ this.logger = logger2;
189
+ this.startRefill();
190
+ }
191
+ startRefill() {
192
+ this.refillInterval = setInterval(() => {
193
+ const now = Date.now();
194
+ for (const [key, data] of this.limits.entries()) {
195
+ const refillAmount = (now - data.lastRefill) / this.config.windowMs * this.config.max;
196
+ data.tokens = Math.min(this.config.max, data.tokens + refillAmount);
197
+ data.lastRefill = now;
198
+ if (data.tokens >= this.config.max && now - data.lastRefill > this.config.windowMs * 2) {
199
+ this.limits.delete(key);
200
+ }
201
+ }
202
+ }, 1e3);
203
+ }
204
+ async check(key) {
205
+ const now = Date.now();
206
+ let data = this.limits.get(key);
207
+ if (!data) {
208
+ data = {
209
+ tokens: this.config.max - 1,
210
+ lastRefill: now
211
+ };
212
+ this.limits.set(key, data);
213
+ return { allowed: true, remaining: data.tokens };
214
+ }
215
+ const refillAmount = (now - data.lastRefill) / this.config.windowMs * this.config.max;
216
+ data.tokens = Math.min(this.config.max, data.tokens + refillAmount);
217
+ data.lastRefill = now;
218
+ if (data.tokens < 1) {
219
+ this.logger.warn(`Rate limit exceeded for key: ${key}`);
220
+ return {
221
+ allowed: false,
222
+ reason: this.config.message || "Too many requests",
223
+ remaining: 0
224
+ };
225
+ }
226
+ data.tokens -= 1;
227
+ return { allowed: true, remaining: Math.floor(data.tokens) };
228
+ }
229
+ middleware() {
230
+ return async (req, res, next) => {
231
+ const key = this.config.keyGenerator ? this.config.keyGenerator(req) : req.ip || req.connection?.remoteAddress || "unknown";
232
+ const result = await this.check(key);
233
+ res.setHeader("X-RateLimit-Limit", this.config.max);
234
+ res.setHeader("X-RateLimit-Remaining", result.remaining ?? 0);
235
+ if (!result.allowed) {
236
+ return res.status(this.config.statusCode || 429).json({
237
+ error: "Too Many Requests",
238
+ message: result.reason
239
+ });
240
+ }
241
+ next();
242
+ };
243
+ }
244
+ getStats() {
245
+ return {
246
+ activeKeys: this.limits.size
247
+ };
248
+ }
249
+ reset(key) {
250
+ if (key) {
251
+ this.limits.delete(key);
252
+ } else {
253
+ this.limits.clear();
254
+ }
255
+ }
256
+ destroy() {
257
+ if (this.refillInterval) {
258
+ clearInterval(this.refillInterval);
259
+ }
260
+ this.limits.clear();
261
+ }
262
+ };
263
+ RateLimiter_default = RateLimiter;
264
+ }
265
+ });
266
+
267
+ // src/modules/security-headers/SecurityHeaders.ts
268
+ var SecurityHeaders_exports = {};
269
+ __export(SecurityHeaders_exports, {
270
+ SecurityHeaders: () => SecurityHeaders,
271
+ default: () => SecurityHeaders_default
272
+ });
273
+ var SecurityHeaders, SecurityHeaders_default;
274
+ var init_SecurityHeaders = __esm({
275
+ "src/modules/security-headers/SecurityHeaders.ts"() {
276
+ "use strict";
277
+ SecurityHeaders = class {
278
+ config;
279
+ logger;
280
+ constructor(config, logger2) {
281
+ this.config = config;
282
+ this.logger = logger2;
283
+ }
284
+ buildCSP() {
285
+ const csp = this.config.contentSecurityPolicy;
286
+ if (!csp) return "";
287
+ const parts = [];
288
+ if (csp.defaultSrc) parts.push(`default-src ${csp.defaultSrc.join(" ")}`);
289
+ if (csp.scriptSrc) parts.push(`script-src ${csp.scriptSrc.join(" ")}`);
290
+ if (csp.styleSrc) parts.push(`style-src ${csp.styleSrc.join(" ")}`);
291
+ if (csp.imgSrc) parts.push(`img-src ${csp.imgSrc.join(" ")}`);
292
+ if (csp.connectSrc) parts.push(`connect-src ${csp.connectSrc.join(" ")}`);
293
+ if (csp.fontSrc) parts.push(`font-src ${csp.fontSrc.join(" ")}`);
294
+ if (csp.objectSrc) parts.push(`object-src ${csp.objectSrc.join(" ")}`);
295
+ if (csp.mediaSrc) parts.push(`media-src ${csp.mediaSrc.join(" ")}`);
296
+ if (csp.frameSrc) parts.push(`frame-src ${csp.frameSrc.join(" ")}`);
297
+ return parts.join("; ");
298
+ }
299
+ buildHSTS() {
300
+ const hsts = this.config.hsts;
301
+ if (!hsts || !hsts.enabled) return "";
302
+ let value = `max-age=${hsts.maxAge}`;
303
+ if (hsts.includeSubDomains) value += "; includeSubDomains";
304
+ if (hsts.preload) value += "; preload";
305
+ return value;
306
+ }
307
+ middleware() {
308
+ return (req, res, next) => {
309
+ if (this.config.xFrameOptions) {
310
+ res.setHeader("X-Frame-Options", this.config.xFrameOptions);
311
+ }
312
+ if (this.config.xContentTypeOptions) {
313
+ res.setHeader("X-Content-Type-Options", this.config.xContentTypeOptions);
314
+ }
315
+ if (this.config.xssProtection) {
316
+ res.setHeader("X-XSS-Protection", this.config.xssProtection);
317
+ }
318
+ if (this.config.referrerPolicy) {
319
+ res.setHeader("Referrer-Policy", this.config.referrerPolicy);
320
+ }
321
+ if (this.config.contentSecurityPolicy) {
322
+ const csp = this.buildCSP();
323
+ if (csp) {
324
+ res.setHeader("Content-Security-Policy", csp);
325
+ }
326
+ }
327
+ if (this.config.hsts && this.config.hsts.enabled) {
328
+ const hsts = this.buildHSTS();
329
+ if (hsts) {
330
+ res.setHeader("Strict-Transport-Security", hsts);
331
+ }
332
+ }
333
+ if (this.config.permissionsPolicy && Object.keys(this.config.permissionsPolicy).length > 0) {
334
+ const permissions = Object.entries(this.config.permissionsPolicy).map(([key, value]) => `${key}=(${value.join(" ")})`).join(", ");
335
+ res.setHeader("Permissions-Policy", permissions);
336
+ }
337
+ res.setHeader("X-Permitted-Cross-Domain-Policies", "none");
338
+ res.setHeader("X-Download-Options", "noopen");
339
+ next();
340
+ };
341
+ }
342
+ getConfig() {
343
+ return this.config;
344
+ }
345
+ };
346
+ SecurityHeaders_default = SecurityHeaders;
347
+ }
348
+ });
349
+
350
+ // src/modules/cors/CORSManager.ts
351
+ var CORSManager_exports = {};
352
+ __export(CORSManager_exports, {
353
+ CORSManager: () => CORSManager,
354
+ default: () => CORSManager_default
355
+ });
356
+ var CORSManager, CORSManager_default;
357
+ var init_CORSManager = __esm({
358
+ "src/modules/cors/CORSManager.ts"() {
359
+ "use strict";
360
+ CORSManager = class {
361
+ config;
362
+ logger;
363
+ constructor(config, logger2) {
364
+ this.config = config;
365
+ this.logger = logger2;
366
+ }
367
+ isOriginAllowed(origin) {
368
+ if (!origin) return this.config.origins.includes("*");
369
+ return this.config.origins.some((allowed) => {
370
+ if (allowed === "*") return true;
371
+ if (allowed === origin) return true;
372
+ if (allowed.startsWith("*.")) {
373
+ const base = allowed.slice(2);
374
+ return origin.endsWith(base);
375
+ }
376
+ if (allowed.includes("*")) {
377
+ const regex = new RegExp("^" + allowed.replace(/\./g, "\\.").replace(/\*/g, ".*") + "$");
378
+ return regex.test(origin);
379
+ }
380
+ return false;
381
+ });
382
+ }
383
+ middleware() {
384
+ return (req, res, next) => {
385
+ const origin = req.headers.origin || req.headers.referer;
386
+ const method = req.method;
387
+ if (method === "OPTIONS") {
388
+ if (this.isOriginAllowed(origin)) {
389
+ if (this.config.credentials) {
390
+ res.setHeader("Access-Control-Allow-Origin", origin || "*");
391
+ } else {
392
+ res.setHeader("Access-Control-Allow-Origin", this.config.origins.includes("*") ? "*" : origin || "");
393
+ }
394
+ res.setHeader("Access-Control-Allow-Methods", this.config.methods?.join(", ") || "GET, POST, PUT, DELETE, OPTIONS");
395
+ res.setHeader("Access-Control-Allow-Headers", this.config.allowedHeaders?.join(", ") || "Content-Type, Authorization");
396
+ res.setHeader("Access-Control-Allow-Credentials", String(this.config.credentials));
397
+ if (this.config.maxAge) {
398
+ res.setHeader("Access-Control-Max-Age", String(this.config.maxAge));
399
+ }
400
+ if (this.config.exposedHeaders?.length) {
401
+ res.setHeader("Access-Control-Expose-Headers", this.config.exposedHeaders.join(", "));
402
+ }
403
+ if (this.config.preflightContinue) {
404
+ return next();
405
+ }
406
+ return res.status(204).end();
407
+ }
408
+ return res.status(403).json({ error: "CORS: Origin not allowed" });
409
+ }
410
+ if (this.isOriginAllowed(origin)) {
411
+ if (this.config.credentials) {
412
+ res.setHeader("Access-Control-Allow-Origin", origin || "*");
413
+ } else {
414
+ res.setHeader("Access-Control-Allow-Origin", this.config.origins.includes("*") ? "*" : origin || "");
415
+ }
416
+ if (this.config.credentials) {
417
+ res.setHeader("Access-Control-Allow-Credentials", "true");
418
+ }
419
+ if (this.config.exposedHeaders?.length) {
420
+ res.setHeader("Access-Control-Expose-Headers", this.config.exposedHeaders.join(", "));
421
+ }
422
+ }
423
+ next();
424
+ };
425
+ }
426
+ addOrigin(origin) {
427
+ if (!this.config.origins.includes(origin)) {
428
+ this.config.origins.push(origin);
429
+ this.logger.info(`Added CORS origin: ${origin}`);
430
+ }
431
+ }
432
+ removeOrigin(origin) {
433
+ const index = this.config.origins.indexOf(origin);
434
+ if (index > -1) {
435
+ this.config.origins.splice(index, 1);
436
+ this.logger.info(`Removed CORS origin: ${origin}`);
437
+ }
438
+ }
439
+ getConfig() {
440
+ return this.config;
441
+ }
442
+ };
443
+ CORSManager_default = CORSManager;
444
+ }
445
+ });
446
+
447
+ // src/modules/xss/XSSProtection.ts
448
+ var XSSProtection_exports = {};
449
+ __export(XSSProtection_exports, {
450
+ XSSProtection: () => XSSProtection,
451
+ default: () => XSSProtection_default
452
+ });
453
+ var XSSProtection, XSSProtection_default;
454
+ var init_XSSProtection = __esm({
455
+ "src/modules/xss/XSSProtection.ts"() {
456
+ "use strict";
457
+ XSSProtection = class {
458
+ config;
459
+ logger;
460
+ patterns = {
461
+ low: /<script|javascript:|on\w+\s*=/i,
462
+ medium: /<script|javascript:|on\w+\s*=|<iframe|<object|<embed|eval\(|innerHTML|outerHTML/i,
463
+ strict: /<script|javascript:|on\w+\s*=|<iframe|<object|<embed|<embed|<applet|<meta|<link|<style|<body|<img|<input|<form|<select|<textarea|<datalist|<keygen|<output|<details|<dialog|<menu|<menuitem|eval\(|innerHTML|outerHTML|document\.|window\.|alert\(|confirm\(|prompt\(/i
464
+ };
465
+ customRules = [];
466
+ constructor(config, logger2) {
467
+ this.config = config;
468
+ this.logger = logger2;
469
+ if (this.config.customRules) {
470
+ for (const [name, pattern] of Object.entries(this.config.customRules)) {
471
+ this.customRules.push({ pattern: new RegExp(pattern, "i"), name });
472
+ }
473
+ }
474
+ }
475
+ sanitizeString(str) {
476
+ if (typeof str !== "string") return str;
477
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#x27;").replace(/\//g, "&#x2F;");
478
+ }
479
+ check(data) {
480
+ const result = this.deepCheck(data);
481
+ if (result.detected) {
482
+ this.logger.warn("XSS attempt detected", {
483
+ patterns: result.patterns,
484
+ level: this.config.level
485
+ });
486
+ if (this.config.level === "strict") {
487
+ return {
488
+ allowed: false,
489
+ reason: `XSS protection: Suspicious input detected - ${result.patterns.join(", ")}`
490
+ };
491
+ }
492
+ }
493
+ return { allowed: true };
494
+ }
495
+ deepCheck(obj, path3 = "") {
496
+ if (obj === null || obj === void 0) {
497
+ return { detected: false, patterns: [] };
498
+ }
499
+ if (typeof obj === "string") {
500
+ return this.checkString(obj, path3);
501
+ }
502
+ if (typeof obj === "object") {
503
+ const patterns = [];
504
+ let detected = false;
505
+ for (const key of Object.keys(obj)) {
506
+ const result = this.deepCheck(obj[key], path3 ? `${path3}.${key}` : key);
507
+ if (result.detected) {
508
+ detected = true;
509
+ patterns.push(...result.patterns);
510
+ }
511
+ }
512
+ return { detected, patterns };
513
+ }
514
+ return { detected: false, patterns: [] };
515
+ }
516
+ checkString(str, path3) {
517
+ const patterns = [];
518
+ const basePattern = this.patterns[this.config.level];
519
+ if (basePattern.test(str)) {
520
+ patterns.push(`base-${this.config.level}`);
521
+ }
522
+ for (const rule of this.customRules) {
523
+ if (rule.pattern.test(str)) {
524
+ patterns.push(rule.name);
525
+ }
526
+ }
527
+ return { detected: patterns.length > 0, patterns };
528
+ }
529
+ sanitize(data) {
530
+ if (typeof data === "string") {
531
+ return this.sanitizeString(data);
532
+ }
533
+ if (Array.isArray(data)) {
534
+ return data.map((item) => this.sanitize(item));
535
+ }
536
+ if (typeof data === "object" && data !== null) {
537
+ const sanitized = {};
538
+ for (const [key, value] of Object.entries(data)) {
539
+ sanitized[key] = this.sanitize(value);
540
+ }
541
+ return sanitized;
542
+ }
543
+ return data;
544
+ }
545
+ middleware() {
546
+ return (req, res, next) => {
547
+ if (req.body) {
548
+ const result = this.check(req.body);
549
+ if (!result.allowed) {
550
+ return res.status(400).json({
551
+ error: "Bad Request",
552
+ message: result.reason
553
+ });
554
+ }
555
+ req.body = this.sanitize(req.body);
556
+ }
557
+ if (req.query) {
558
+ const result = this.check(req.query);
559
+ if (!result.allowed) {
560
+ return res.status(400).json({
561
+ error: "Bad Request",
562
+ message: result.reason
563
+ });
564
+ }
565
+ }
566
+ next();
567
+ };
568
+ }
569
+ addCustomRule(name, pattern) {
570
+ this.customRules.push({ pattern: new RegExp(pattern, "i"), name });
571
+ }
572
+ getConfig() {
573
+ return this.config;
574
+ }
575
+ };
576
+ XSSProtection_default = XSSProtection;
577
+ }
578
+ });
579
+
580
+ // src/modules/csrf/CSRFProtection.ts
581
+ var CSRFProtection_exports = {};
582
+ __export(CSRFProtection_exports, {
583
+ CSRFProtection: () => CSRFProtection,
584
+ default: () => CSRFProtection_default
585
+ });
586
+ var import_crypto, CSRFProtection, CSRFProtection_default;
587
+ var init_CSRFProtection = __esm({
588
+ "src/modules/csrf/CSRFProtection.ts"() {
589
+ "use strict";
590
+ import_crypto = __toESM(require("crypto"), 1);
591
+ CSRFProtection = class {
592
+ config;
593
+ logger;
594
+ tokens = /* @__PURE__ */ new Map();
595
+ cleanupInterval = null;
596
+ constructor(config, logger2) {
597
+ this.config = config;
598
+ this.logger = logger2;
599
+ this.startCleanup();
600
+ }
601
+ startCleanup() {
602
+ this.cleanupInterval = setInterval(() => {
603
+ const now = Date.now();
604
+ const expiryMs = 36e5;
605
+ for (const [key, token] of this.tokens.entries()) {
606
+ if (now - this.hashToTimestamp(token) > expiryMs) {
607
+ this.tokens.delete(key);
608
+ }
609
+ }
610
+ }, 3e5);
611
+ }
612
+ hashToTimestamp(token) {
613
+ const hash = token.substring(0, 8);
614
+ return parseInt(hash, 16) * 1e3;
615
+ }
616
+ generateToken(sessionId) {
617
+ const timestamp = Math.floor(Date.now() / 1e3).toString(16);
618
+ const random = import_crypto.default.randomBytes(this.config.tokenLength || 32).toString("hex");
619
+ const token = timestamp + random;
620
+ this.tokens.set(sessionId, token);
621
+ return token;
622
+ }
623
+ validateToken(token, sessionId) {
624
+ const storedToken = this.tokens.get(sessionId);
625
+ if (!storedToken) return false;
626
+ if (storedToken !== token) {
627
+ this.logger.warn("CSRF token mismatch", { sessionId });
628
+ return false;
629
+ }
630
+ const tokenTimestamp = this.hashToTimestamp(token);
631
+ const now = Date.now();
632
+ const maxAge = 36e5;
633
+ if (now - tokenTimestamp > maxAge) {
634
+ this.logger.warn("CSRF token expired", { sessionId });
635
+ this.tokens.delete(sessionId);
636
+ return false;
637
+ }
638
+ return true;
639
+ }
640
+ middleware() {
641
+ return (req, res, next) => {
642
+ const sessionId = req.session?.id || req.headers["x-session-id"] || req.ip;
643
+ if (req.method === "GET" || req.method === "HEAD" || req.method === "OPTIONS") {
644
+ const token2 = this.generateToken(sessionId);
645
+ if (this.config.cookie) {
646
+ res.cookie(this.config.cookie.name || "_csrf", token2, {
647
+ httpOnly: this.config.cookie.httpOnly !== false,
648
+ secure: this.config.cookie.secure !== false,
649
+ sameSite: this.config.cookie.sameSite || "strict",
650
+ maxAge: 36e5
651
+ });
652
+ }
653
+ res.locals.csrfToken = token2;
654
+ return next();
655
+ }
656
+ const token = req.body?.csrf || req.headers["x-csrf-token"] || req.headers["x-xsrf-token"];
657
+ if (!token) {
658
+ this.logger.warn("CSRF token missing", {
659
+ method: req.method,
660
+ path: req.path
661
+ });
662
+ return res.status(403).json({
663
+ error: "Forbidden",
664
+ message: "CSRF token missing"
665
+ });
666
+ }
667
+ if (!this.validateToken(token, sessionId)) {
668
+ return res.status(403).json({
669
+ error: "Forbidden",
670
+ message: "Invalid or expired CSRF token"
671
+ });
672
+ }
673
+ next();
674
+ };
675
+ }
676
+ getToken(sessionId) {
677
+ return this.generateToken(sessionId);
678
+ }
679
+ destroy() {
680
+ if (this.cleanupInterval) {
681
+ clearInterval(this.cleanupInterval);
682
+ }
683
+ this.tokens.clear();
684
+ }
685
+ };
686
+ CSRFProtection_default = CSRFProtection;
687
+ }
688
+ });
689
+
690
+ // src/modules/sql-injection/SQLInjectionProtection.ts
691
+ var SQLInjectionProtection_exports = {};
692
+ __export(SQLInjectionProtection_exports, {
693
+ SQLInjectionProtection: () => SQLInjectionProtection,
694
+ default: () => SQLInjectionProtection_default
695
+ });
696
+ var SQLInjectionProtection, SQLInjectionProtection_default;
697
+ var init_SQLInjectionProtection = __esm({
698
+ "src/modules/sql-injection/SQLInjectionProtection.ts"() {
699
+ "use strict";
700
+ SQLInjectionProtection = class {
701
+ config;
702
+ logger;
703
+ customPatterns = [];
704
+ defaultPatterns = [
705
+ /(\b(SELECT|INSERT|UPDATE|DELETE|DROP|UNION|ALTER|CREATE|TRUNCATE)\b.*\b(FROM|INTO|TABLE|DATABASE)\b)/i,
706
+ /((\%27)|(\'))((\%6F)|o|(\%4F))((\%72)|r|(\%52))/i,
707
+ /((\%27)|(\'))((\%6F)|o|(\%4F))((\%72)|r|(\%52))/i,
708
+ /((\%27)|(\'))\s*((\%6F)|o|(\%4F))((\%72)|r|(\%52))/i,
709
+ /(char\s*\(\s*\d+\s*\))/i,
710
+ /(\%77here\s+\w+\s*=\s*\w+)/i,
711
+ /(\hre\b.*\b\w+\s*=\s*\w+)/i,
712
+ /(union\s+select\s+)/i,
713
+ /(union\s+all\s+select\s+)/i,
714
+ /(select\s+.*\s+from\s+)/i,
715
+ /(insert\s+into\s+)/i,
716
+ /(delete\s+from\s+)/i,
717
+ /(update\s+.*\s+set\s+)/i,
718
+ /(drop\s+table\s+)/i,
719
+ /(drop\s+database\s+)/i,
720
+ /(exec\s*\(|execute\s*\(|xp_)/i,
721
+ /(0x[0-9a-fA-F]+)/i,
722
+ /(\bOR\b\s+\b\d+\s*=\s*\d+)/i,
723
+ /(\bAND\b\s+\b\d+\s*=\s*\d+)/i,
724
+ /(\'\s*OR\s*\'\s*\'\s*=\s*\'\s*)/i,
725
+ /(\'\s*OR\s+\w+\s*=\s*\w+)/i,
726
+ /(--\s*$)/m,
727
+ /(;\s*drop\s+)/i,
728
+ /(;\s*exec\s+)/i,
729
+ /(\/\*.*\*\/)/,
730
+ /(\bhaving\b\s+\w+\s*[=<>])/i,
731
+ /(\blimit\b\s+\d+(,\s*\d+)?)/i
732
+ ];
733
+ constructor(config, logger2) {
734
+ this.config = config;
735
+ this.logger = logger2;
736
+ if (this.config.customPatterns) {
737
+ for (const pattern of this.config.customPatterns) {
738
+ this.customPatterns.push(new RegExp(pattern, "i"));
739
+ }
740
+ }
741
+ }
742
+ check(data) {
743
+ const allInput = this.extractInput(data);
744
+ const detectedPatterns = [];
745
+ for (const input of allInput) {
746
+ if (typeof input !== "string") continue;
747
+ for (const pattern of this.defaultPatterns) {
748
+ if (pattern.test(input)) {
749
+ detectedPatterns.push(`default-${pattern.toString()}`);
750
+ }
751
+ }
752
+ for (const pattern of this.customPatterns) {
753
+ if (pattern.test(input)) {
754
+ detectedPatterns.push(`custom-${pattern.toString()}`);
755
+ }
756
+ }
757
+ if (this.hasSuspiciousPatterns(input)) {
758
+ detectedPatterns.push("suspicious-comments-union");
759
+ }
760
+ }
761
+ if (detectedPatterns.length > 0) {
762
+ this.logger.warn("SQL Injection attempt detected", {
763
+ ip: data.ip,
764
+ url: data.url,
765
+ patterns: detectedPatterns
766
+ });
767
+ if (this.config.blockOnDetect || !this.config.logOnly) {
768
+ return {
769
+ allowed: false,
770
+ reason: "SQL Injection protection: Suspicious input detected",
771
+ detected: detectedPatterns.join(", ")
772
+ };
773
+ }
774
+ }
775
+ return { allowed: true };
776
+ }
777
+ extractInput(data) {
778
+ const inputs = [];
779
+ if (data.body) {
780
+ inputs.push(...this.flattenObject(data.body));
781
+ }
782
+ if (data.url) {
783
+ const url = new URL(data.url, "http://localhost");
784
+ for (const [, value] of url.searchParams) {
785
+ inputs.push(value);
786
+ }
787
+ }
788
+ return inputs;
789
+ }
790
+ flattenObject(obj, prefix = "") {
791
+ const result = [];
792
+ if (obj === null || obj === void 0) {
793
+ return result;
794
+ }
795
+ if (typeof obj === "string" || typeof obj === "number" || typeof obj === "boolean") {
796
+ result.push(String(obj));
797
+ return result;
798
+ }
799
+ if (Array.isArray(obj)) {
800
+ for (const item of obj) {
801
+ result.push(...this.flattenObject(item));
802
+ }
803
+ return result;
804
+ }
805
+ if (typeof obj === "object") {
806
+ for (const [key, value] of Object.entries(obj)) {
807
+ result.push(...this.flattenObject(value, `${prefix}.${key}`));
808
+ }
809
+ }
810
+ return result;
811
+ }
812
+ hasSuspiciousPatterns(input) {
813
+ const suspicious = [
814
+ /\/\*.*\*\//,
815
+ /;\s*(drop|insert|update|delete|create|alter)/i,
816
+ /@@\w+/,
817
+ /0x[0-9a-fA-F]{8,}/,
818
+ /waitfor\s+delay/i,
819
+ /benchmark\s*\(/i,
820
+ /sleep\s*\(/i
821
+ ];
822
+ return suspicious.some((pattern) => pattern.test(input));
823
+ }
824
+ middleware() {
825
+ return (req, res, next) => {
826
+ const data = {
827
+ ip: req.ip || req.connection?.remoteAddress,
828
+ method: req.method,
829
+ url: req.url,
830
+ headers: req.headers,
831
+ body: req.body,
832
+ userAgent: req.headers?.["user-agent"]
833
+ };
834
+ const result = this.check(data);
835
+ if (!result.allowed) {
836
+ if (this.config.blockOnDetect) {
837
+ return res.status(403).json({
838
+ error: "Forbidden",
839
+ message: result.reason
840
+ });
841
+ } else {
842
+ this.logger.warn("SQL Injection detected (log only)", { reason: result.reason });
843
+ }
844
+ }
845
+ next();
846
+ };
847
+ }
848
+ addPattern(pattern) {
849
+ this.customPatterns.push(new RegExp(pattern, "i"));
850
+ }
851
+ getConfig() {
852
+ return this.config;
853
+ }
854
+ };
855
+ SQLInjectionProtection_default = SQLInjectionProtection;
856
+ }
857
+ });
858
+
859
+ // src/modules/bot-protection/BotProtection.ts
860
+ var BotProtection_exports = {};
861
+ __export(BotProtection_exports, {
862
+ BotProtection: () => BotProtection,
863
+ default: () => BotProtection_default
864
+ });
865
+ var BotProtection, BotProtection_default;
866
+ var init_BotProtection = __esm({
867
+ "src/modules/bot-protection/BotProtection.ts"() {
868
+ "use strict";
869
+ BotProtection = class {
870
+ config;
871
+ logger;
872
+ challengeCache = /* @__PURE__ */ new Map();
873
+ cleanupInterval = null;
874
+ knownBotPatterns = [
875
+ /bot/i,
876
+ /crawler/i,
877
+ /spider/i,
878
+ /scraper/i,
879
+ /curl/i,
880
+ /wget/i,
881
+ /python-requests/i,
882
+ /node-fetch/i,
883
+ /go-http/i,
884
+ /java\//i,
885
+ /httpclient/i,
886
+ /fetch/i
887
+ ];
888
+ constructor(config, logger2) {
889
+ this.config = config;
890
+ this.logger = logger2;
891
+ this.startCleanup();
892
+ }
893
+ startCleanup() {
894
+ this.cleanupInterval = setInterval(() => {
895
+ const now = Date.now();
896
+ for (const [key, value] of this.challengeCache.entries()) {
897
+ if (now > value.expires) {
898
+ this.challengeCache.delete(key);
899
+ }
900
+ }
901
+ }, 6e4);
902
+ }
903
+ isKnownBot(userAgent) {
904
+ for (const pattern of this.knownBotPatterns) {
905
+ if (pattern.test(userAgent)) {
906
+ return true;
907
+ }
908
+ }
909
+ if (this.config.blockUserAgents && this.config.blockUserAgents.length > 0) {
910
+ for (const pattern of this.config.blockUserAgents) {
911
+ if (new RegExp(pattern, "i").test(userAgent)) {
912
+ return true;
913
+ }
914
+ }
915
+ }
916
+ if (this.config.allowUserAgents && this.config.allowUserAgents.length > 0) {
917
+ for (const pattern of this.config.allowUserAgents) {
918
+ if (new RegExp(pattern, "i").test(userAgent)) {
919
+ return false;
920
+ }
921
+ }
922
+ }
923
+ return false;
924
+ }
925
+ generateChallenge() {
926
+ const a = Math.floor(Math.random() * 10) + 1;
927
+ const b = Math.floor(Math.random() * 10) + 1;
928
+ const answer = (a + b).toString();
929
+ const question = `${a} + ${b} = ?`;
930
+ return { question, answer };
931
+ }
932
+ async check(data) {
933
+ const userAgent = data.userAgent || "";
934
+ const ip = data.ip || "unknown";
935
+ if (this.isKnownBot(userAgent)) {
936
+ if (this.config.challengeEnabled) {
937
+ const cacheKey = `${ip}:${data.url}`;
938
+ const challengeData = this.challengeCache.get(cacheKey);
939
+ if (!challengeData || !challengeData.solved) {
940
+ const challenge = this.generateChallenge();
941
+ this.challengeCache.set(cacheKey, {
942
+ solved: false,
943
+ expires: Date.now() + this.config.challengeExpiryMs
944
+ });
945
+ this.logger.info("Bot challenge issued", { ip, userAgent });
946
+ return {
947
+ allowed: false,
948
+ reason: "Bot verification required",
949
+ challenge: {
950
+ question: challenge.question,
951
+ cookieName: this.config.challengeCookieName
952
+ }
953
+ };
954
+ }
955
+ } else {
956
+ return {
957
+ allowed: false,
958
+ reason: "Bot detected and blocked"
959
+ };
960
+ }
961
+ }
962
+ return { allowed: true };
963
+ }
964
+ verifyChallenge(ip, url, answer) {
965
+ const cacheKey = `${ip}:${url}`;
966
+ const challengeData = this.challengeCache.get(cacheKey);
967
+ if (!challengeData) {
968
+ return false;
969
+ }
970
+ const storedChallenge = this.challengeCache.get(cacheKey + ":answer");
971
+ if (!storedChallenge) {
972
+ const newChallenge = this.generateChallenge();
973
+ this.challengeCache.set(cacheKey + ":answer", {
974
+ solved: false,
975
+ expires: Date.now() + 6e4
976
+ });
977
+ return newChallenge.answer === answer;
978
+ }
979
+ return storedChallenge.solved;
980
+ }
981
+ solveChallenge(ip, url) {
982
+ const cacheKey = `${ip}:${url}`;
983
+ const data = this.challengeCache.get(cacheKey);
984
+ if (data) {
985
+ data.solved = true;
986
+ this.challengeCache.set(cacheKey, data);
987
+ }
988
+ }
989
+ middleware() {
990
+ return async (req, res, next) => {
991
+ const ip = req.ip || req.connection?.remoteAddress || "unknown";
992
+ const userAgent = req.headers?.["user-agent"] || "";
993
+ const data = {
994
+ ip,
995
+ method: req.method,
996
+ url: req.url,
997
+ headers: req.headers,
998
+ userAgent
999
+ };
1000
+ const challengeCookie = req.cookies?.[this.config.challengeCookieName || "webarmor_challenge"];
1001
+ if (challengeCookie) {
1002
+ try {
1003
+ const decoded = Buffer.from(challengeCookie, "base64").toString();
1004
+ const [storedIp, timestamp] = decoded.split(":");
1005
+ if (storedIp === ip && Date.now() - parseInt(timestamp) < this.config.challengeExpiryMs) {
1006
+ return next();
1007
+ }
1008
+ } catch (e) {
1009
+ }
1010
+ }
1011
+ const result = await this.check(data);
1012
+ if (!result.allowed) {
1013
+ if (result.challenge) {
1014
+ res.setHeader("X-WebArmor-Challenge", "required");
1015
+ return res.status(403).json({
1016
+ error: "Bot Verification Required",
1017
+ message: result.reason,
1018
+ challenge: result.challenge
1019
+ });
1020
+ }
1021
+ return res.status(403).json({
1022
+ error: "Forbidden",
1023
+ message: result.reason
1024
+ });
1025
+ }
1026
+ next();
1027
+ };
1028
+ }
1029
+ getConfig() {
1030
+ return this.config;
1031
+ }
1032
+ destroy() {
1033
+ if (this.cleanupInterval) {
1034
+ clearInterval(this.cleanupInterval);
1035
+ }
1036
+ this.challengeCache.clear();
1037
+ }
1038
+ };
1039
+ BotProtection_default = BotProtection;
1040
+ }
1041
+ });
1042
+
1043
+ // src/modules/ip-filter/IPFilter.ts
1044
+ var IPFilter_exports = {};
1045
+ __export(IPFilter_exports, {
1046
+ IPFilter: () => IPFilter,
1047
+ default: () => IPFilter_default
1048
+ });
1049
+ var IPFilter, IPFilter_default;
1050
+ var init_IPFilter = __esm({
1051
+ "src/modules/ip-filter/IPFilter.ts"() {
1052
+ "use strict";
1053
+ IPFilter = class {
1054
+ config;
1055
+ logger;
1056
+ customBlockedIPs = /* @__PURE__ */ new Set();
1057
+ constructor(config, logger2) {
1058
+ this.config = config;
1059
+ this.logger = logger2;
1060
+ }
1061
+ parseIP(ip) {
1062
+ if (ip.includes("/")) {
1063
+ return { ip, isIPv6: ip.includes(":"), isSubnet: true };
1064
+ }
1065
+ return { ip, isIPv6: ip.includes(":"), isSubnet: false };
1066
+ }
1067
+ isIPInRange(ip, range) {
1068
+ if (!range.includes("/")) {
1069
+ return ip === range;
1070
+ }
1071
+ const [subnet, mask] = range.split("/");
1072
+ let maskNum = parseInt(mask);
1073
+ if (maskNum >= 32) {
1074
+ return ip === subnet;
1075
+ }
1076
+ const ipParts = ip.split(".").map(Number);
1077
+ const subnetParts = subnet.split(".").map(Number);
1078
+ const maskParts = [];
1079
+ for (let i = 0; i < 4; i++) {
1080
+ if (maskNum >= 8) {
1081
+ maskParts.push(255);
1082
+ maskNum -= 8;
1083
+ } else if (maskNum > 0) {
1084
+ maskParts.push(256 - Math.pow(2, 8 - maskNum));
1085
+ maskNum = 0;
1086
+ } else {
1087
+ maskParts.push(0);
1088
+ }
1089
+ }
1090
+ for (let i = 0; i < 4; i++) {
1091
+ if ((ipParts[i] & maskParts[i]) !== (subnetParts[i] & maskParts[i])) {
1092
+ return false;
1093
+ }
1094
+ }
1095
+ return true;
1096
+ }
1097
+ getClientIP(req) {
1098
+ if (this.config.useXForwardedFor) {
1099
+ const forwarded = req.headers?.["x-forwarded-for"];
1100
+ if (forwarded) {
1101
+ return forwarded.split(",")[0].trim();
1102
+ }
1103
+ }
1104
+ return req.ip || req.connection?.remoteAddress || req.socket?.remoteAddress || "127.0.0.1";
1105
+ }
1106
+ async check(ip) {
1107
+ const clientIP = this.parseIP(ip);
1108
+ if (this.config.whitelist.length > 0) {
1109
+ const isWhitelisted = this.config.whitelist.some(
1110
+ (range) => this.isIPInRange(ip, range)
1111
+ );
1112
+ if (!isWhitelisted) {
1113
+ if (this.config.logBlocked) {
1114
+ this.logger.warn("IP not in whitelist, blocked", { ip });
1115
+ }
1116
+ return {
1117
+ allowed: false,
1118
+ reason: "IP not in whitelist"
1119
+ };
1120
+ }
1121
+ return { allowed: true };
1122
+ }
1123
+ const isBlocked = this.config.blacklist.some(
1124
+ (range) => this.isIPInRange(ip, range)
1125
+ );
1126
+ if (isBlocked || this.customBlockedIPs.has(ip)) {
1127
+ if (this.config.logBlocked) {
1128
+ this.logger.warn("IP blocked by blacklist", { ip });
1129
+ }
1130
+ return {
1131
+ allowed: false,
1132
+ reason: "IP is blocked"
1133
+ };
1134
+ }
1135
+ return { allowed: true };
1136
+ }
1137
+ addToWhitelist(ip) {
1138
+ if (!this.config.whitelist.includes(ip)) {
1139
+ this.config.whitelist.push(ip);
1140
+ this.logger.info(`Added to whitelist: ${ip}`);
1141
+ }
1142
+ }
1143
+ addToBlacklist(ip) {
1144
+ this.customBlockedIPs.add(ip);
1145
+ this.logger.info(`Added to blacklist: ${ip}`);
1146
+ }
1147
+ removeFromWhitelist(ip) {
1148
+ const index = this.config.whitelist.indexOf(ip);
1149
+ if (index > -1) {
1150
+ this.config.whitelist.splice(index, 1);
1151
+ this.logger.info(`Removed from whitelist: ${ip}`);
1152
+ }
1153
+ }
1154
+ removeFromBlacklist(ip) {
1155
+ this.customBlockedIPs.delete(ip);
1156
+ this.logger.info(`Removed from blacklist: ${ip}`);
1157
+ }
1158
+ clearCustomBlacklist() {
1159
+ this.customBlockedIPs.clear();
1160
+ this.logger.info("Custom blacklist cleared");
1161
+ }
1162
+ middleware() {
1163
+ return async (req, res, next) => {
1164
+ const ip = this.getClientIP(req);
1165
+ const result = await this.check(ip);
1166
+ if (!result.allowed) {
1167
+ return res.status(403).json({
1168
+ error: "Forbidden",
1169
+ message: result.reason
1170
+ });
1171
+ }
1172
+ next();
1173
+ };
1174
+ }
1175
+ getConfig() {
1176
+ return this.config;
1177
+ }
1178
+ };
1179
+ IPFilter_default = IPFilter;
1180
+ }
1181
+ });
1182
+
1183
+ // src/modules/file-block/FileBlocker.ts
1184
+ var FileBlocker_exports = {};
1185
+ __export(FileBlocker_exports, {
1186
+ FileBlocker: () => FileBlocker,
1187
+ default: () => FileBlocker_default
1188
+ });
1189
+ var FileBlocker, FileBlocker_default;
1190
+ var init_FileBlocker = __esm({
1191
+ "src/modules/file-block/FileBlocker.ts"() {
1192
+ "use strict";
1193
+ FileBlocker = class {
1194
+ config;
1195
+ logger;
1196
+ constructor(config, logger2) {
1197
+ this.config = config;
1198
+ this.logger = logger2;
1199
+ }
1200
+ isPathBlocked(path3) {
1201
+ const normalizedPath = path3.toLowerCase();
1202
+ for (const blocked of this.config.blockedPaths) {
1203
+ if (normalizedPath.includes(blocked.toLowerCase())) {
1204
+ return true;
1205
+ }
1206
+ }
1207
+ const sensitivePatterns = [
1208
+ /\.env$/i,
1209
+ /\.git\//i,
1210
+ /\.gitignore$/i,
1211
+ /config\.json$/i,
1212
+ /\.npmrc$/i,
1213
+ /\.aws\//i,
1214
+ /\.ssh\//i,
1215
+ /credentials\.json$/i,
1216
+ /\.pem$/i,
1217
+ /\.key$/i,
1218
+ /\.p12$/i
1219
+ ];
1220
+ for (const pattern of sensitivePatterns) {
1221
+ if (pattern.test(normalizedPath)) {
1222
+ return true;
1223
+ }
1224
+ }
1225
+ for (const sensitive of this.config.protectSensitive) {
1226
+ if (normalizedPath.includes(sensitive.toLowerCase())) {
1227
+ return true;
1228
+ }
1229
+ }
1230
+ const ext = normalizedPath.split(".").pop();
1231
+ if (ext && this.config.blockedExtensions.includes(`.${ext}`)) {
1232
+ return true;
1233
+ }
1234
+ if (this.config.customRules && this.config.customRules.length > 0) {
1235
+ for (const rule of this.config.customRules) {
1236
+ const regex = new RegExp(rule.pattern, "i");
1237
+ if (regex.test(normalizedPath)) {
1238
+ return rule.action === "block";
1239
+ }
1240
+ }
1241
+ }
1242
+ return false;
1243
+ }
1244
+ check(path3) {
1245
+ if (this.isPathBlocked(path3)) {
1246
+ this.logger.warn("File access blocked", { path: path3 });
1247
+ return {
1248
+ allowed: false,
1249
+ reason: "Access to this path is denied"
1250
+ };
1251
+ }
1252
+ return { allowed: true };
1253
+ }
1254
+ middleware() {
1255
+ return (req, res, next) => {
1256
+ const path3 = req.path || req.url;
1257
+ const result = this.check(path3);
1258
+ if (!result.allowed) {
1259
+ return res.status(403).json({
1260
+ error: "Forbidden",
1261
+ message: result.reason
1262
+ });
1263
+ }
1264
+ next();
1265
+ };
1266
+ }
1267
+ addBlockedPath(path3) {
1268
+ if (!this.config.blockedPaths.includes(path3)) {
1269
+ this.config.blockedPaths.push(path3);
1270
+ this.logger.info(`Added blocked path: ${path3}`);
1271
+ }
1272
+ }
1273
+ removeBlockedPath(path3) {
1274
+ const index = this.config.blockedPaths.indexOf(path3);
1275
+ if (index > -1) {
1276
+ this.config.blockedPaths.splice(index, 1);
1277
+ this.logger.info(`Removed blocked path: ${path3}`);
1278
+ }
1279
+ }
1280
+ addBlockedExtension(ext) {
1281
+ const formatted = ext.startsWith(".") ? ext : `.${ext}`;
1282
+ if (!this.config.blockedExtensions.includes(formatted)) {
1283
+ this.config.blockedExtensions.push(formatted);
1284
+ this.logger.info(`Added blocked extension: ${formatted}`);
1285
+ }
1286
+ }
1287
+ removeBlockedExtension(ext) {
1288
+ const formatted = ext.startsWith(".") ? ext : `.${ext}`;
1289
+ const index = this.config.blockedExtensions.indexOf(formatted);
1290
+ if (index > -1) {
1291
+ this.config.blockedExtensions.splice(index, 1);
1292
+ this.logger.info(`Removed blocked extension: ${formatted}`);
1293
+ }
1294
+ }
1295
+ addCustomRule(pattern, action) {
1296
+ this.config.customRules?.push({ pattern, action });
1297
+ this.logger.info(`Added custom rule: ${pattern} -> ${action}`);
1298
+ }
1299
+ getConfig() {
1300
+ return this.config;
1301
+ }
1302
+ };
1303
+ FileBlocker_default = FileBlocker;
1304
+ }
1305
+ });
1306
+
1307
+ // src/index.ts
1308
+ var index_exports = {};
1309
+ __export(index_exports, {
1310
+ ConfigEncryptor: () => ConfigEncryptor_default,
1311
+ DatabaseEncryptor: () => DatabaseEncryptor_default,
1312
+ FileEncryptor: () => FileEncryptor_default,
1313
+ Logger: () => Logger_default,
1314
+ SessionEncryptor: () => SessionEncryptor_default,
1315
+ Shield: () => Shield_default,
1316
+ createShield: () => createShield,
1317
+ default: () => index_default,
1318
+ security: () => security
1319
+ });
1320
+ module.exports = __toCommonJS(index_exports);
1321
+
1322
+ // src/core/Config.ts
1323
+ var defaultConfig = {
1324
+ framework: "express",
1325
+ debug: false,
1326
+ logLevel: "info",
1327
+ ddos: {
1328
+ enabled: true,
1329
+ maxRequests: 1e3,
1330
+ windowMs: 6e4,
1331
+ blockDurationMs: 3e5,
1332
+ scoreThreshold: 50
1333
+ },
1334
+ rateLimit: {
1335
+ enabled: true,
1336
+ windowMs: 6e4,
1337
+ max: 100,
1338
+ message: "Too many requests from this IP, please try again later.",
1339
+ statusCode: 429,
1340
+ skipSuccessfulRequests: false,
1341
+ keyGenerator: void 0
1342
+ },
1343
+ securityHeaders: {
1344
+ enabled: true,
1345
+ contentSecurityPolicy: {
1346
+ defaultSrc: ["'self'"],
1347
+ scriptSrc: ["'self'"],
1348
+ styleSrc: ["'self'"],
1349
+ imgSrc: ["'self'", "data:"]
1350
+ },
1351
+ hsts: {
1352
+ enabled: true,
1353
+ maxAge: 31536e3,
1354
+ includeSubDomains: true,
1355
+ preload: true
1356
+ },
1357
+ xFrameOptions: "DENY",
1358
+ xContentTypeOptions: "nosniff",
1359
+ xssProtection: "1; mode=block",
1360
+ referrerPolicy: "strict-origin-when-cross-origin",
1361
+ permissionsPolicy: {}
1362
+ },
1363
+ cors: {
1364
+ enabled: true,
1365
+ origins: ["*"],
1366
+ methods: ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"],
1367
+ allowedHeaders: ["Content-Type", "Authorization"],
1368
+ exposedHeaders: [],
1369
+ credentials: false,
1370
+ maxAge: 86400,
1371
+ preflightContinue: false
1372
+ },
1373
+ xss: {
1374
+ enabled: true,
1375
+ level: "medium",
1376
+ blockOnDetect: true,
1377
+ customRules: {}
1378
+ },
1379
+ csrf: {
1380
+ enabled: false,
1381
+ cookie: {
1382
+ name: "_csrf",
1383
+ httpOnly: true,
1384
+ secure: true,
1385
+ sameSite: "strict"
1386
+ },
1387
+ tokenLength: 32
1388
+ },
1389
+ sqlInjection: {
1390
+ enabled: true,
1391
+ blockOnDetect: true,
1392
+ logOnly: false,
1393
+ customPatterns: []
1394
+ },
1395
+ botProtection: {
1396
+ enabled: true,
1397
+ challengeEnabled: true,
1398
+ challengeExpiryMs: 36e5,
1399
+ challengeCookieName: "webarmor_challenge",
1400
+ allowUserAgents: [],
1401
+ blockUserAgents: []
1402
+ },
1403
+ ipFilter: {
1404
+ enabled: false,
1405
+ whitelist: [],
1406
+ blacklist: [],
1407
+ logBlocked: true,
1408
+ useXForwardedFor: true
1409
+ },
1410
+ fileBlock: {
1411
+ enabled: true,
1412
+ blockedPaths: [],
1413
+ blockedExtensions: [".exe", ".bat", ".cmd", ".sh", ".ps1"],
1414
+ protectSensitive: [".env", ".git", "config.json", ".npmrc", ".aws/credentials"],
1415
+ customRules: []
1416
+ },
1417
+ encryption: {
1418
+ enabled: false,
1419
+ algorithm: "aes-256-gcm",
1420
+ defaultKey: "",
1421
+ encryptEnv: false,
1422
+ encryptDatabase: false,
1423
+ encryptConfig: false,
1424
+ keyEnvVariable: "WEBARMOR_KEY"
1425
+ }
1426
+ };
1427
+
1428
+ // src/core/Logger.ts
1429
+ var Logger = class {
1430
+ level;
1431
+ logs = [];
1432
+ maxLogs = 1e3;
1433
+ debugMode = false;
1434
+ levels = {
1435
+ error: 0,
1436
+ warn: 1,
1437
+ info: 2,
1438
+ debug: 3
1439
+ };
1440
+ constructor(options) {
1441
+ this.level = options?.level || "info";
1442
+ this.debugMode = options?.debug || false;
1443
+ }
1444
+ shouldLog(level) {
1445
+ return this.levels[level] <= this.levels[this.level];
1446
+ }
1447
+ formatMessage(level, message, data) {
1448
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
1449
+ let formatted = `[${timestamp}] [${level.toUpperCase()}] ${message}`;
1450
+ if (data && this.debugMode) {
1451
+ formatted += ` ${JSON.stringify(data)}`;
1452
+ }
1453
+ return formatted;
1454
+ }
1455
+ error(message, data) {
1456
+ if (this.shouldLog("error")) {
1457
+ console.error(this.formatMessage("error", message, data));
1458
+ this.addLog("error", message, data);
1459
+ }
1460
+ }
1461
+ warn(message, data) {
1462
+ if (this.shouldLog("warn")) {
1463
+ console.warn(this.formatMessage("warn", message, data));
1464
+ this.addLog("warn", message, data);
1465
+ }
1466
+ }
1467
+ info(message, data) {
1468
+ if (this.shouldLog("info")) {
1469
+ console.log(this.formatMessage("info", message, data));
1470
+ this.addLog("info", message, data);
1471
+ }
1472
+ }
1473
+ debug(message, data) {
1474
+ if (this.shouldLog("debug")) {
1475
+ console.log(this.formatMessage("debug", message, data));
1476
+ this.addLog("debug", message, data);
1477
+ }
1478
+ }
1479
+ addLog(level, message, data) {
1480
+ this.logs.push({
1481
+ level,
1482
+ message,
1483
+ timestamp: /* @__PURE__ */ new Date(),
1484
+ data
1485
+ });
1486
+ if (this.logs.length > this.maxLogs) {
1487
+ this.logs.shift();
1488
+ }
1489
+ }
1490
+ getLogs(level) {
1491
+ if (level) {
1492
+ return this.logs.filter((log) => log.level === level);
1493
+ }
1494
+ return [...this.logs];
1495
+ }
1496
+ clearLogs() {
1497
+ this.logs = [];
1498
+ }
1499
+ setLevel(level) {
1500
+ this.level = level;
1501
+ }
1502
+ setDebug(debug) {
1503
+ this.debugMode = debug;
1504
+ }
1505
+ };
1506
+ var logger = new Logger();
1507
+ var Logger_default = Logger;
1508
+
1509
+ // src/core/Shield.ts
1510
+ var Shield = class {
1511
+ config;
1512
+ logger;
1513
+ stats = {
1514
+ requests: 0,
1515
+ blocked: 0,
1516
+ allowed: 0,
1517
+ ddosBlocked: 0,
1518
+ rateLimited: 0,
1519
+ xssBlocked: 0,
1520
+ sqlInjectionBlocked: 0,
1521
+ botBlocked: 0,
1522
+ ipBlocked: 0,
1523
+ fileBlocked: 0,
1524
+ startTime: Date.now()
1525
+ };
1526
+ modules = /* @__PURE__ */ new Map();
1527
+ enabledModules = /* @__PURE__ */ new Set();
1528
+ constructor(userConfig) {
1529
+ this.config = { ...defaultConfig, ...userConfig };
1530
+ this.logger = new Logger_default({
1531
+ level: this.config.logLevel,
1532
+ debug: this.config.debug
1533
+ });
1534
+ }
1535
+ async initialize() {
1536
+ this.logger.info("WebArmor initializing...", { framework: this.config.framework });
1537
+ await this.initializeModules();
1538
+ this.logger.info("WebArmor initialized successfully");
1539
+ }
1540
+ async initializeModules() {
1541
+ if (this.config.ddos.enabled) {
1542
+ try {
1543
+ const { DDoSProtection: DDoSProtection2 } = await Promise.resolve().then(() => (init_DDoSProtection(), DDoSProtection_exports));
1544
+ const ddos = new DDoSProtection2(this.config.ddos, this.logger);
1545
+ this.modules.set("ddos", ddos);
1546
+ this.enabledModules.add("ddos");
1547
+ } catch (e) {
1548
+ this.logger.error("Failed to initialize DDoS protection", e);
1549
+ }
1550
+ }
1551
+ if (this.config.rateLimit.enabled) {
1552
+ try {
1553
+ const { RateLimiter: RateLimiter2 } = await Promise.resolve().then(() => (init_RateLimiter(), RateLimiter_exports));
1554
+ const rateLimit = new RateLimiter2(this.config.rateLimit, this.logger);
1555
+ this.modules.set("rateLimit", rateLimit);
1556
+ this.enabledModules.add("rateLimit");
1557
+ } catch (e) {
1558
+ this.logger.error("Failed to initialize Rate Limiter", e);
1559
+ }
1560
+ }
1561
+ if (this.config.securityHeaders && typeof this.config.securityHeaders !== "boolean" && this.config.securityHeaders.enabled) {
1562
+ try {
1563
+ const { SecurityHeaders: SecurityHeaders2 } = await Promise.resolve().then(() => (init_SecurityHeaders(), SecurityHeaders_exports));
1564
+ const headers = new SecurityHeaders2(this.config.securityHeaders, this.logger);
1565
+ this.modules.set("securityHeaders", headers);
1566
+ this.enabledModules.add("securityHeaders");
1567
+ } catch (e) {
1568
+ this.logger.error("Failed to initialize Security Headers", e);
1569
+ }
1570
+ }
1571
+ if (this.config.cors.enabled) {
1572
+ try {
1573
+ const { CORSManager: CORSManager2 } = await Promise.resolve().then(() => (init_CORSManager(), CORSManager_exports));
1574
+ const cors = new CORSManager2(this.config.cors, this.logger);
1575
+ this.modules.set("cors", cors);
1576
+ this.enabledModules.add("cors");
1577
+ } catch (e) {
1578
+ this.logger.error("Failed to initialize CORS", e);
1579
+ }
1580
+ }
1581
+ if (this.config.xss.enabled) {
1582
+ try {
1583
+ const { XSSProtection: XSSProtection2 } = await Promise.resolve().then(() => (init_XSSProtection(), XSSProtection_exports));
1584
+ const xss = new XSSProtection2(this.config.xss, this.logger);
1585
+ this.modules.set("xss", xss);
1586
+ this.enabledModules.add("xss");
1587
+ } catch (e) {
1588
+ this.logger.error("Failed to initialize XSS Protection", e);
1589
+ }
1590
+ }
1591
+ if (this.config.csrf.enabled) {
1592
+ try {
1593
+ const { CSRFProtection: CSRFProtection2 } = await Promise.resolve().then(() => (init_CSRFProtection(), CSRFProtection_exports));
1594
+ const csrf = new CSRFProtection2(this.config.csrf, this.logger);
1595
+ this.modules.set("csrf", csrf);
1596
+ this.enabledModules.add("csrf");
1597
+ } catch (e) {
1598
+ this.logger.error("Failed to initialize CSRF Protection", e);
1599
+ }
1600
+ }
1601
+ if (this.config.sqlInjection.enabled) {
1602
+ try {
1603
+ const { SQLInjectionProtection: SQLInjectionProtection2 } = await Promise.resolve().then(() => (init_SQLInjectionProtection(), SQLInjectionProtection_exports));
1604
+ const sql = new SQLInjectionProtection2(this.config.sqlInjection, this.logger);
1605
+ this.modules.set("sqlInjection", sql);
1606
+ this.enabledModules.add("sqlInjection");
1607
+ } catch (e) {
1608
+ this.logger.error("Failed to initialize SQL Injection Protection", e);
1609
+ }
1610
+ }
1611
+ if (this.config.botProtection.enabled) {
1612
+ try {
1613
+ const { BotProtection: BotProtection2 } = await Promise.resolve().then(() => (init_BotProtection(), BotProtection_exports));
1614
+ const bot = new BotProtection2(this.config.botProtection, this.logger);
1615
+ this.modules.set("botProtection", bot);
1616
+ this.enabledModules.add("botProtection");
1617
+ } catch (e) {
1618
+ this.logger.error("Failed to initialize Bot Protection", e);
1619
+ }
1620
+ }
1621
+ if (this.config.ipFilter.enabled) {
1622
+ try {
1623
+ const { IPFilter: IPFilter2 } = await Promise.resolve().then(() => (init_IPFilter(), IPFilter_exports));
1624
+ const ipFilter = new IPFilter2(this.config.ipFilter, this.logger);
1625
+ this.modules.set("ipFilter", ipFilter);
1626
+ this.enabledModules.add("ipFilter");
1627
+ } catch (e) {
1628
+ this.logger.error("Failed to initialize IP Filter", e);
1629
+ }
1630
+ }
1631
+ if (this.config.fileBlock.enabled) {
1632
+ try {
1633
+ const { FileBlocker: FileBlocker2 } = await Promise.resolve().then(() => (init_FileBlocker(), FileBlocker_exports));
1634
+ const fileBlock = new FileBlocker2(this.config.fileBlock, this.logger);
1635
+ this.modules.set("fileBlock", fileBlock);
1636
+ this.enabledModules.add("fileBlock");
1637
+ } catch (e) {
1638
+ this.logger.error("Failed to initialize File Blocker", e);
1639
+ }
1640
+ }
1641
+ }
1642
+ apply(app) {
1643
+ this.logger.info("Applying WebArmor middleware to app");
1644
+ if (app && typeof app.use === "function") {
1645
+ if (this.modules.has("securityHeaders")) {
1646
+ const headers = this.modules.get("securityHeaders");
1647
+ app.use(headers.middleware());
1648
+ }
1649
+ if (this.modules.has("cors")) {
1650
+ const cors = this.modules.get("cors");
1651
+ app.use(cors.middleware());
1652
+ }
1653
+ if (this.modules.has("rateLimit")) {
1654
+ const rateLimit = this.modules.get("rateLimit");
1655
+ app.use(rateLimit.middleware());
1656
+ }
1657
+ if (this.modules.has("xss")) {
1658
+ const xss = this.modules.get("xss");
1659
+ app.use(xss.middleware());
1660
+ }
1661
+ if (this.modules.has("sqlInjection")) {
1662
+ const sql = this.modules.get("sqlInjection");
1663
+ app.use(sql.middleware());
1664
+ }
1665
+ if (this.modules.has("botProtection")) {
1666
+ const bot = this.modules.get("botProtection");
1667
+ app.use(bot.middleware());
1668
+ }
1669
+ if (this.modules.has("ipFilter")) {
1670
+ const ipFilter = this.modules.get("ipFilter");
1671
+ app.use(ipFilter.middleware());
1672
+ }
1673
+ if (this.modules.has("fileBlock")) {
1674
+ const fileBlock = this.modules.get("fileBlock");
1675
+ app.use(fileBlock.middleware());
1676
+ }
1677
+ }
1678
+ this.logger.info("WebArmor middleware applied");
1679
+ }
1680
+ async protect(data) {
1681
+ this.stats.requests++;
1682
+ if (this.modules.has("ipFilter")) {
1683
+ const ipFilter = this.modules.get("ipFilter");
1684
+ const result = await ipFilter.check(data.ip);
1685
+ if (!result.allowed) {
1686
+ this.stats.ipBlocked++;
1687
+ this.stats.blocked++;
1688
+ return result;
1689
+ }
1690
+ }
1691
+ if (this.modules.has("ddos")) {
1692
+ const ddos = this.modules.get("ddos");
1693
+ const result = await ddos.check(data);
1694
+ if (!result.allowed) {
1695
+ this.stats.ddosBlocked++;
1696
+ this.stats.blocked++;
1697
+ return result;
1698
+ }
1699
+ }
1700
+ if (this.modules.has("rateLimit")) {
1701
+ const rateLimit = this.modules.get("rateLimit");
1702
+ const key = data.ip;
1703
+ const result = await rateLimit.check(key);
1704
+ if (!result.allowed) {
1705
+ this.stats.rateLimited++;
1706
+ this.stats.blocked++;
1707
+ return result;
1708
+ }
1709
+ }
1710
+ if (this.modules.has("botProtection")) {
1711
+ const bot = this.modules.get("botProtection");
1712
+ const result = await bot.check(data);
1713
+ if (!result.allowed) {
1714
+ this.stats.botBlocked++;
1715
+ this.stats.blocked++;
1716
+ return result;
1717
+ }
1718
+ }
1719
+ if (this.modules.has("xss") && data.body) {
1720
+ const xss = this.modules.get("xss");
1721
+ const result = xss.check(data.body);
1722
+ if (!result.allowed) {
1723
+ this.stats.xssBlocked++;
1724
+ if (this.config.xss.blockOnDetect) {
1725
+ this.stats.blocked++;
1726
+ return result;
1727
+ }
1728
+ }
1729
+ }
1730
+ if (this.modules.has("sqlInjection")) {
1731
+ const sql = this.modules.get("sqlInjection");
1732
+ const result = sql.check(data);
1733
+ if (!result.allowed) {
1734
+ this.stats.sqlInjectionBlocked++;
1735
+ if (this.config.sqlInjection.blockOnDetect) {
1736
+ this.stats.blocked++;
1737
+ return result;
1738
+ }
1739
+ }
1740
+ }
1741
+ this.stats.allowed++;
1742
+ return { allowed: true };
1743
+ }
1744
+ getStats() {
1745
+ return {
1746
+ ...this.stats,
1747
+ uptime: Date.now() - this.stats.startTime,
1748
+ enabledModules: Array.from(this.enabledModules)
1749
+ };
1750
+ }
1751
+ updateConfig(config) {
1752
+ this.config = { ...this.config, ...config };
1753
+ this.logger.info("Configuration updated");
1754
+ }
1755
+ enableModule(moduleName) {
1756
+ if (this.modules.has(moduleName)) {
1757
+ this.enabledModules.add(moduleName);
1758
+ this.logger.info(`Module ${moduleName} enabled`);
1759
+ }
1760
+ }
1761
+ disableModule(moduleName) {
1762
+ if (this.enabledModules.has(moduleName)) {
1763
+ this.enabledModules.delete(moduleName);
1764
+ this.logger.info(`Module ${moduleName} disabled`);
1765
+ }
1766
+ }
1767
+ getLogger() {
1768
+ return this.logger;
1769
+ }
1770
+ getConfig() {
1771
+ return this.config;
1772
+ }
1773
+ getModule(name) {
1774
+ return this.modules.get(name);
1775
+ }
1776
+ };
1777
+ var Shield_default = Shield;
1778
+
1779
+ // src/encryption/FileEncryptor.ts
1780
+ var import_crypto2 = __toESM(require("crypto"), 1);
1781
+ var import_fs = __toESM(require("fs"), 1);
1782
+ var FileEncryptor = class {
1783
+ algorithm = "aes-256-gcm";
1784
+ keyLength = 32;
1785
+ ivLength = 16;
1786
+ authTagLength = 16;
1787
+ deriveKey(password, salt) {
1788
+ return import_crypto2.default.pbkdf2Sync(password, salt, 1e5, this.keyLength, "sha256");
1789
+ }
1790
+ encryptFile(inputPath, outputPath, password) {
1791
+ return new Promise((resolve, reject) => {
1792
+ try {
1793
+ const salt = import_crypto2.default.randomBytes(32);
1794
+ const key = this.deriveKey(password, salt);
1795
+ const iv = import_crypto2.default.randomBytes(this.ivLength);
1796
+ const cipher = import_crypto2.default.createCipheriv(this.algorithm, key, iv);
1797
+ const input = import_fs.default.readFileSync(inputPath);
1798
+ const encrypted = Buffer.concat([cipher.update(input), cipher.final()]);
1799
+ const authTag = cipher.getAuthTag();
1800
+ const output = Buffer.concat([
1801
+ salt,
1802
+ iv,
1803
+ authTag,
1804
+ encrypted
1805
+ ]);
1806
+ import_fs.default.writeFileSync(outputPath, output);
1807
+ resolve();
1808
+ } catch (error) {
1809
+ reject(error);
1810
+ }
1811
+ });
1812
+ }
1813
+ decryptFile(inputPath, outputPath, password) {
1814
+ return new Promise((resolve, reject) => {
1815
+ try {
1816
+ const fileData = import_fs.default.readFileSync(inputPath);
1817
+ const salt = fileData.subarray(0, 32);
1818
+ const iv = fileData.subarray(32, 32 + this.ivLength);
1819
+ const authTag = fileData.subarray(32 + this.ivLength, 32 + this.ivLength + this.authTagLength);
1820
+ const encrypted = fileData.subarray(32 + this.ivLength + this.authTagLength);
1821
+ const key = this.deriveKey(password, salt);
1822
+ const decipher = import_crypto2.default.createDecipheriv(this.algorithm, key, iv);
1823
+ decipher.setAuthTag(authTag);
1824
+ const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]);
1825
+ import_fs.default.writeFileSync(outputPath, decrypted);
1826
+ resolve();
1827
+ } catch (error) {
1828
+ reject(error);
1829
+ }
1830
+ });
1831
+ }
1832
+ encryptString(data, password) {
1833
+ const salt = import_crypto2.default.randomBytes(32);
1834
+ const key = this.deriveKey(password, salt);
1835
+ const iv = import_crypto2.default.randomBytes(this.ivLength);
1836
+ const cipher = import_crypto2.default.createCipheriv(this.algorithm, key, iv);
1837
+ const encrypted = Buffer.concat([cipher.update(data, "utf8"), cipher.final()]);
1838
+ const authTag = cipher.getAuthTag();
1839
+ return Buffer.concat([salt, iv, authTag, encrypted]).toString("base64");
1840
+ }
1841
+ decryptString(encryptedData, password) {
1842
+ const data = Buffer.from(encryptedData, "base64");
1843
+ const salt = data.subarray(0, 32);
1844
+ const iv = data.subarray(32, 32 + this.ivLength);
1845
+ const authTag = data.subarray(32 + this.ivLength, 32 + this.ivLength + this.authTagLength);
1846
+ const encrypted = data.subarray(32 + this.ivLength + this.authTagLength);
1847
+ const key = this.deriveKey(password, salt);
1848
+ const decipher = import_crypto2.default.createDecipheriv(this.algorithm, key, iv);
1849
+ decipher.setAuthTag(authTag);
1850
+ return decipher.update(encrypted) + decipher.final("utf8");
1851
+ }
1852
+ encryptJSON(data, password) {
1853
+ const jsonString = JSON.stringify(data);
1854
+ return this.encryptString(jsonString, password);
1855
+ }
1856
+ decryptJSON(encryptedData, password) {
1857
+ const decrypted = this.decryptString(encryptedData, password);
1858
+ return JSON.parse(decrypted);
1859
+ }
1860
+ };
1861
+ var FileEncryptor_default = FileEncryptor;
1862
+
1863
+ // src/encryption/ConfigEncryptor.ts
1864
+ var import_fs2 = __toESM(require("fs"), 1);
1865
+ var import_path = __toESM(require("path"), 1);
1866
+ var ConfigEncryptor = class {
1867
+ encryptor;
1868
+ configDir;
1869
+ backupDir;
1870
+ constructor(configDir = "./config") {
1871
+ this.encryptor = new FileEncryptor_default();
1872
+ this.configDir = configDir;
1873
+ this.backupDir = import_path.default.join(configDir, ".backup");
1874
+ }
1875
+ encryptConfigFile(filename, password) {
1876
+ const inputPath = import_path.default.join(this.configDir, filename);
1877
+ const outputPath = import_path.default.join(this.configDir, `${filename}.enc`);
1878
+ if (!import_fs2.default.existsSync(this.configDir)) {
1879
+ import_fs2.default.mkdirSync(this.configDir, { recursive: true });
1880
+ }
1881
+ this.createBackup(inputPath);
1882
+ this.encryptor.encryptFile(inputPath, outputPath, password);
1883
+ import_fs2.default.unlinkSync(inputPath);
1884
+ }
1885
+ decryptConfigFile(filename, password) {
1886
+ const inputPath = import_path.default.join(this.configDir, `${filename}.enc`);
1887
+ const outputPath = import_path.default.join(this.configDir, filename.replace(".enc", ""));
1888
+ if (!import_fs2.default.existsSync(inputPath)) {
1889
+ throw new Error(`Encrypted file not found: ${filename}`);
1890
+ }
1891
+ this.encryptor.decryptFile(inputPath, outputPath, password);
1892
+ }
1893
+ encryptEnvFile(password) {
1894
+ const envPath = import_path.default.join(process.cwd(), ".env");
1895
+ const envBackupPath = import_path.default.join(process.cwd(), ".env.backup");
1896
+ const envEncryptedPath = import_path.default.join(process.cwd(), ".env.enc");
1897
+ if (!import_fs2.default.existsSync(envPath)) {
1898
+ throw new Error(".env file not found");
1899
+ }
1900
+ if (import_fs2.default.existsSync(envBackupPath)) {
1901
+ import_fs2.default.unlinkSync(envBackupPath);
1902
+ }
1903
+ import_fs2.default.copyFileSync(envPath, envBackupPath);
1904
+ this.encryptor.encryptFile(envPath, envEncryptedPath, password);
1905
+ import_fs2.default.unlinkSync(envPath);
1906
+ }
1907
+ decryptEnvFile(password) {
1908
+ const envEncryptedPath = import_path.default.join(process.cwd(), ".env.enc");
1909
+ const envPath = import_path.default.join(process.cwd(), ".env");
1910
+ if (!import_fs2.default.existsSync(envEncryptedPath)) {
1911
+ throw new Error(".env.enc file not found");
1912
+ }
1913
+ this.encryptor.decryptFile(envEncryptedPath, envPath, password);
1914
+ }
1915
+ getEncryptedEnv(password) {
1916
+ const envEncryptedPath = import_path.default.join(process.cwd(), ".env.enc");
1917
+ if (!import_fs2.default.existsSync(envEncryptedPath)) {
1918
+ throw new Error(".env.enc file not found");
1919
+ }
1920
+ const tempOutput = import_path.default.join(process.cwd(), ".env.temp");
1921
+ this.encryptor.decryptFile(envEncryptedPath, tempOutput, password);
1922
+ const content = import_fs2.default.readFileSync(tempOutput, "utf8");
1923
+ import_fs2.default.unlinkSync(tempOutput);
1924
+ const envVars = {};
1925
+ const lines = content.split("\n");
1926
+ for (const line of lines) {
1927
+ const trimmed = line.trim();
1928
+ if (!trimmed || trimmed.startsWith("#")) continue;
1929
+ const [key, ...valueParts] = trimmed.split("=");
1930
+ if (key) {
1931
+ envVars[key.trim()] = valueParts.join("=").trim();
1932
+ }
1933
+ }
1934
+ return envVars;
1935
+ }
1936
+ createBackup(filePath) {
1937
+ if (!import_fs2.default.existsSync(filePath)) return;
1938
+ if (!import_fs2.default.existsSync(this.backupDir)) {
1939
+ import_fs2.default.mkdirSync(this.backupDir, { recursive: true });
1940
+ }
1941
+ const filename = import_path.default.basename(filePath);
1942
+ const timestamp = Date.now();
1943
+ const backupPath = import_path.default.join(this.backupDir, `${filename}.${timestamp}.bak`);
1944
+ import_fs2.default.copyFileSync(filePath, backupPath);
1945
+ }
1946
+ restoreBackup(filename) {
1947
+ if (!import_fs2.default.existsSync(this.backupDir)) {
1948
+ throw new Error("No backups found");
1949
+ }
1950
+ const backups = import_fs2.default.readdirSync(this.backupDir).filter((f) => f.startsWith(filename)).sort().reverse();
1951
+ if (backups.length === 0) {
1952
+ throw new Error(`No backups found for ${filename}`);
1953
+ }
1954
+ const latestBackup = import_path.default.join(this.backupDir, backups[0]);
1955
+ const originalPath = import_path.default.join(this.configDir, filename);
1956
+ import_fs2.default.copyFileSync(latestBackup, originalPath);
1957
+ }
1958
+ listBackups() {
1959
+ if (!import_fs2.default.existsSync(this.backupDir)) {
1960
+ return [];
1961
+ }
1962
+ return import_fs2.default.readdirSync(this.backupDir);
1963
+ }
1964
+ };
1965
+ var ConfigEncryptor_default = ConfigEncryptor;
1966
+
1967
+ // src/encryption/DatabaseEncryptor.ts
1968
+ var import_fs3 = __toESM(require("fs"), 1);
1969
+ var import_path2 = __toESM(require("path"), 1);
1970
+ var DatabaseEncryptor = class {
1971
+ encryptor;
1972
+ dbDir;
1973
+ constructor(dbDir = "./data") {
1974
+ this.encryptor = new FileEncryptor_default();
1975
+ this.dbDir = dbDir;
1976
+ }
1977
+ encryptDatabase(dbPath, password, outputPath) {
1978
+ const targetPath = outputPath || `${dbPath}.enc`;
1979
+ if (!import_fs3.default.existsSync(dbPath)) {
1980
+ throw new Error(`Database file not found: ${dbPath}`);
1981
+ }
1982
+ this.encryptor.encryptFile(dbPath, targetPath, password);
1983
+ }
1984
+ decryptDatabase(encryptedPath, password, outputPath) {
1985
+ const targetPath = outputPath || encryptedPath.replace(".enc", "");
1986
+ if (!import_fs3.default.existsSync(encryptedPath)) {
1987
+ throw new Error(`Encrypted database not found: ${encryptedPath}`);
1988
+ }
1989
+ this.encryptor.decryptFile(encryptedPath, targetPath, password);
1990
+ }
1991
+ encryptJSONDatabase(dbPath, password) {
1992
+ if (!import_fs3.default.existsSync(dbPath)) {
1993
+ throw new Error(`Database file not found: ${dbPath}`);
1994
+ }
1995
+ const data = JSON.parse(import_fs3.default.readFileSync(dbPath, "utf8"));
1996
+ const encrypted = this.encryptor.encryptJSON(data, password);
1997
+ import_fs3.default.writeFileSync(dbPath, encrypted, "utf8");
1998
+ }
1999
+ decryptJSONDatabase(dbPath, password) {
2000
+ if (!import_fs3.default.existsSync(dbPath)) {
2001
+ throw new Error(`Database file not found: ${dbPath}`);
2002
+ }
2003
+ const encrypted = import_fs3.default.readFileSync(dbPath, "utf8");
2004
+ return this.encryptor.decryptJSON(encrypted, password);
2005
+ }
2006
+ encryptSQLite(dbPath, password) {
2007
+ this.encryptDatabase(dbPath, password);
2008
+ }
2009
+ decryptSQLite(encryptedPath, password) {
2010
+ this.decryptDatabase(encryptedPath, password);
2011
+ }
2012
+ autoEncryptDatabases(password, extensions = [".json", ".sqlite", ".db"]) {
2013
+ if (!import_fs3.default.existsSync(this.dbDir)) {
2014
+ return;
2015
+ }
2016
+ const files = import_fs3.default.readdirSync(this.dbDir);
2017
+ for (const file of files) {
2018
+ const ext = import_path2.default.extname(file);
2019
+ if (extensions.includes(ext)) {
2020
+ const filePath = import_path2.default.join(this.dbDir, file);
2021
+ const encryptedPath = `${filePath}.enc`;
2022
+ this.encryptor.encryptFile(filePath, encryptedPath, password);
2023
+ import_fs3.default.unlinkSync(filePath);
2024
+ }
2025
+ }
2026
+ }
2027
+ autoDecryptDatabases(password) {
2028
+ if (!import_fs3.default.existsSync(this.dbDir)) {
2029
+ return;
2030
+ }
2031
+ const files = import_fs3.default.readdirSync(this.dbDir);
2032
+ for (const file of files) {
2033
+ if (file.endsWith(".enc")) {
2034
+ const encryptedPath = import_path2.default.join(this.dbDir, file);
2035
+ const decryptedPath = encryptedPath.replace(".enc", "");
2036
+ this.encryptor.decryptFile(encryptedPath, decryptedPath, password);
2037
+ import_fs3.default.unlinkSync(encryptedPath);
2038
+ }
2039
+ }
2040
+ }
2041
+ createEncryptedBackup(dbPath, backupDir, password) {
2042
+ if (!import_fs3.default.existsSync(backupDir)) {
2043
+ import_fs3.default.mkdirSync(backupDir, { recursive: true });
2044
+ }
2045
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
2046
+ const filename = import_path2.default.basename(dbPath, import_path2.default.extname(dbPath));
2047
+ const backupPath = import_path2.default.join(backupDir, `${filename}_${timestamp}.enc`);
2048
+ this.encryptor.encryptFile(dbPath, backupPath, password);
2049
+ return backupPath;
2050
+ }
2051
+ listEncryptedBackups(backupDir) {
2052
+ if (!import_fs3.default.existsSync(backupDir)) {
2053
+ return [];
2054
+ }
2055
+ return import_fs3.default.readdirSync(backupDir).filter((f) => f.endsWith(".enc")).map((f) => import_path2.default.join(backupDir, f));
2056
+ }
2057
+ };
2058
+ var DatabaseEncryptor_default = DatabaseEncryptor;
2059
+
2060
+ // src/encryption/SessionEncryptor.ts
2061
+ var import_crypto3 = __toESM(require("crypto"), 1);
2062
+ var SessionEncryptor = class {
2063
+ encryptor;
2064
+ key = null;
2065
+ constructor(key) {
2066
+ this.encryptor = new FileEncryptor_default();
2067
+ if (key) {
2068
+ this.setKey(key);
2069
+ }
2070
+ }
2071
+ setKey(key) {
2072
+ this.key = import_crypto3.default.createHash("sha256").update(key).digest();
2073
+ }
2074
+ ensureKey() {
2075
+ if (!this.key) {
2076
+ throw new Error("Encryption key not set. Call setKey() first.");
2077
+ }
2078
+ }
2079
+ encryptSession(sessionData) {
2080
+ this.ensureKey();
2081
+ const json = JSON.stringify(sessionData);
2082
+ return this.encryptor.encryptString(json, this.key.toString("base64"));
2083
+ }
2084
+ decryptSession(encryptedData) {
2085
+ this.ensureKey();
2086
+ const decrypted = this.encryptor.decryptString(encryptedData, this.key.toString("base64"));
2087
+ return JSON.parse(decrypted);
2088
+ }
2089
+ encryptValue(value) {
2090
+ this.ensureKey();
2091
+ if (typeof value === "object") {
2092
+ return this.encryptor.encryptJSON(value, this.key.toString("base64"));
2093
+ }
2094
+ return this.encryptor.encryptString(String(value), this.key.toString("base64"));
2095
+ }
2096
+ decryptValue(encryptedValue) {
2097
+ this.ensureKey();
2098
+ try {
2099
+ const decrypted = this.encryptor.decryptString(encryptedValue, this.key.toString("base64"));
2100
+ try {
2101
+ return JSON.parse(decrypted);
2102
+ } catch {
2103
+ return decrypted;
2104
+ }
2105
+ } catch {
2106
+ try {
2107
+ return this.encryptor.decryptJSON(encryptedValue, this.key.toString("base64"));
2108
+ } catch {
2109
+ throw new Error("Failed to decrypt value");
2110
+ }
2111
+ }
2112
+ }
2113
+ createSignedSession(sessionData, secret) {
2114
+ const encrypted = this.encryptSession(sessionData);
2115
+ const signature = import_crypto3.default.createHmac("sha256", secret).update(encrypted).digest("base64");
2116
+ return `${encrypted}.${signature}`;
2117
+ }
2118
+ verifySignedSession(signedSession, secret) {
2119
+ const parts = signedSession.split(".");
2120
+ if (parts.length !== 2) {
2121
+ return { valid: false };
2122
+ }
2123
+ const [encrypted, signature] = parts;
2124
+ const expectedSignature = import_crypto3.default.createHmac("sha256", secret).update(encrypted).digest("base64");
2125
+ if (signature !== expectedSignature) {
2126
+ return { valid: false };
2127
+ }
2128
+ try {
2129
+ const data = this.decryptSession(encrypted);
2130
+ return { valid: true, data };
2131
+ } catch {
2132
+ return { valid: false };
2133
+ }
2134
+ }
2135
+ encryptCookie(sessionData, password) {
2136
+ return this.encryptor.encryptString(JSON.stringify(sessionData), password);
2137
+ }
2138
+ decryptCookie(encryptedCookie, password) {
2139
+ const decrypted = this.encryptor.decryptString(encryptedCookie, password);
2140
+ return JSON.parse(decrypted);
2141
+ }
2142
+ generateSecureToken(length = 32) {
2143
+ return import_crypto3.default.randomBytes(length).toString("hex");
2144
+ }
2145
+ hashData(data) {
2146
+ return import_crypto3.default.createHash("sha256").update(data).digest("hex");
2147
+ }
2148
+ verifyHash(data, hash) {
2149
+ return this.hashData(data) === hash;
2150
+ }
2151
+ };
2152
+ var SessionEncryptor_default = SessionEncryptor;
2153
+
2154
+ // src/index.ts
2155
+ async function createWebArmor(config) {
2156
+ const shield = new Shield_default(config);
2157
+ await shield.initialize();
2158
+ return shield;
2159
+ }
2160
+ var index_default = {
2161
+ create: createWebArmor,
2162
+ Shield: Shield_default,
2163
+ FileEncryptor: FileEncryptor_default,
2164
+ ConfigEncryptor: ConfigEncryptor_default,
2165
+ DatabaseEncryptor: DatabaseEncryptor_default,
2166
+ SessionEncryptor: SessionEncryptor_default,
2167
+ Logger: Logger_default
2168
+ };
2169
+ var security = {
2170
+ encrypt: (data, password) => {
2171
+ const enc = new FileEncryptor_default();
2172
+ return enc.encryptString(data, password);
2173
+ },
2174
+ decrypt: (encryptedData, password) => {
2175
+ const enc = new FileEncryptor_default();
2176
+ return enc.decryptString(encryptedData, password);
2177
+ },
2178
+ encryptFile: async (inputPath, outputPath, password) => {
2179
+ const enc = new FileEncryptor_default();
2180
+ await enc.encryptFile(inputPath, outputPath, password);
2181
+ },
2182
+ decryptFile: async (inputPath, outputPath, password) => {
2183
+ const enc = new FileEncryptor_default();
2184
+ await enc.decryptFile(inputPath, outputPath, password);
2185
+ }
2186
+ };
2187
+ var createShield = createWebArmor;
2188
+ // Annotate the CommonJS export names for ESM import in node:
2189
+ 0 && (module.exports = {
2190
+ ConfigEncryptor,
2191
+ DatabaseEncryptor,
2192
+ FileEncryptor,
2193
+ Logger,
2194
+ SessionEncryptor,
2195
+ Shield,
2196
+ createShield,
2197
+ security
2198
+ });
2199
+ //# sourceMappingURL=index.cjs.map