hi-secure 1.0.0 → 1.0.3
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/ExpressRLAdapter.d.ts.map +1 -1
- package/dist/adapters/ExpressRLAdapter.js +0 -29
- package/dist/adapters/ExpressRLAdapter.js.map +1 -1
- package/dist/adapters/GoogleAdapter.d.ts.map +1 -1
- package/dist/adapters/GoogleAdapter.js +4 -3
- package/dist/adapters/GoogleAdapter.js.map +1 -1
- package/dist/adapters/JWTAdapter.d.ts.map +1 -1
- package/dist/adapters/JWTAdapter.js +3 -1
- package/dist/adapters/JWTAdapter.js.map +1 -1
- package/dist/core/HiSecure.d.ts +3 -18
- package/dist/core/HiSecure.d.ts.map +1 -1
- package/dist/core/HiSecure.js +29 -132
- package/dist/core/HiSecure.js.map +1 -1
- package/dist/core/errors/HttpError.d.ts +17 -0
- package/dist/core/errors/HttpError.d.ts.map +1 -0
- package/dist/core/errors/HttpError.js +36 -0
- package/dist/core/errors/HttpError.js.map +1 -0
- package/dist/core/useSecure.d.ts +0 -7
- package/dist/core/useSecure.d.ts.map +1 -1
- package/dist/core/useSecure.js +65 -21
- package/dist/core/useSecure.js.map +1 -1
- package/dist/index.d.ts +3 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -9
- package/dist/index.js.map +1 -1
- package/dist/managers/AuthManager.d.ts.map +1 -1
- package/dist/managers/AuthManager.js +18 -17
- package/dist/managers/AuthManager.js.map +1 -1
- package/dist/managers/ValidatorManager.d.ts +4 -6
- package/dist/managers/ValidatorManager.d.ts.map +1 -1
- package/dist/managers/ValidatorManager.js +97 -144
- package/dist/managers/ValidatorManager.js.map +1 -1
- package/dist/middlewares/errorHandler.js +2 -2
- package/dist/middlewares/errorHandler.js.map +1 -1
- package/dist/utils/normalizeOptions.d.ts.map +1 -1
- package/dist/utils/normalizeOptions.js +14 -4
- package/dist/utils/normalizeOptions.js.map +1 -1
- package/package.json +2 -2
- package/readme.md +195 -0
- package/src/adapters/ExpressRLAdapter.ts +0 -38
- package/src/adapters/GoogleAdapter.ts +5 -3
- package/src/adapters/JWTAdapter.ts +3 -1
- package/src/core/HiSecure.ts +414 -175
- package/src/core/useSecure.ts +91 -36
- package/src/index.ts +28 -12
- package/src/managers/AuthManager.ts +15 -13
- package/src/managers/ValidatorManager.ts +120 -182
- package/src/middlewares/errorHandler.ts +1 -1
- package/src/utils/normalizeOptions.ts +24 -9
- /package/src/core/errors/{HttpErrror.ts → HttpError.ts} +0 -0
package/src/core/useSecure.ts
CHANGED
|
@@ -61,51 +61,106 @@
|
|
|
61
61
|
// src/core/useSecure.ts - SIMPLER VERSION
|
|
62
62
|
// This is now optional since HiSecure class has fluent API
|
|
63
63
|
|
|
64
|
+
|
|
65
|
+
// import { HiSecure } from "./HiSecure.js";
|
|
66
|
+
// import { SecureOptions } from "./types/SecureOptions.js";
|
|
67
|
+
|
|
68
|
+
// /**
|
|
69
|
+
// * @deprecated Use HiSecure.middleware() or fluent API instead
|
|
70
|
+
// */
|
|
71
|
+
// export function useSecure(options?: SecureOptions | "api" | "strict" | "public") {
|
|
72
|
+
// console.warn("⚠ useSecure() is deprecated. Use HiSecure.middleware() or fluent API methods.");
|
|
73
|
+
// return HiSecure.middleware(options);
|
|
74
|
+
// }
|
|
75
|
+
|
|
76
|
+
// /**
|
|
77
|
+
// * Legacy support - route-level security
|
|
78
|
+
// */
|
|
79
|
+
// export function secureRoute(options?: SecureOptions) {
|
|
80
|
+
// const chain: any[] = [];
|
|
81
|
+
|
|
82
|
+
// if (options?.cors) {
|
|
83
|
+
// chain.push(HiSecure.cors(
|
|
84
|
+
// typeof options.cors === 'object' ? options.cors : undefined
|
|
85
|
+
// ));
|
|
86
|
+
// }
|
|
87
|
+
|
|
88
|
+
// if (options?.rateLimit) {
|
|
89
|
+
// chain.push(HiSecure.rateLimit(
|
|
90
|
+
// typeof options.rateLimit === 'object' ? options.rateLimit :
|
|
91
|
+
// options.rateLimit === "strict" ? "strict" : "relaxed"
|
|
92
|
+
// ));
|
|
93
|
+
// }
|
|
94
|
+
|
|
95
|
+
// if (options?.sanitize) {
|
|
96
|
+
// chain.push(HiSecure.sanitize(
|
|
97
|
+
// typeof options.sanitize === 'object' ? options.sanitize : undefined
|
|
98
|
+
// ));
|
|
99
|
+
// }
|
|
100
|
+
|
|
101
|
+
// if (options?.validate) {
|
|
102
|
+
// chain.push(HiSecure.validate(options.validate));
|
|
103
|
+
// }
|
|
104
|
+
|
|
105
|
+
// if (options?.auth) {
|
|
106
|
+
// chain.push(HiSecure.auth(
|
|
107
|
+
// typeof options.auth === 'object' ? options.auth : undefined
|
|
108
|
+
// ));
|
|
109
|
+
// }
|
|
110
|
+
|
|
111
|
+
// return chain;
|
|
112
|
+
// }
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
|
|
64
117
|
import { HiSecure } from "./HiSecure.js";
|
|
65
118
|
import { SecureOptions } from "./types/SecureOptions.js";
|
|
66
119
|
|
|
67
|
-
/**
|
|
68
|
-
* @deprecated Use HiSecure.middleware() or fluent API instead
|
|
69
|
-
*/
|
|
70
|
-
export function useSecure(options?: SecureOptions | "api" | "strict" | "public") {
|
|
71
|
-
console.warn("⚠ useSecure() is deprecated. Use HiSecure.middleware() or fluent API methods.");
|
|
72
|
-
return HiSecure.middleware(options);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Legacy support - route-level security
|
|
77
|
-
*/
|
|
78
120
|
export function secureRoute(options?: SecureOptions) {
|
|
121
|
+
if (!options) return [];
|
|
122
|
+
|
|
79
123
|
const chain: any[] = [];
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
124
|
+
|
|
125
|
+
// 🔥 1. CORS
|
|
126
|
+
if (options.cors !== undefined) {
|
|
127
|
+
chain.push(
|
|
128
|
+
HiSecure.cors(typeof options.cors === "object" ? options.cors : undefined)
|
|
129
|
+
);
|
|
85
130
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
131
|
+
|
|
132
|
+
// 🔥 2. Rate Limiting (auto strict / relaxed detection)
|
|
133
|
+
if (options.rateLimit !== undefined) {
|
|
134
|
+
const rl = options.rateLimit;
|
|
135
|
+
if (rl === "strict" || rl === "relaxed") {
|
|
136
|
+
chain.push(HiSecure.rateLimit(rl));
|
|
137
|
+
} else if (typeof rl === "object") {
|
|
138
|
+
chain.push(HiSecure.rateLimit(rl));
|
|
139
|
+
} else {
|
|
140
|
+
chain.push(HiSecure.rateLimit("relaxed"));
|
|
141
|
+
}
|
|
92
142
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
143
|
+
|
|
144
|
+
// 🔥 3. Sanitization
|
|
145
|
+
if (options.sanitize !== undefined) {
|
|
146
|
+
chain.push(
|
|
147
|
+
HiSecure.sanitize(typeof options.sanitize === "object" ? options.sanitize : undefined)
|
|
148
|
+
);
|
|
98
149
|
}
|
|
99
|
-
|
|
100
|
-
|
|
150
|
+
|
|
151
|
+
// 🔥 4. Validation — smart auto-detection
|
|
152
|
+
if (options.validate) {
|
|
101
153
|
chain.push(HiSecure.validate(options.validate));
|
|
102
154
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
155
|
+
|
|
156
|
+
// 🔥 5. Auth (roles included)
|
|
157
|
+
if (options.auth) {
|
|
158
|
+
chain.push(
|
|
159
|
+
HiSecure.auth(
|
|
160
|
+
typeof options.auth === "object" ? options.auth : undefined
|
|
161
|
+
)
|
|
162
|
+
);
|
|
108
163
|
}
|
|
109
|
-
|
|
164
|
+
|
|
110
165
|
return chain;
|
|
111
|
-
}
|
|
166
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,17 +1,33 @@
|
|
|
1
|
-
// src/index.ts - MAIN ENTRY POINT
|
|
2
|
-
import { HiSecure } from "./core/HiSecure.js";
|
|
3
|
-
import { useSecure, secureRoute } from "./core/useSecure.js";
|
|
1
|
+
// // src/index.ts - MAIN ENTRY POINT
|
|
2
|
+
// import { HiSecure } from "./core/HiSecure.js";
|
|
3
|
+
// import { useSecure, secureRoute } from "./core/useSecure.js";
|
|
4
|
+
|
|
5
|
+
// // Export the singleton instance for quick usage
|
|
6
|
+
// const hiSecure = HiSecure.getInstance();
|
|
7
|
+
|
|
8
|
+
// // Export everything
|
|
9
|
+
// export {
|
|
10
|
+
// HiSecure, // Class for advanced usage
|
|
11
|
+
// hiSecure, // Singleton instance
|
|
12
|
+
// useSecure, // Legacy function (deprecated)
|
|
13
|
+
// secureRoute // Route-level security helper
|
|
14
|
+
// };
|
|
15
|
+
|
|
16
|
+
// // Default export is the singleton instance
|
|
17
|
+
// export default hiSecure;
|
|
4
18
|
|
|
5
|
-
// Export the singleton instance for quick usage
|
|
6
|
-
const hiSecure = HiSecure.getInstance();
|
|
7
19
|
|
|
8
|
-
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
// src/index.ts
|
|
23
|
+
import { HiSecure } from "./core/HiSecure.js";
|
|
24
|
+
import { secureRoute } from "./core/useSecure.js"; // Only if kept
|
|
25
|
+
|
|
26
|
+
// DON'T auto-init here
|
|
9
27
|
export {
|
|
10
|
-
HiSecure,
|
|
11
|
-
|
|
12
|
-
useSecure, // Legacy function (deprecated)
|
|
13
|
-
secureRoute // Route-level security helper
|
|
28
|
+
HiSecure, // Class
|
|
29
|
+
secureRoute // Optional sugar API
|
|
14
30
|
};
|
|
15
31
|
|
|
16
|
-
// Default export
|
|
17
|
-
export default
|
|
32
|
+
// Default export: class itself (NOT instance)
|
|
33
|
+
export default HiSecure;
|
|
@@ -105,9 +105,11 @@
|
|
|
105
105
|
import { JWTAdapter } from "../adapters/JWTAdapter.js";
|
|
106
106
|
import { GoogleAdapter } from "../adapters/GoogleAdapter.js";
|
|
107
107
|
import { AdapterError } from "../core/errors/AdapterError.js";
|
|
108
|
-
import { HttpError } from "../core/errors/
|
|
108
|
+
import { HttpError } from "../core/errors/HttpError.js";
|
|
109
109
|
import { Request, Response, NextFunction } from "express";
|
|
110
|
-
import { logError, logWarn, logInfo } from "../logging";
|
|
110
|
+
// import { logError, logWarn, logInfo } from "../logging";
|
|
111
|
+
import { logger } from "../logging";
|
|
112
|
+
|
|
111
113
|
|
|
112
114
|
export interface AuthOptions {
|
|
113
115
|
jwtSecret: string;
|
|
@@ -130,10 +132,10 @@ export class AuthManager {
|
|
|
130
132
|
}
|
|
131
133
|
|
|
132
134
|
if (opts.jwtSecret.length < 32) {
|
|
133
|
-
|
|
135
|
+
logger.warn("⚠ JWT secret is less than 32 characters - consider using a stronger secret");
|
|
134
136
|
}
|
|
135
137
|
|
|
136
|
-
|
|
138
|
+
logger.info("AuthManager initialized");
|
|
137
139
|
|
|
138
140
|
this.jwtAdapter = new JWTAdapter({
|
|
139
141
|
secret: opts.jwtSecret,
|
|
@@ -142,17 +144,17 @@ export class AuthManager {
|
|
|
142
144
|
|
|
143
145
|
if (opts.googleClientId) {
|
|
144
146
|
this.googleAdapter = new GoogleAdapter(opts.googleClientId);
|
|
145
|
-
|
|
147
|
+
logger.info("GoogleAdapter enabled");
|
|
146
148
|
}
|
|
147
149
|
}
|
|
148
150
|
|
|
149
151
|
sign(payload: object, options?: { expiresIn?: string | number, jti?: string }) {
|
|
150
|
-
|
|
152
|
+
logger.info("JWT Sign called");
|
|
151
153
|
return this.jwtAdapter.sign(payload, options);
|
|
152
154
|
}
|
|
153
155
|
|
|
154
156
|
verify(token: string) {
|
|
155
|
-
|
|
157
|
+
logger.info("JWT Verify called");
|
|
156
158
|
return this.jwtAdapter.verify(token);
|
|
157
159
|
}
|
|
158
160
|
|
|
@@ -161,12 +163,12 @@ export class AuthManager {
|
|
|
161
163
|
throw new AdapterError("GoogleAdapter not configured.");
|
|
162
164
|
}
|
|
163
165
|
|
|
164
|
-
|
|
166
|
+
logger.info("Google ID Token verify called");
|
|
165
167
|
|
|
166
168
|
try {
|
|
167
169
|
return await this.googleAdapter.verifyIdToken(idToken);
|
|
168
170
|
} catch (err: any) {
|
|
169
|
-
|
|
171
|
+
logger.error("Google ID Token verification failed", { error: err?.message });
|
|
170
172
|
throw HttpError.Unauthorized("Invalid Google ID token");
|
|
171
173
|
}
|
|
172
174
|
}
|
|
@@ -185,7 +187,7 @@ export class AuthManager {
|
|
|
185
187
|
|
|
186
188
|
// If auth is required but no header
|
|
187
189
|
if (!header) {
|
|
188
|
-
|
|
190
|
+
logger.warn("Missing Authorization header", {
|
|
189
191
|
path: req.path,
|
|
190
192
|
method: req.method
|
|
191
193
|
});
|
|
@@ -195,7 +197,7 @@ export class AuthManager {
|
|
|
195
197
|
// Parse Bearer token
|
|
196
198
|
const [type, token] = String(header).split(" ");
|
|
197
199
|
if (type !== "Bearer" || !token) {
|
|
198
|
-
|
|
200
|
+
logger.warn("Invalid Authorization header", {
|
|
199
201
|
path: req.path,
|
|
200
202
|
method: req.method
|
|
201
203
|
});
|
|
@@ -214,7 +216,7 @@ export class AuthManager {
|
|
|
214
216
|
if (roles && roles.length > 0) {
|
|
215
217
|
const userRole = (decoded as any).role || (decoded as any).roles?.[0];
|
|
216
218
|
if (!userRole || !roles.includes(userRole)) {
|
|
217
|
-
|
|
219
|
+
logger.warn("Insufficient permissions", {
|
|
218
220
|
path: req.path,
|
|
219
221
|
requiredRoles: roles,
|
|
220
222
|
userRole
|
|
@@ -225,7 +227,7 @@ export class AuthManager {
|
|
|
225
227
|
|
|
226
228
|
return next();
|
|
227
229
|
} catch (err: any) {
|
|
228
|
-
|
|
230
|
+
logger.error("JWT verify failed", {
|
|
229
231
|
error: err?.message,
|
|
230
232
|
path: req.path,
|
|
231
233
|
method: req.method
|
|
@@ -1,132 +1,7 @@
|
|
|
1
|
-
// //
|
|
2
|
-
// // // import { logger } from "../logging";
|
|
3
|
-
// // // import { ValidationError } from "../core/errors/ValidationError";
|
|
4
|
-
|
|
5
|
-
// // // export class ValidatorManager {
|
|
6
|
-
// // // private config: HiSecureConfig["validation"];
|
|
7
|
-
// // // private primaryAdapter: any;
|
|
8
|
-
// // // private fallbackAdapter: any;
|
|
9
|
-
|
|
10
|
-
// // // constructor(
|
|
11
|
-
// // // config: HiSecureConfig["validation"],
|
|
12
|
-
// // // primaryAdapter: any,
|
|
13
|
-
// // // fallbackAdapter: any
|
|
14
|
-
// // // ) {
|
|
15
|
-
// // // this.config = config;
|
|
16
|
-
// // // this.primaryAdapter = primaryAdapter;
|
|
17
|
-
// // // this.fallbackAdapter = fallbackAdapter;
|
|
18
|
-
// // // }
|
|
19
|
-
|
|
20
|
-
// // // /**
|
|
21
|
-
// // // * Validate request body using primary adapter (Zod/express-validator).
|
|
22
|
-
// // // * Fallback is only used if the adapter implementation itself throws.
|
|
23
|
-
// // // */
|
|
24
|
-
// // // validate(schema: any) {
|
|
25
|
-
// // // return (req: any, res: any, next: any) => {
|
|
26
|
-
// // // try {
|
|
27
|
-
// // // const middleware = this.primaryAdapter.validate(schema);
|
|
28
|
-
// // // return middleware(req, res, next);
|
|
29
|
-
|
|
30
|
-
// // // } catch (err: any) {
|
|
31
|
-
// // // logger.warn("⚠ Primary validator failed", {
|
|
32
|
-
// // // error: err?.message,
|
|
33
|
-
// // // path: req.path,
|
|
34
|
-
// // // method: req.method
|
|
35
|
-
// // // });
|
|
36
|
-
|
|
37
|
-
// // // if (!this.fallbackAdapter) {
|
|
38
|
-
// // // return next(new ValidationError("Validation failed."));
|
|
39
|
-
// // // }
|
|
40
|
-
|
|
41
|
-
// // // try {
|
|
42
|
-
// // // logger.info("📌 Using fallback validator");
|
|
43
|
-
// // // const fallbackMiddleware = this.fallbackAdapter.validate(schema);
|
|
44
|
-
// // // return fallbackMiddleware(req, res, next);
|
|
45
|
-
|
|
46
|
-
// // // } catch (fallbackErr: any) {
|
|
47
|
-
// // // logger.error("❌ Fallback validation also failed", {
|
|
48
|
-
// // // error: fallbackErr?.message
|
|
49
|
-
// // // });
|
|
50
|
-
|
|
51
|
-
// // // return next(new ValidationError("Both validators failed."));
|
|
52
|
-
// // // }
|
|
53
|
-
// // // }
|
|
54
|
-
// // // };
|
|
55
|
-
// // // }
|
|
56
|
-
// // // }
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
// // import { HiSecureConfig } from "../core/config.js";
|
|
61
|
-
// // import { logger } from "../logging";
|
|
62
|
-
// // import { ValidationError } from "../core/errors/ValidationError.js";
|
|
63
|
-
|
|
64
|
-
// // interface ValidatorAdapter {
|
|
65
|
-
// // validate: (schema?: any) => any;
|
|
66
|
-
// // }
|
|
67
|
-
|
|
68
|
-
// // export class ValidatorManager {
|
|
69
|
-
// // private config: HiSecureConfig["validation"];
|
|
70
|
-
// // private primaryAdapter: ValidatorAdapter;
|
|
71
|
-
// // private fallbackAdapter: ValidatorAdapter | null;
|
|
72
|
-
|
|
73
|
-
// // constructor(
|
|
74
|
-
// // config: HiSecureConfig["validation"],
|
|
75
|
-
// // primaryAdapter: ValidatorAdapter,
|
|
76
|
-
// // fallbackAdapter: ValidatorAdapter | null
|
|
77
|
-
// // ) {
|
|
78
|
-
// // this.config = config;
|
|
79
|
-
// // this.primaryAdapter = primaryAdapter;
|
|
80
|
-
// // this.fallbackAdapter = fallbackAdapter;
|
|
81
|
-
// // }
|
|
82
|
-
|
|
83
|
-
// // /**
|
|
84
|
-
// // * MAIN DYNAMIC VALIDATOR ENTRY
|
|
85
|
-
// // * schema = per-route schema
|
|
86
|
-
// // * If schema is undefined → use global schema
|
|
87
|
-
// // */
|
|
88
|
-
// // validate(schema?: any) {
|
|
89
|
-
// // return (req: any, res: any, next: any) => {
|
|
90
|
-
// // try {
|
|
91
|
-
// // const middleware = this.primaryAdapter.validate(schema);
|
|
92
|
-
// // return middleware(req, res, next);
|
|
93
|
-
|
|
94
|
-
// // } catch (err: any) {
|
|
95
|
-
// // logger.warn("⚠ Primary validator failed", {
|
|
96
|
-
// // error: err?.message,
|
|
97
|
-
// // path: req.path,
|
|
98
|
-
// // method: req.method
|
|
99
|
-
// // });
|
|
100
|
-
|
|
101
|
-
// // if (!this.fallbackAdapter) {
|
|
102
|
-
// // return next(new ValidationError("Validation failed"));
|
|
103
|
-
// // }
|
|
104
|
-
|
|
105
|
-
// // try {
|
|
106
|
-
// // logger.info("📌 Using fallback validator");
|
|
107
|
-
// // const fallbackMiddleware = this.fallbackAdapter.validate(schema);
|
|
108
|
-
// // return fallbackMiddleware(req, res, next);
|
|
109
|
-
|
|
110
|
-
// // } catch (fallbackErr: any) {
|
|
111
|
-
// // logger.error("❌ Fallback validator also failed", {
|
|
112
|
-
// // error: fallbackErr?.message
|
|
113
|
-
// // });
|
|
114
|
-
|
|
115
|
-
// // return next(new ValidationError("Both validators failed"));
|
|
116
|
-
// // }
|
|
117
|
-
// // }
|
|
118
|
-
// // };
|
|
119
|
-
// // }
|
|
120
|
-
// // }
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
// // src/managers/ValidatorManager.ts - FIXED
|
|
1
|
+
// // src/managers/ValidatorManager.ts - COMPLETE FIXED
|
|
127
2
|
// import { logger } from "../logging";
|
|
128
3
|
// import { ValidationError } from "../core/errors/ValidationError.js";
|
|
129
|
-
// import { HiSecureConfig } from "../core/types/HiSecureConfig";
|
|
4
|
+
// import { HiSecureConfig } from "../core/types/HiSecureConfig.js"; // ✅ FIXED IMPORT
|
|
130
5
|
|
|
131
6
|
// interface ValidatorAdapter {
|
|
132
7
|
// validate: (schema?: any) => any;
|
|
@@ -196,74 +71,137 @@
|
|
|
196
71
|
// }
|
|
197
72
|
|
|
198
73
|
|
|
199
|
-
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
// // src/managers/ValidatorManager.ts
|
|
77
|
+
// import { logger } from "../logging";
|
|
78
|
+
// import { ValidationError } from "../core/errors/ValidationError.js";
|
|
79
|
+
|
|
80
|
+
// interface ValidatorAdapter {
|
|
81
|
+
// validate: (schema?: any) => any;
|
|
82
|
+
// }
|
|
83
|
+
|
|
84
|
+
// export class ValidatorManager {
|
|
85
|
+
// private primaryAdapter: ValidatorAdapter;
|
|
86
|
+
// private fallbackAdapter: ValidatorAdapter | null;
|
|
87
|
+
|
|
88
|
+
// constructor(primaryAdapter: ValidatorAdapter, fallbackAdapter: ValidatorAdapter | null) {
|
|
89
|
+
// this.primaryAdapter = primaryAdapter;
|
|
90
|
+
// this.fallbackAdapter = fallbackAdapter;
|
|
91
|
+
// }
|
|
92
|
+
|
|
93
|
+
// validate(schema?: any) {
|
|
94
|
+
// return (req: any, res: any, next: any) => {
|
|
95
|
+
// const isZod = schema && typeof schema === "object" && typeof schema.safeParse === "function";
|
|
96
|
+
// const isExpressValidator = Array.isArray(schema);
|
|
97
|
+
|
|
98
|
+
// let adapter: ValidatorAdapter;
|
|
99
|
+
|
|
100
|
+
// if (isZod) {
|
|
101
|
+
// adapter = this.primaryAdapter; // ZodAdapter
|
|
102
|
+
// logger.debug("📌 Using Zod adapter for validation");
|
|
103
|
+
// }
|
|
104
|
+
// else if (isExpressValidator) {
|
|
105
|
+
// adapter = this.fallbackAdapter!; // ExpressValidatorAdapter
|
|
106
|
+
// logger.debug("📌 Using express-validator adapter for validation");
|
|
107
|
+
// }
|
|
108
|
+
// else {
|
|
109
|
+
// return next(); // nothing to validate
|
|
110
|
+
// }
|
|
111
|
+
|
|
112
|
+
// const middleware = adapter.validate(schema);
|
|
113
|
+
|
|
114
|
+
// // Execute validation chain
|
|
115
|
+
// middleware(req, res, (err?: any) => {
|
|
116
|
+
// if (err instanceof ValidationError) {
|
|
117
|
+
// return next(err);
|
|
118
|
+
// }
|
|
119
|
+
// if (err) {
|
|
120
|
+
// logger.error("❌ Validator internal error", { error: err.message });
|
|
121
|
+
// return next(new ValidationError("Validation failed internally."));
|
|
122
|
+
// }
|
|
123
|
+
// next();
|
|
124
|
+
// });
|
|
125
|
+
// };
|
|
126
|
+
// }
|
|
127
|
+
// }
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
// src/managers/ValidatorManager.ts
|
|
200
134
|
import { logger } from "../logging";
|
|
201
135
|
import { ValidationError } from "../core/errors/ValidationError.js";
|
|
202
|
-
import { HiSecureConfig } from "../core/types/HiSecureConfig.js"; // ✅ FIXED IMPORT
|
|
203
136
|
|
|
204
137
|
interface ValidatorAdapter {
|
|
205
138
|
validate: (schema?: any) => any;
|
|
206
139
|
}
|
|
207
140
|
|
|
208
141
|
export class ValidatorManager {
|
|
209
|
-
private
|
|
210
|
-
private
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
primaryAdapter: ValidatorAdapter,
|
|
216
|
-
fallbackAdapter: ValidatorAdapter | null
|
|
217
|
-
) {
|
|
218
|
-
this.config = config;
|
|
219
|
-
this.primaryAdapter = primaryAdapter;
|
|
220
|
-
this.fallbackAdapter = fallbackAdapter;
|
|
142
|
+
private zodAdapter: ValidatorAdapter;
|
|
143
|
+
private expressAdapter: ValidatorAdapter;
|
|
144
|
+
|
|
145
|
+
constructor(zodAdapter: ValidatorAdapter, expressAdapter: ValidatorAdapter) {
|
|
146
|
+
this.zodAdapter = zodAdapter;
|
|
147
|
+
this.expressAdapter = expressAdapter;
|
|
221
148
|
}
|
|
222
149
|
|
|
223
150
|
validate(schema?: any) {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// If error is a ValidationError, pass it through (don't fallback!)
|
|
235
|
-
if (err instanceof ValidationError) {
|
|
236
|
-
logger.warn("⚠ Validation failed", {
|
|
237
|
-
path: req.path,
|
|
238
|
-
method: req.method,
|
|
239
|
-
error: err.message
|
|
240
|
-
});
|
|
241
|
-
return next(err);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// Only use fallback for ADAPTER errors, not validation errors
|
|
245
|
-
logger.warn("⚠ Primary validator adapter failed", {
|
|
246
|
-
error: err?.message,
|
|
247
|
-
path: req.path,
|
|
248
|
-
method: req.method
|
|
249
|
-
});
|
|
151
|
+
// const isZod = schema && typeof schema.safeParse === "function";
|
|
152
|
+
const isZod =
|
|
153
|
+
schema &&
|
|
154
|
+
typeof schema === "object" &&
|
|
155
|
+
typeof schema._def === "object" &&
|
|
156
|
+
typeof schema.safeParse === "function";
|
|
157
|
+
|
|
158
|
+
const isExpressValidator = Array.isArray(schema);
|
|
250
159
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
160
|
+
return (req: any, res: any, next: any) => {
|
|
161
|
+
let middleware;
|
|
162
|
+
|
|
163
|
+
if (isZod) {
|
|
164
|
+
logger.debug("📌 Using Zod adapter");
|
|
165
|
+
middleware = this.zodAdapter.validate(schema);
|
|
166
|
+
}
|
|
167
|
+
else if (isExpressValidator) {
|
|
168
|
+
logger.debug("📌 Using express-validator adapter");
|
|
169
|
+
middleware = this.expressAdapter.validate(schema);
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
return next(); // no schema found
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// CASE 1 — express-validator returns ARRAY
|
|
176
|
+
if (Array.isArray(middleware)) {
|
|
177
|
+
let idx = 0;
|
|
178
|
+
|
|
179
|
+
const run = (err?: any) => {
|
|
180
|
+
if (err) return next(err);
|
|
181
|
+
|
|
182
|
+
const fn = middleware[idx++];
|
|
183
|
+
if (!fn) return next(); // done
|
|
184
|
+
|
|
185
|
+
try {
|
|
186
|
+
fn(req, res, run);
|
|
187
|
+
} catch (error: any) {
|
|
188
|
+
next(new ValidationError(error.message));
|
|
263
189
|
}
|
|
264
|
-
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
return run();
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// CASE 2 — Zod returns SINGLE MIDDLEWARE
|
|
196
|
+
try {
|
|
197
|
+
middleware(req, res, (err?: any) => {
|
|
198
|
+
if (err) return next(err);
|
|
199
|
+
next();
|
|
265
200
|
});
|
|
266
|
-
})
|
|
201
|
+
} catch (err: any) {
|
|
202
|
+
next(new ValidationError(err.message));
|
|
203
|
+
}
|
|
267
204
|
};
|
|
268
205
|
}
|
|
269
|
-
}
|
|
206
|
+
}
|
|
207
|
+
|
|
@@ -168,7 +168,7 @@ import { AdapterError } from "../core/errors/AdapterError.js";
|
|
|
168
168
|
import { ValidationError } from "../core/errors/ValidationError.js";
|
|
169
169
|
import { SanitizerError } from "../core/errors/SanitizerError.js";
|
|
170
170
|
import { SecurityError } from "../core/errors/SecurityError.js";
|
|
171
|
-
import { HttpError } from "../core/errors/
|
|
171
|
+
import { HttpError } from "../core/errors/HttpError.js";
|
|
172
172
|
|
|
173
173
|
export function errorHandler(
|
|
174
174
|
err: any,
|
|
@@ -160,10 +160,16 @@ export function normalizeOptions(input?: SecureOptions | false): NormalizedOptio
|
|
|
160
160
|
options: typeof opts.sanitize === "object" ? opts.sanitize : undefined
|
|
161
161
|
},
|
|
162
162
|
|
|
163
|
+
// validate: {
|
|
164
|
+
// enabled: !!opts.validate,
|
|
165
|
+
// schema: opts.validate || undefined
|
|
166
|
+
// },
|
|
167
|
+
|
|
163
168
|
validate: {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
169
|
+
enabled: opts.validate !== undefined,
|
|
170
|
+
schema: opts.validate
|
|
171
|
+
},
|
|
172
|
+
|
|
167
173
|
|
|
168
174
|
json: {
|
|
169
175
|
enabled: opts.json === undefined ? true : opts.json !== false,
|
|
@@ -212,13 +218,22 @@ function normalizeRateLimit(value: SecureOptions["rateLimit"]): NormalizedOption
|
|
|
212
218
|
}
|
|
213
219
|
|
|
214
220
|
function normalizeAuth(value: SecureOptions["auth"]): NormalizedOptions["auth"] {
|
|
215
|
-
if (value === false) {
|
|
216
|
-
|
|
217
|
-
}
|
|
221
|
+
// if (value === false) {
|
|
222
|
+
// return { enabled: false, required: false };
|
|
223
|
+
// }
|
|
218
224
|
|
|
219
|
-
if (value === true || value === undefined) {
|
|
220
|
-
|
|
221
|
-
}
|
|
225
|
+
// if (value === true || value === undefined) {
|
|
226
|
+
// return { enabled: true, required: true };
|
|
227
|
+
// }
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
if (value === undefined) {
|
|
231
|
+
return { enabled: false, required: false };
|
|
232
|
+
}
|
|
233
|
+
if (value === true) {
|
|
234
|
+
return { enabled: true, required: true };
|
|
235
|
+
}
|
|
236
|
+
|
|
222
237
|
|
|
223
238
|
const authOptions = value as AuthOptions;
|
|
224
239
|
const enabled = authOptions.required !== false;
|