hi-secure 1.0.6 → 1.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/dist/adapters/ArgonAdapter.js +1 -1
  2. package/dist/adapters/ArgonAdapter.js.map +1 -1
  3. package/dist/adapters/ExpressRLAdapter.d.ts.map +1 -1
  4. package/dist/adapters/ExpressRLAdapter.js +1 -2
  5. package/dist/adapters/ExpressRLAdapter.js.map +1 -1
  6. package/dist/adapters/ExpressValidatorAdapter.d.ts.map +1 -1
  7. package/dist/adapters/ExpressValidatorAdapter.js +1 -39
  8. package/dist/adapters/ExpressValidatorAdapter.js.map +1 -1
  9. package/dist/adapters/GoogleAdapter.d.ts.map +1 -1
  10. package/dist/adapters/GoogleAdapter.js +0 -101
  11. package/dist/adapters/GoogleAdapter.js.map +1 -1
  12. package/dist/adapters/JWTAdapter.d.ts.map +1 -1
  13. package/dist/adapters/JWTAdapter.js +3 -210
  14. package/dist/adapters/JWTAdapter.js.map +1 -1
  15. package/dist/adapters/RLFlexibleAdapter.d.ts.map +1 -1
  16. package/dist/adapters/RLFlexibleAdapter.js +0 -52
  17. package/dist/adapters/RLFlexibleAdapter.js.map +1 -1
  18. package/dist/adapters/SanitizeHtmlAdapter.d.ts +0 -3
  19. package/dist/adapters/SanitizeHtmlAdapter.d.ts.map +1 -1
  20. package/dist/adapters/SanitizeHtmlAdapter.js +2 -71
  21. package/dist/adapters/SanitizeHtmlAdapter.js.map +1 -1
  22. package/dist/adapters/XSSAdapter.d.ts +0 -10
  23. package/dist/adapters/XSSAdapter.d.ts.map +1 -1
  24. package/dist/adapters/XSSAdapter.js +2 -19
  25. package/dist/adapters/XSSAdapter.js.map +1 -1
  26. package/dist/adapters/ZodAdapter.d.ts.map +1 -1
  27. package/dist/adapters/ZodAdapter.js +2 -6
  28. package/dist/adapters/ZodAdapter.js.map +1 -1
  29. package/dist/core/HiSecure.d.ts +15 -2
  30. package/dist/core/HiSecure.d.ts.map +1 -1
  31. package/dist/core/HiSecure.js +130 -37
  32. package/dist/core/HiSecure.js.map +1 -1
  33. package/dist/core/useSecure.d.ts +4 -0
  34. package/dist/core/useSecure.d.ts.map +1 -1
  35. package/dist/core/useSecure.js +19 -114
  36. package/dist/core/useSecure.js.map +1 -1
  37. package/dist/index.d.ts +4 -4
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +6 -19
  40. package/dist/index.js.map +1 -1
  41. package/dist/managers/AuthManager.d.ts.map +1 -1
  42. package/dist/managers/AuthManager.js +1 -89
  43. package/dist/managers/AuthManager.js.map +1 -1
  44. package/dist/managers/CorsManager.d.ts.map +1 -1
  45. package/dist/managers/CorsManager.js +1 -19
  46. package/dist/managers/CorsManager.js.map +1 -1
  47. package/dist/managers/HashManager.d.ts.map +1 -1
  48. package/dist/managers/HashManager.js +0 -243
  49. package/dist/managers/HashManager.js.map +1 -1
  50. package/dist/managers/JsonManager.d.ts.map +1 -1
  51. package/dist/managers/JsonManager.js +1 -77
  52. package/dist/managers/JsonManager.js.map +1 -1
  53. package/dist/managers/RateLimitManager.d.ts.map +1 -1
  54. package/dist/managers/RateLimitManager.js +3 -17
  55. package/dist/managers/RateLimitManager.js.map +1 -1
  56. package/dist/managers/SanitizerManager.d.ts +0 -6
  57. package/dist/managers/SanitizerManager.d.ts.map +1 -1
  58. package/dist/managers/SanitizerManager.js +1 -213
  59. package/dist/managers/SanitizerManager.js.map +1 -1
  60. package/dist/managers/ValidatorManager.d.ts.map +1 -1
  61. package/dist/managers/ValidatorManager.js +1 -109
  62. package/dist/managers/ValidatorManager.js.map +1 -1
  63. package/dist/middlewares/errorHandler.d.ts.map +1 -1
  64. package/dist/middlewares/errorHandler.js +0 -19
  65. package/dist/middlewares/errorHandler.js.map +1 -1
  66. package/dist/utils/deepFreeze.d.ts.map +1 -1
  67. package/dist/utils/deepFreeze.js +0 -25
  68. package/dist/utils/deepFreeze.js.map +1 -1
  69. package/dist/utils/deepMerge.d.ts.map +1 -1
  70. package/dist/utils/deepMerge.js +0 -26
  71. package/dist/utils/deepMerge.js.map +1 -1
  72. package/dist/utils/normalizeOptions.d.ts +1 -3
  73. package/dist/utils/normalizeOptions.d.ts.map +1 -1
  74. package/dist/utils/normalizeOptions.js +0 -1
  75. package/dist/utils/normalizeOptions.js.map +1 -1
  76. package/package.json +1 -1
  77. package/src/adapters/ArgonAdapter.ts +1 -1
  78. package/src/adapters/ExpressRLAdapter.ts +1 -2
  79. package/src/adapters/ExpressValidatorAdapter.ts +1 -54
  80. package/src/adapters/GoogleAdapter.ts +0 -129
  81. package/src/adapters/JWTAdapter.ts +5 -259
  82. package/src/adapters/RLFlexibleAdapter.ts +2 -65
  83. package/src/adapters/SanitizeHtmlAdapter.ts +3 -87
  84. package/src/adapters/XSSAdapter.ts +11 -19
  85. package/src/adapters/ZodAdapter.ts +2 -51
  86. package/src/core/HiSecure.ts +25 -36
  87. package/src/core/useSecure.ts +5 -7
  88. package/src/index.ts +4 -5
  89. package/src/managers/AuthManager.ts +5 -109
  90. package/src/managers/CorsManager.ts +1 -25
  91. package/src/managers/HashManager.ts +0 -286
  92. package/src/managers/JsonManager.ts +1 -91
  93. package/src/managers/RateLimitManager.ts +3 -262
  94. package/src/managers/SanitizerManager.ts +4 -263
  95. package/src/managers/ValidatorManager.ts +53 -187
  96. package/src/middlewares/errorHandler.ts +1 -176
  97. package/src/utils/deepFreeze.ts +0 -32
  98. package/src/utils/deepMerge.ts +0 -35
  99. package/src/utils/normalizeOptions.ts +16 -133
  100. package/src/examples/e1.ts +0 -1
  101. package/src/test/t1.ts +0 -1
@@ -1,259 +1,5 @@
1
- // // import jwt from "jsonwebtoken";
2
- // // import { AdapterError } from "../core/errors/AdapterError";
3
- // // import { logError } from "../logging";
4
-
5
- // // export interface JWTAdapterOptions {
6
- // // secret: string;
7
- // // expiresIn?: string | number | undefined;
8
- // // }
9
-
10
- // // export class JWTAdapter {
11
- // // private secret: string;
12
- // // private expiresIn?: string | number;
13
-
14
- // // constructor(options: JWTAdapterOptions) {
15
- // // if (!options.secret) {
16
- // // throw new AdapterError("JWT secret is required");
17
- // // }
18
-
19
- // // this.secret = options.secret;
20
-
21
- // // // Normalize expiresIn
22
- // // if (options.expiresIn !== undefined) {
23
- // // this.expiresIn = options.expiresIn as string | number;
24
- // // }
25
- // // }
26
-
27
- // // sign(
28
- // // payload: object,
29
- // // options?: { expiresIn?: string | number }
30
- // // ) {
31
- // // try {
32
- // // const finalExpires =
33
- // // options?.expiresIn ?? this.expiresIn;
34
-
35
- // // const jwtOptions: jwt.SignOptions = {};
36
-
37
- // // if (finalExpires !== undefined) {
38
- // // // Force safe cast → matches SignOptions type
39
- // // jwtOptions.expiresIn = finalExpires as number | any;
40
- // // }
41
-
42
- // // return jwt.sign(payload, this.secret, jwtOptions);
43
-
44
- // // } catch (err: any) {
45
- // // logError("JWTAdapter.sign failed", { error: err?.message });
46
- // // throw new AdapterError(err?.message || "JWT sign failed");
47
- // // }
48
- // // }
49
-
50
- // // verify(token: string) {
51
- // // try {
52
- // // return jwt.verify(token, this.secret);
53
- // // } catch (err: any) {
54
- // // logError("JWTAdapter.verify failed", { error: err?.message });
55
- // // throw new AdapterError(err?.message || "JWT verify failed");
56
- // // }
57
- // // }
58
- // // }
59
-
60
-
61
-
62
- // // // src/adapters/JWTAdapter.ts - FIXED (Security hardening)
63
- // // import jwt from "jsonwebtoken";
64
- // // import { v4 as uuidv4 } from "uuid";
65
- // // import { AdapterError } from "../core/errors/AdapterError.js";
66
- // // import { logError } from "../logging/index.js";
67
-
68
- // // export interface JWTAdapterOptions {
69
- // // secret: string;
70
- // // expiresIn?: string | number;
71
- // // algorithm?: jwt.Algorithm;
72
- // // issuer?: string;
73
- // // audience?: string | string[];
74
- // // }
75
-
76
- // // export interface SignOptions {
77
- // // expiresIn?: string | number;
78
- // // jti?: string; // JWT ID for token revocation
79
- // // subject?: string;
80
- // // issuer?: string;
81
- // // audience?: string | string[];
82
- // // }
83
-
84
- // // export class JWTAdapter {
85
- // // private secret: string;
86
- // // private expiresIn?: string | number;
87
- // // private algorithm: jwt.Algorithm;
88
- // // private issuer?: string;
89
- // // private audience?: string | string[];
90
-
91
- // // constructor(options: JWTAdapterOptions) {
92
- // // if (!options.secret) {
93
- // // throw new AdapterError("JWT secret is required");
94
- // // }
95
-
96
- // // if (options.secret.length < 32) {
97
- // // logError("⚠ JWT secret is too short (minimum 32 characters recommended)");
98
- // // }
99
-
100
- // // this.secret = options.secret;
101
- // // this.expiresIn = options.expiresIn;
102
- // // this.algorithm = options.algorithm || 'HS256'; // ⚠️ Default algorithm
103
- // // this.issuer = options.issuer;
104
- // // this.audience = options.audience;
105
- // // }
106
-
107
- // // sign(payload: object, options?: SignOptions) {
108
- // // try {
109
- // // const jwtOptions: jwt.SignOptions = {
110
- // // algorithm: this.algorithm,
111
- // // issuer: options?.issuer || this.issuer,
112
- // // audience: options?.audience || this.audience,
113
- // // jwtid: options?.jti || uuidv4(), // Unique token ID
114
- // // subject: options?.subject
115
- // // };
116
-
117
- // // if (options?.expiresIn !== undefined) {
118
- // // jwtOptions.expiresIn = options.expiresIn as number;
119
- // // } else if (this.expiresIn !== undefined) {
120
- // // jwtOptions.expiresIn = this.expiresIn as number;
121
- // // }
122
-
123
- // // return jwt.sign(payload, this.secret, jwtOptions);
124
-
125
- // // } catch (err: any) {
126
- // // logError("JWTAdapter.sign failed", { error: err?.message });
127
- // // throw new AdapterError(err?.message || "JWT sign failed");
128
- // // }
129
- // // }
130
-
131
- // // verify(token: string, options?: { audience?: string | string[] }) {
132
- // // try {
133
- // // const verifyOptions: jwt.VerifyOptions = {
134
- // // algorithms: [this.algorithm],
135
- // // issuer: this.issuer,
136
- // // audience: options?.audience as string || this.audience as string
137
- // // };
138
-
139
- // // return jwt.verify(token, this.secret, verifyOptions);
140
- // // } catch (err: any) {
141
- // // logError("JWTAdapter.verify failed", { error: err?.message });
142
-
143
- // // // Provide better error messages
144
- // // if (err.name === 'TokenExpiredError') {
145
- // // throw new AdapterError("JWT token has expired");
146
- // // }
147
- // // if (err.name === 'JsonWebTokenError') {
148
- // // throw new AdapterError("Invalid JWT token");
149
- // // }
150
-
151
- // // throw new AdapterError(err?.message || "JWT verification failed");
152
- // // }
153
- // // }
154
- // // }
155
-
156
-
157
- // // src/adapters/JWTAdapter.ts - FIXED (Security hardening)
158
- // import jwt from "jsonwebtoken";
159
- // import { nanoid } from "nanoid"; // CHANGED: from uuid to nanoid
160
- // import { AdapterError } from "../core/errors/AdapterError.js";
161
- // import { logError } from "../logging/index.js";
162
-
163
- // export interface JWTAdapterOptions {
164
- // secret: string;
165
- // expiresIn?: string | number;
166
- // algorithm?: jwt.Algorithm;
167
- // issuer?: string;
168
- // audience?: string | string[];
169
- // }
170
-
171
- // export interface SignOptions {
172
- // expiresIn?: string | number;
173
- // jti?: string; // JWT ID for token revocation
174
- // subject?: string;
175
- // issuer?: string;
176
- // audience?: string | string[];
177
- // }
178
-
179
- // export class JWTAdapter {
180
- // private secret: string;
181
- // private expiresIn?: string | number;
182
- // private algorithm: jwt.Algorithm;
183
- // private issuer?: string;
184
- // private audience?: string | string[];
185
-
186
- // constructor(options: JWTAdapterOptions) {
187
- // if (!options.secret) {
188
- // throw new AdapterError("JWT secret is required");
189
- // }
190
-
191
- // if (options.secret.length < 32) {
192
- // logError("⚠ JWT secret is too short (minimum 32 characters recommended)");
193
- // }
194
-
195
- // this.secret = options.secret;
196
- // this.expiresIn = options.expiresIn;
197
- // this.algorithm = options.algorithm || 'HS256'; // ⚠️ Default algorithm
198
- // this.issuer = options.issuer;
199
- // this.audience = options.audience;
200
- // }
201
-
202
- // sign(payload: object, options?: SignOptions) {
203
- // try {
204
- // const jwtOptions: jwt.SignOptions = {
205
- // algorithm: this.algorithm,
206
- // issuer: options?.issuer || this.issuer,
207
- // audience: options?.audience || this.audience,
208
- // jwtid: options?.jti || nanoid(), // CHANGED: uuidv4() to nanoid()
209
- // subject: options?.subject
210
- // };
211
-
212
- // if (options?.expiresIn !== undefined) {
213
- // jwtOptions.expiresIn = options.expiresIn as number;
214
- // } else if (this.expiresIn !== undefined) {
215
- // jwtOptions.expiresIn = this.expiresIn as number;
216
- // }
217
-
218
- // return jwt.sign(payload, this.secret, jwtOptions);
219
-
220
- // } catch (err: any) {
221
- // logError("JWTAdapter.sign failed", { error: err?.message });
222
- // throw new AdapterError(err?.message || "JWT sign failed");
223
- // }
224
- // }
225
-
226
- // verify(token: string, options?: { audience?: string | string[] }) {
227
- // try {
228
- // const verifyOptions: jwt.VerifyOptions = {
229
- // algorithms: [this.algorithm],
230
- // issuer: this.issuer,
231
- // audience: options?.audience as string || this.audience as string
232
- // };
233
-
234
- // return jwt.verify(token, this.secret, verifyOptions);
235
- // } catch (err: any) {
236
- // logError("JWTAdapter.verify failed", { error: err?.message });
237
-
238
- // // Provide better error messages
239
- // if (err.name === 'TokenExpiredError') {
240
- // throw new AdapterError("JWT token has expired");
241
- // }
242
- // if (err.name === 'JsonWebTokenError') {
243
- // throw new AdapterError("Invalid JWT token");
244
- // }
245
-
246
- // throw new AdapterError(err?.message || "JWT verification failed");
247
- // }
248
- // }
249
- // }
250
-
251
-
252
-
253
-
254
- // src/adapters/JWTAdapter.ts - FIXED (Using crypto.randomUUID())
255
1
  import jwt from "jsonwebtoken";
256
- import { randomUUID } from "crypto"; // Built-in Node.js
2
+ import { randomUUID } from "crypto";
257
3
  import { AdapterError } from "../core/errors/AdapterError.js";
258
4
  import { logError } from "../logging/index.js";
259
5
  import { logger } from "../logging";
@@ -268,7 +14,7 @@ export interface JWTAdapterOptions {
268
14
 
269
15
  export interface SignOptions {
270
16
  expiresIn?: string | number;
271
- jti?: string; // JWT ID for token revocation
17
+ jti?: string;
272
18
  subject?: string;
273
19
  issuer?: string;
274
20
  audience?: string | string[];
@@ -293,7 +39,7 @@ export class JWTAdapter {
293
39
 
294
40
  this.secret = options.secret;
295
41
  this.expiresIn = options.expiresIn;
296
- this.algorithm = options.algorithm || 'HS256'; // ⚠️ Default algorithm
42
+ this.algorithm = options.algorithm || 'HS256'; // Default algorithm
297
43
  this.issuer = options.issuer;
298
44
  this.audience = options.audience;
299
45
  }
@@ -304,7 +50,7 @@ export class JWTAdapter {
304
50
  algorithm: this.algorithm,
305
51
  issuer: options?.issuer || this.issuer,
306
52
  audience: options?.audience || this.audience,
307
- jwtid: options?.jti || randomUUID(), // ✅ Using crypto.randomUUID()
53
+ jwtid: options?.jti || randomUUID(),
308
54
  subject: options?.subject
309
55
  };
310
56
 
@@ -334,7 +80,7 @@ export class JWTAdapter {
334
80
  } catch (err: any) {
335
81
  logError("JWTAdapter.verify failed", { error: err?.message });
336
82
 
337
- // Provide better error messages
83
+
338
84
  if (err.name === 'TokenExpiredError') {
339
85
  throw new AdapterError("JWT token has expired");
340
86
  }
@@ -1,72 +1,10 @@
1
-
2
- // import { RateLimiterMemory, RateLimiterRes } from "rate-limiter-flexible";
3
- // import { logger } from "../logging";
4
- // import { AdapterError } from "../core/errors/AdapterError";
5
-
6
- // export class RLFlexibleAdapter {
7
-
8
- // /**
9
- // * Create middleware dynamically using options.
10
- // */
11
- // getMiddleware(options: {
12
- // points?: number;
13
- // duration?: number; // seconds
14
- // message?: any;
15
- // } = {}) {
16
-
17
- // try {
18
- // const limiter = new RateLimiterMemory({
19
- // points: options.points ?? 100,
20
- // duration: options.duration ?? 60,
21
- // });
22
-
23
- // return async (req: any, res: any, next: any) => {
24
- // const ip = req.ip ||
25
- // req.headers["x-forwarded-for"] ||
26
- // req.connection?.remoteAddress ||
27
- // "unknown";
28
-
29
- // try {
30
- // await limiter.consume(ip);
31
- // next();
32
- // } catch (err: any) {
33
- // const rlErr = err as RateLimiterRes;
34
-
35
- // logger.warn("⚠ RLFlexibleAdapter: rate limit exceeded", {
36
- // ip,
37
- // path: req.path,
38
- // method: req.method,
39
- // retryAfter: rlErr.msBeforeNext
40
- // });
41
-
42
- // return res.status(429).json({
43
- // success: false,
44
- // error: "RATE_LIMIT_EXCEEDED",
45
- // retryAfter: Math.ceil(rlErr.msBeforeNext / 1000),
46
- // message: options.message ?? "Too many requests, slow down."
47
- // });
48
- // }
49
- // };
50
-
51
- // } catch (err: any) {
52
- // logger.error("❌ RLFlexibleAdapter: failed to initialize limiter", {
53
- // error: err?.message || err
54
- // });
55
- // throw new AdapterError("RateLimiterFlexible creation failed.");
56
- // }
57
- // }
58
- // }
59
-
60
-
61
-
62
- // src/adapters/RLFlexibleAdapter.ts - IMPROVED
63
1
  import { RateLimiterMemory, RateLimiterRes } from "rate-limiter-flexible";
64
2
  import { logger } from "../logging/index.js";
65
3
  import { AdapterError } from "../core/errors/AdapterError.js";
66
4
 
67
5
  export interface RLOptions {
68
6
  points?: number;
69
- duration?: number; // seconds
7
+ duration?: number;
70
8
  message?: any;
71
9
  blockDuration?: number;
72
10
  }
@@ -90,7 +28,7 @@ export class RLFlexibleAdapter {
90
28
  });
91
29
 
92
30
  return async (req: any, res: any, next: any) => {
93
- // Better IP extraction
31
+
94
32
  const ip = this.extractIP(req);
95
33
 
96
34
  try {
@@ -126,7 +64,6 @@ export class RLFlexibleAdapter {
126
64
  }
127
65
 
128
66
  private extractIP(req: any): string {
129
- // Order of priority for IP extraction
130
67
  return (
131
68
  req.headers['x-real-ip'] ||
132
69
  req.headers['x-forwarded-for']?.split(',')[0]?.trim() ||
@@ -1,85 +1,3 @@
1
- // import sanitizeHtml from "sanitize-html";
2
- // import { AdapterError } from "../core/errors/AdapterError";
3
- // import { logger } from "../logging";
4
-
5
- // export class SanitizeHtmlAdapter {
6
- // private globalOptions: sanitizeHtml.IOptions;
7
-
8
- // constructor(options: sanitizeHtml.IOptions = {}) {
9
- // this.globalOptions = options;
10
- // }
11
-
12
- // /**
13
- // * Sanitize a string with merged global + dynamic options
14
- // */
15
- // sanitize(input: string, dynamicOptions?: any): string {
16
- // try {
17
- // const opts = { ...this.globalOptions, ...(dynamicOptions || {}) };
18
-
19
- // const clean = sanitizeHtml(input, opts);
20
-
21
- // return typeof clean === "string" ? clean : String(clean);
22
-
23
- // } catch (err: any) {
24
- // logger.error("❌ sanitize-html failed", {
25
- // error: err?.message || err,
26
- // preview: typeof input === "string" ? input.slice(0, 100) : undefined
27
- // });
28
-
29
- // throw new AdapterError("sanitize-html adapter failed.");
30
- // }
31
- // }
32
-
33
- // /**
34
- // * Deep sanitize nested objects, arrays, strings
35
- // */
36
- // private deepSanitize(obj: any, dynamicOptions?: any): any {
37
- // if (typeof obj === "string") {
38
- // return this.sanitize(obj, dynamicOptions);
39
- // }
40
-
41
- // if (Array.isArray(obj)) {
42
- // return obj.map((item) => this.deepSanitize(item, dynamicOptions));
43
- // }
44
-
45
- // if (obj && typeof obj === "object") {
46
- // const result: any = {};
47
- // for (const key of Object.keys(obj)) {
48
- // result[key] = this.deepSanitize(obj[key], dynamicOptions);
49
- // }
50
- // return result;
51
- // }
52
-
53
- // return obj;
54
- // }
55
-
56
- // /**
57
- // * Middleware wrapper with dynamic per-route options
58
- // */
59
- // middleware(dynamicOptions?: any) {
60
- // return (req: any, _res: any, next: any) => {
61
- // try {
62
- // if (req.body) {
63
- // req.body = this.deepSanitize(req.body, dynamicOptions);
64
-
65
- // logger.debug("🧼 sanitize-html applied", {
66
- // keys: Object.keys(req.body)
67
- // });
68
- // }
69
- // next();
70
-
71
- // } catch (err: any) {
72
- // logger.error("❌ sanitize-html middleware failed", {
73
- // error: err?.message || err
74
- // });
75
- // next(err);
76
- // }
77
- // };
78
- // }
79
- // }
80
-
81
-
82
- // src/adapters/SanitizeHtmlAdapter.ts - FIXED
83
1
  import sanitizeHtml from "sanitize-html";
84
2
  import { AdapterError } from "../core/errors/AdapterError.js";
85
3
  import { logger } from "../logging/index.js";
@@ -108,14 +26,12 @@ export class SanitizeHtmlAdapter {
108
26
  }
109
27
  }
110
28
 
111
- /**
112
- * Deep sanitize with recursion protection
113
- */
29
+ // Deep Sanitization - Recursively
114
30
  private deepSanitize(obj: any, dynamicOptions?: any, visited = new WeakSet()): any {
115
- // Handle circular references
31
+
116
32
  if (obj && typeof obj === "object") {
117
33
  if (visited.has(obj)) {
118
- return obj; // Circular reference detected
34
+ return obj;
119
35
  }
120
36
  visited.add(obj);
121
37
  }
@@ -1,4 +1,3 @@
1
- // src/adapters/XSSAdapter.ts - NEW FILE
2
1
  import { FilterXSS, getDefaultWhiteList, whiteList } from 'xss';
3
2
  import { AdapterError } from "../core/errors/AdapterError.js";
4
3
  import { logger } from "../logging/index.js";
@@ -25,12 +24,12 @@ export class XSSAdapter {
25
24
  // Default safe configuration
26
25
  const defaultOptions: XSSOptions = {
27
26
  whiteList: getDefaultWhiteList(),
28
- stripIgnoreTag: true, // Remove non-whitelisted tags completely
27
+ stripIgnoreTag: true,
29
28
  stripIgnoreTagBody: ['script', 'style', 'iframe', 'object', 'embed'],
30
29
  allowCommentTag: false,
31
- css: false, // Disable CSS by default
30
+ css: false,
32
31
  onTag: (tag, html, options) => {
33
- // Add noopener/noreferrer to links for security
32
+
34
33
  if (tag === 'a') {
35
34
  return html.replace(/<a /i, '<a target="_blank" rel="noopener noreferrer" ');
36
35
  }
@@ -42,21 +41,19 @@ export class XSSAdapter {
42
41
  this.defaultFilter = new FilterXSS(finalOptions);
43
42
  }
44
43
 
45
- /**
46
- * Sanitize a string with global + dynamic merged options
47
- */
44
+
48
45
  sanitize(input: string, dynamicOptions?: XSSOptions): string {
49
46
  try {
50
47
  if (typeof input !== "string") {
51
48
  return input as any;
52
49
  }
53
50
 
54
- // If no dynamic options, use default filter
51
+
55
52
  if (!dynamicOptions || Object.keys(dynamicOptions).length === 0) {
56
53
  return this.defaultFilter.process(input);
57
54
  }
58
55
 
59
- // Merge options for this specific call
56
+
60
57
  const mergedOptions = { ...this.globalOptions, ...dynamicOptions };
61
58
  const customFilter = new FilterXSS(mergedOptions);
62
59
 
@@ -71,10 +68,7 @@ export class XSSAdapter {
71
68
  }
72
69
  }
73
70
 
74
- /**
75
- * Middleware wrapper WITH dynamic options
76
- * Doesn't mutate original request - creates sanitized copy
77
- */
71
+
78
72
  middleware(dynamicOptions?: XSSOptions) {
79
73
  return (req: any, _res: any, next: any) => {
80
74
  try {
@@ -94,14 +88,14 @@ export class XSSAdapter {
94
88
  : v
95
89
  );
96
90
  } else if (val && typeof val === "object") {
97
- // Handle nested objects (simple recursion)
91
+
98
92
  sanitizedBody[key] = this.deepSanitize(val, dynamicOptions);
99
93
  } else {
100
94
  sanitizedBody[key] = val;
101
95
  }
102
96
  }
103
97
 
104
- // Store sanitized version separately
98
+
105
99
  req.sanitizedBody = sanitizedBody;
106
100
 
107
101
  logger.debug("🛡️ XSS sanitizer applied", {
@@ -120,11 +114,9 @@ export class XSSAdapter {
120
114
  };
121
115
  }
122
116
 
123
- /**
124
- * Deep sanitize nested objects/arrays
125
- */
117
+
126
118
  private deepSanitize(obj: any, options?: XSSOptions, visited = new WeakSet()): any {
127
- // Handle circular references
119
+
128
120
  if (obj && typeof obj === "object") {
129
121
  if (visited.has(obj)) {
130
122
  return obj;
@@ -1,54 +1,5 @@
1
- // import { ZodSchema, ZodError } from "zod";
2
- // import { ValidationError } from "../core/errors/ValidationError";
3
- // import { logger } from "../logging";
4
-
5
- // export class ZodAdapter {
6
- // private globalSchema?: ZodSchema;
7
-
8
- // constructor(globalSchema?: ZodSchema) {
9
- // this.globalSchema = globalSchema as any;
10
- // }
11
-
12
- // /**
13
- // * Validate with global + dynamic schema (dynamic overrides global)
14
- // */
15
- // validate(dynamicSchema?: ZodSchema) {
16
- // return (req: any, res: any, next: any) => {
17
- // const schema = dynamicSchema || this.globalSchema;
18
-
19
- // if (!schema) return next(); // no validation for this route
20
-
21
- // const result = schema.safeParse(req.body);
22
-
23
- // if (result.success) return next();
24
-
25
- // const zodErr: ZodError = result.error;
26
-
27
- // const issues = zodErr.issues.map(issue => ({
28
- // message: issue.message,
29
- // path: issue.path.join("."),
30
- // code: issue.code
31
- // }));
32
-
33
- // logger.warn("⚠ Zod validation failed", {
34
- // path: req.path,
35
- // method: req.method,
36
- // issues,
37
- // preview: JSON.stringify(req.body).slice(0, 200)
38
- // });
39
-
40
- // return next(
41
- // new ValidationError(issues[0]?.message || "Validation failed.")
42
- // );
43
- // };
44
- // }
45
- // }
46
-
47
-
48
-
49
- // src/adapters/ZodAdapter.ts - FIXED
50
1
  import { ZodSchema, ZodError } from "zod";
51
- import { ValidationError } from "../core/errors/ValidationError.js"; // ✅ Add .js
2
+ import { ValidationError } from "../core/errors/ValidationError.js";
52
3
  import { logger } from "../logging/index.js";
53
4
 
54
5
  export class ZodAdapter {
@@ -84,7 +35,7 @@ export class ZodAdapter {
84
35
  });
85
36
 
86
37
  return next(
87
- new ValidationError("Validation failed.", issues as any) // ✅ Pass issues
38
+ new ValidationError("Validation failed.", issues as any)
88
39
  );
89
40
  };
90
41
  }