hi-secure 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/adapters/ArgonAdapter.d.ts +8 -0
- package/dist/adapters/ArgonAdapter.d.ts.map +1 -0
- package/dist/adapters/ArgonAdapter.js +45 -0
- package/dist/adapters/ArgonAdapter.js.map +1 -0
- package/dist/adapters/BcryptAdapter.d.ts +7 -0
- package/dist/adapters/BcryptAdapter.d.ts.map +1 -0
- package/dist/adapters/BcryptAdapter.js +48 -0
- package/dist/adapters/BcryptAdapter.js.map +1 -0
- package/dist/adapters/DomPurifyAdapter.d.ts +13 -0
- package/dist/adapters/DomPurifyAdapter.d.ts.map +1 -0
- package/dist/adapters/DomPurifyAdapter.js +61 -0
- package/dist/adapters/DomPurifyAdapter.js.map +1 -0
- package/dist/adapters/ExpressRLAdapter.d.ts +13 -0
- package/dist/adapters/ExpressRLAdapter.d.ts.map +1 -0
- package/dist/adapters/ExpressRLAdapter.js +68 -0
- package/dist/adapters/ExpressRLAdapter.js.map +1 -0
- package/dist/adapters/ExpressValidatorAdapter.d.ts +6 -0
- package/dist/adapters/ExpressValidatorAdapter.d.ts.map +1 -0
- package/dist/adapters/ExpressValidatorAdapter.js +78 -0
- package/dist/adapters/ExpressValidatorAdapter.js.map +1 -0
- package/dist/adapters/GoggleAdapter.d.ts +15 -0
- package/dist/adapters/GoggleAdapter.d.ts.map +1 -0
- package/dist/adapters/GoggleAdapter.js +91 -0
- package/dist/adapters/GoggleAdapter.js.map +1 -0
- package/dist/adapters/GoogleAdapter.d.ts +15 -0
- package/dist/adapters/GoogleAdapter.d.ts.map +1 -0
- package/dist/adapters/GoogleAdapter.js +159 -0
- package/dist/adapters/GoogleAdapter.js.map +1 -0
- package/dist/adapters/JWTAdapter.d.ts +28 -0
- package/dist/adapters/JWTAdapter.d.ts.map +1 -0
- package/dist/adapters/JWTAdapter.js +276 -0
- package/dist/adapters/JWTAdapter.js.map +1 -0
- package/dist/adapters/RLFlexibleAdapter.d.ts +11 -0
- package/dist/adapters/RLFlexibleAdapter.d.ts.map +1 -0
- package/dist/adapters/RLFlexibleAdapter.js +115 -0
- package/dist/adapters/RLFlexibleAdapter.js.map +1 -0
- package/dist/adapters/SanitizeHtmlAdapter.d.ts +12 -0
- package/dist/adapters/SanitizeHtmlAdapter.d.ts.map +1 -0
- package/dist/adapters/SanitizeHtmlAdapter.js +141 -0
- package/dist/adapters/SanitizeHtmlAdapter.js.map +1 -0
- package/dist/adapters/XSSAdapter.d.ts +33 -0
- package/dist/adapters/XSSAdapter.d.ts.map +1 -0
- package/dist/adapters/XSSAdapter.js +127 -0
- package/dist/adapters/XSSAdapter.js.map +1 -0
- package/dist/adapters/ZodAdapter.d.ts +7 -0
- package/dist/adapters/ZodAdapter.d.ts.map +1 -0
- package/dist/adapters/ZodAdapter.js +39 -0
- package/dist/adapters/ZodAdapter.js.map +1 -0
- package/dist/core/HiSecure.d.ts +62 -0
- package/dist/core/HiSecure.d.ts.map +1 -0
- package/dist/core/HiSecure.js +273 -0
- package/dist/core/HiSecure.js.map +1 -0
- package/dist/core/config.d.ts +3 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +53 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/constants.d.ts +37 -0
- package/dist/core/constants.d.ts.map +1 -0
- package/dist/core/constants.js +67 -0
- package/dist/core/constants.js.map +1 -0
- package/dist/core/errors/AdapterError.d.ts +5 -0
- package/dist/core/errors/AdapterError.d.ts.map +1 -0
- package/dist/core/errors/AdapterError.js +15 -0
- package/dist/core/errors/AdapterError.js.map +1 -0
- package/dist/core/errors/HttpErrror.d.ts +17 -0
- package/dist/core/errors/HttpErrror.d.ts.map +1 -0
- package/dist/core/errors/HttpErrror.js +36 -0
- package/dist/core/errors/HttpErrror.js.map +1 -0
- package/dist/core/errors/SanitizerError.d.ts +5 -0
- package/dist/core/errors/SanitizerError.d.ts.map +1 -0
- package/dist/core/errors/SanitizerError.js +14 -0
- package/dist/core/errors/SanitizerError.js.map +1 -0
- package/dist/core/errors/SecurityError.d.ts +5 -0
- package/dist/core/errors/SecurityError.d.ts.map +1 -0
- package/dist/core/errors/SecurityError.js +14 -0
- package/dist/core/errors/SecurityError.js.map +1 -0
- package/dist/core/errors/ValidationError.d.ts +5 -0
- package/dist/core/errors/ValidationError.d.ts.map +1 -0
- package/dist/core/errors/ValidationError.js +14 -0
- package/dist/core/errors/ValidationError.js.map +1 -0
- package/dist/core/types/HiSecureConfig.d.ts +47 -0
- package/dist/core/types/HiSecureConfig.d.ts.map +1 -0
- package/dist/core/types/HiSecureConfig.js +3 -0
- package/dist/core/types/HiSecureConfig.js.map +1 -0
- package/dist/core/types/SecureOptions.d.ts +30 -0
- package/dist/core/types/SecureOptions.d.ts.map +1 -0
- package/dist/core/types/SecureOptions.js +4 -0
- package/dist/core/types/SecureOptions.js.map +1 -0
- package/dist/core/useSecure.d.ts +10 -0
- package/dist/core/useSecure.d.ts.map +1 -0
- package/dist/core/useSecure.js +85 -0
- package/dist/core/useSecure.js.map +1 -0
- package/dist/examples/e1.d.ts +1 -0
- package/dist/examples/e1.d.ts.map +1 -0
- package/dist/examples/e1.js +3 -0
- package/dist/examples/e1.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/logging/index.d.ts +3 -0
- package/dist/logging/index.d.ts.map +1 -0
- package/dist/logging/index.js +19 -0
- package/dist/logging/index.js.map +1 -0
- package/dist/logging/morganSetup.d.ts +2 -0
- package/dist/logging/morganSetup.d.ts.map +1 -0
- package/dist/logging/morganSetup.js +9 -0
- package/dist/logging/morganSetup.js.map +1 -0
- package/dist/logging/winstonSetup.d.ts +6 -0
- package/dist/logging/winstonSetup.d.ts.map +1 -0
- package/dist/logging/winstonSetup.js +22 -0
- package/dist/logging/winstonSetup.js.map +1 -0
- package/dist/managers/AuthManager.d.ts +23 -0
- package/dist/managers/AuthManager.d.ts.map +1 -0
- package/dist/managers/AuthManager.js +190 -0
- package/dist/managers/AuthManager.js.map +1 -0
- package/dist/managers/CorsManager.d.ts +9 -0
- package/dist/managers/CorsManager.d.ts.map +1 -0
- package/dist/managers/CorsManager.js +55 -0
- package/dist/managers/CorsManager.js.map +1 -0
- package/dist/managers/HashManager.d.ts +22 -0
- package/dist/managers/HashManager.d.ts.map +1 -0
- package/dist/managers/HashManager.js +319 -0
- package/dist/managers/HashManager.js.map +1 -0
- package/dist/managers/JsonManager.d.ts +6 -0
- package/dist/managers/JsonManager.d.ts.map +1 -0
- package/dist/managers/JsonManager.js +142 -0
- package/dist/managers/JsonManager.js.map +1 -0
- package/dist/managers/RateLimitManager.d.ts +16 -0
- package/dist/managers/RateLimitManager.d.ts.map +1 -0
- package/dist/managers/RateLimitManager.js +108 -0
- package/dist/managers/RateLimitManager.js.map +1 -0
- package/dist/managers/SanitizerManager.d.ts +18 -0
- package/dist/managers/SanitizerManager.d.ts.map +1 -0
- package/dist/managers/SanitizerManager.js +296 -0
- package/dist/managers/SanitizerManager.js.map +1 -0
- package/dist/managers/ValidatorManager.d.ts +13 -0
- package/dist/managers/ValidatorManager.d.ts.map +1 -0
- package/dist/managers/ValidatorManager.js +218 -0
- package/dist/managers/ValidatorManager.js.map +1 -0
- package/dist/middlewares/errorHandler.d.ts +3 -0
- package/dist/middlewares/errorHandler.d.ts.map +1 -0
- package/dist/middlewares/errorHandler.js +94 -0
- package/dist/middlewares/errorHandler.js.map +1 -0
- package/dist/middlewares/index.d.ts +3 -0
- package/dist/middlewares/index.d.ts.map +1 -0
- package/dist/middlewares/index.js +19 -0
- package/dist/middlewares/index.js.map +1 -0
- package/dist/middlewares/requestLogger.d.ts +2 -0
- package/dist/middlewares/requestLogger.d.ts.map +1 -0
- package/dist/middlewares/requestLogger.js +8 -0
- package/dist/middlewares/requestLogger.js.map +1 -0
- package/dist/test/t1.d.ts +1 -0
- package/dist/test/t1.d.ts.map +1 -0
- package/dist/test/t1.js +3 -0
- package/dist/test/t1.js.map +1 -0
- package/dist/utils/deepFreeze.d.ts +2 -0
- package/dist/utils/deepFreeze.d.ts.map +1 -0
- package/dist/utils/deepFreeze.js +69 -0
- package/dist/utils/deepFreeze.js.map +1 -0
- package/dist/utils/deepMerge.d.ts +5 -0
- package/dist/utils/deepMerge.d.ts.map +1 -0
- package/dist/utils/deepMerge.js +68 -0
- package/dist/utils/deepMerge.js.map +1 -0
- package/dist/utils/normalizeOptions.d.ts +38 -0
- package/dist/utils/normalizeOptions.d.ts.map +1 -0
- package/dist/utils/normalizeOptions.js +119 -0
- package/dist/utils/normalizeOptions.js.map +1 -0
- package/package.json +50 -0
- package/src/adapters/ArgonAdapter.ts +41 -0
- package/src/adapters/BcryptAdapter.ts +49 -0
- package/src/adapters/ExpressRLAdapter.ts +84 -0
- package/src/adapters/ExpressValidatorAdapter.ts +99 -0
- package/src/adapters/GoogleAdapter.ts +206 -0
- package/src/adapters/JWTAdapter.ts +346 -0
- package/src/adapters/RLFlexibleAdapter.ts +139 -0
- package/src/adapters/SanitizeHtmlAdapter.ts +162 -0
- package/src/adapters/XSSAdapter.ts +153 -0
- package/src/adapters/ZodAdapter.ts +91 -0
- package/src/core/HiSecure.ts +955 -0
- package/src/core/config.ts +156 -0
- package/src/core/constants.ts +73 -0
- package/src/core/errors/AdapterError.ts +14 -0
- package/src/core/errors/HttpErrror.ts +46 -0
- package/src/core/errors/SanitizerError.ts +13 -0
- package/src/core/errors/SecurityError.ts +13 -0
- package/src/core/errors/ValidationError.ts +13 -0
- package/src/core/types/HiSecureConfig.ts +62 -0
- package/src/core/types/SecureOptions.ts +61 -0
- package/src/core/useSecure.ts +111 -0
- package/src/examples/e1.ts +1 -0
- package/src/index.ts +17 -0
- package/src/logging/index.ts +2 -0
- package/src/logging/morganSetup.ts +3 -0
- package/src/logging/winstonSetup.ts +17 -0
- package/src/managers/AuthManager.ts +237 -0
- package/src/managers/CorsManager.ts +58 -0
- package/src/managers/HashManager.ts +390 -0
- package/src/managers/JsonManager.ts +149 -0
- package/src/managers/RateLimitManager.ts +368 -0
- package/src/managers/SanitizerManager.ts +359 -0
- package/src/managers/ValidatorManager.ts +269 -0
- package/src/middlewares/errorHandler.ts +265 -0
- package/src/middlewares/index.ts +2 -0
- package/src/middlewares/requestLogger.ts +5 -0
- package/src/test/t1.ts +1 -0
- package/src/utils/deepFreeze.ts +76 -0
- package/src/utils/deepMerge.ts +87 -0
- package/src/utils/normalizeOptions.ts +265 -0
- package/tsconfig.json +30 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
// src/adapters/XSSAdapter.ts - NEW FILE
|
|
2
|
+
import { FilterXSS, getDefaultWhiteList, whiteList } from 'xss';
|
|
3
|
+
import { AdapterError } from "../core/errors/AdapterError.js";
|
|
4
|
+
import { logger } from "../logging/index.js";
|
|
5
|
+
|
|
6
|
+
export interface XSSOptions {
|
|
7
|
+
whiteList?: typeof whiteList;
|
|
8
|
+
stripIgnoreTag?: boolean;
|
|
9
|
+
stripIgnoreTagBody?: string[];
|
|
10
|
+
allowCommentTag?: boolean;
|
|
11
|
+
css?: boolean | { [key: string]: boolean };
|
|
12
|
+
onTag?: (tag: string, html: string, options: any) => string;
|
|
13
|
+
onTagAttr?: (tag: string, name: string, value: string, isWhiteAttr: boolean) => string;
|
|
14
|
+
onIgnoreTag?: (tag: string, html: string, options: any) => string;
|
|
15
|
+
[key: string]: any;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class XSSAdapter {
|
|
19
|
+
private globalOptions: XSSOptions;
|
|
20
|
+
private defaultFilter: FilterXSS;
|
|
21
|
+
|
|
22
|
+
constructor(options: XSSOptions = {}) {
|
|
23
|
+
this.globalOptions = options;
|
|
24
|
+
|
|
25
|
+
// Default safe configuration
|
|
26
|
+
const defaultOptions: XSSOptions = {
|
|
27
|
+
whiteList: getDefaultWhiteList(),
|
|
28
|
+
stripIgnoreTag: true, // Remove non-whitelisted tags completely
|
|
29
|
+
stripIgnoreTagBody: ['script', 'style', 'iframe', 'object', 'embed'],
|
|
30
|
+
allowCommentTag: false,
|
|
31
|
+
css: false, // Disable CSS by default
|
|
32
|
+
onTag: (tag, html, options) => {
|
|
33
|
+
// Add noopener/noreferrer to links for security
|
|
34
|
+
if (tag === 'a') {
|
|
35
|
+
return html.replace(/<a /i, '<a target="_blank" rel="noopener noreferrer" ');
|
|
36
|
+
}
|
|
37
|
+
return html;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const finalOptions = { ...defaultOptions, ...options };
|
|
42
|
+
this.defaultFilter = new FilterXSS(finalOptions);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Sanitize a string with global + dynamic merged options
|
|
47
|
+
*/
|
|
48
|
+
sanitize(input: string, dynamicOptions?: XSSOptions): string {
|
|
49
|
+
try {
|
|
50
|
+
if (typeof input !== "string") {
|
|
51
|
+
return input as any;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// If no dynamic options, use default filter
|
|
55
|
+
if (!dynamicOptions || Object.keys(dynamicOptions).length === 0) {
|
|
56
|
+
return this.defaultFilter.process(input);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Merge options for this specific call
|
|
60
|
+
const mergedOptions = { ...this.globalOptions, ...dynamicOptions };
|
|
61
|
+
const customFilter = new FilterXSS(mergedOptions);
|
|
62
|
+
|
|
63
|
+
return customFilter.process(input);
|
|
64
|
+
|
|
65
|
+
} catch (err: any) {
|
|
66
|
+
logger.error("❌ XSS sanitizer failed", {
|
|
67
|
+
error: err?.message,
|
|
68
|
+
preview: input?.slice?.(0, 80)
|
|
69
|
+
});
|
|
70
|
+
throw new AdapterError("XSS sanitizer failed.");
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Middleware wrapper WITH dynamic options
|
|
76
|
+
* Doesn't mutate original request - creates sanitized copy
|
|
77
|
+
*/
|
|
78
|
+
middleware(dynamicOptions?: XSSOptions) {
|
|
79
|
+
return (req: any, _res: any, next: any) => {
|
|
80
|
+
try {
|
|
81
|
+
if (req.body && typeof req.body === "object") {
|
|
82
|
+
const originalBody = req.body;
|
|
83
|
+
const sanitizedBody: any = Array.isArray(originalBody) ? [] : {};
|
|
84
|
+
|
|
85
|
+
for (const key of Object.keys(originalBody)) {
|
|
86
|
+
const val = originalBody[key];
|
|
87
|
+
|
|
88
|
+
if (typeof val === "string") {
|
|
89
|
+
sanitizedBody[key] = this.sanitize(val, dynamicOptions);
|
|
90
|
+
} else if (Array.isArray(val)) {
|
|
91
|
+
sanitizedBody[key] = val.map((v) =>
|
|
92
|
+
typeof v === "string"
|
|
93
|
+
? this.sanitize(v, dynamicOptions)
|
|
94
|
+
: v
|
|
95
|
+
);
|
|
96
|
+
} else if (val && typeof val === "object") {
|
|
97
|
+
// Handle nested objects (simple recursion)
|
|
98
|
+
sanitizedBody[key] = this.deepSanitize(val, dynamicOptions);
|
|
99
|
+
} else {
|
|
100
|
+
sanitizedBody[key] = val;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Store sanitized version separately
|
|
105
|
+
req.sanitizedBody = sanitizedBody;
|
|
106
|
+
|
|
107
|
+
logger.debug("🛡️ XSS sanitizer applied", {
|
|
108
|
+
originalKeys: Object.keys(originalBody),
|
|
109
|
+
sanitizedKeys: Object.keys(sanitizedBody)
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
next();
|
|
114
|
+
} catch (err: any) {
|
|
115
|
+
logger.error("❌ XSS middleware failed", {
|
|
116
|
+
error: err?.message || err
|
|
117
|
+
});
|
|
118
|
+
next(err);
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Deep sanitize nested objects/arrays
|
|
125
|
+
*/
|
|
126
|
+
private deepSanitize(obj: any, options?: XSSOptions, visited = new WeakSet()): any {
|
|
127
|
+
// Handle circular references
|
|
128
|
+
if (obj && typeof obj === "object") {
|
|
129
|
+
if (visited.has(obj)) {
|
|
130
|
+
return obj;
|
|
131
|
+
}
|
|
132
|
+
visited.add(obj);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (typeof obj === "string") {
|
|
136
|
+
return this.sanitize(obj, options);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (Array.isArray(obj)) {
|
|
140
|
+
return obj.map(item => this.deepSanitize(item, options, visited));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (obj && typeof obj === "object") {
|
|
144
|
+
const result: any = {};
|
|
145
|
+
for (const key of Object.keys(obj)) {
|
|
146
|
+
result[key] = this.deepSanitize(obj[key], options, visited);
|
|
147
|
+
}
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return obj;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
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
|
+
import { ZodSchema, ZodError } from "zod";
|
|
51
|
+
import { ValidationError } from "../core/errors/ValidationError.js"; // ✅ Add .js
|
|
52
|
+
import { logger } from "../logging/index.js";
|
|
53
|
+
|
|
54
|
+
export class ZodAdapter {
|
|
55
|
+
private globalSchema?: ZodSchema;
|
|
56
|
+
|
|
57
|
+
constructor(globalSchema?: ZodSchema) {
|
|
58
|
+
this.globalSchema = globalSchema;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
validate(dynamicSchema?: ZodSchema) {
|
|
62
|
+
return (req: any, res: any, next: any) => {
|
|
63
|
+
const schema = dynamicSchema || this.globalSchema;
|
|
64
|
+
|
|
65
|
+
if (!schema) return next();
|
|
66
|
+
|
|
67
|
+
const result = schema.safeParse(req.body);
|
|
68
|
+
|
|
69
|
+
if (result.success) return next();
|
|
70
|
+
|
|
71
|
+
const zodErr: ZodError = result.error;
|
|
72
|
+
|
|
73
|
+
const issues = zodErr.issues.map(issue => ({
|
|
74
|
+
message: issue.message,
|
|
75
|
+
path: issue.path.join("."),
|
|
76
|
+
code: issue.code
|
|
77
|
+
}));
|
|
78
|
+
|
|
79
|
+
logger.warn("⚠ Zod validation failed", {
|
|
80
|
+
path: req.path,
|
|
81
|
+
method: req.method,
|
|
82
|
+
issues,
|
|
83
|
+
preview: JSON.stringify(req.body).slice(0, 200)
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
return next(
|
|
87
|
+
new ValidationError("Validation failed.", issues as any) // ✅ Pass issues
|
|
88
|
+
);
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
}
|