hi-secure 1.0.15 → 1.0.16
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 +1 -1
- package/dist/adapters/ArgonAdapter.d.ts.map +1 -1
- package/dist/adapters/ArgonAdapter.js +43 -5
- package/dist/adapters/ArgonAdapter.js.map +1 -1
- package/dist/adapters/BcryptAdapter.d.ts.map +1 -1
- package/dist/adapters/BcryptAdapter.js +43 -3
- package/dist/adapters/BcryptAdapter.js.map +1 -1
- package/dist/adapters/ExpressRLAdapter.d.ts.map +1 -1
- package/dist/adapters/ExpressRLAdapter.js +48 -6
- package/dist/adapters/ExpressRLAdapter.js.map +1 -1
- package/dist/adapters/ExpressValidatorAdapter.d.ts.map +1 -1
- package/dist/adapters/ExpressValidatorAdapter.js +50 -10
- package/dist/adapters/ExpressValidatorAdapter.js.map +1 -1
- package/dist/adapters/GoogleAdapter.d.ts.map +1 -1
- package/dist/adapters/GoogleAdapter.js +82 -16
- package/dist/adapters/GoogleAdapter.js.map +1 -1
- package/dist/adapters/JWTAdapter.d.ts.map +1 -1
- package/dist/adapters/JWTAdapter.js +104 -15
- package/dist/adapters/JWTAdapter.js.map +1 -1
- package/dist/adapters/RLFlexibleAdapter.d.ts.map +1 -1
- package/dist/adapters/RLFlexibleAdapter.js +87 -12
- package/dist/adapters/RLFlexibleAdapter.js.map +1 -1
- package/dist/adapters/SanitizeHtmlAdapter.d.ts.map +1 -1
- package/dist/adapters/SanitizeHtmlAdapter.js +81 -13
- package/dist/adapters/SanitizeHtmlAdapter.js.map +1 -1
- package/dist/adapters/XSSAdapter.d.ts +1 -1
- package/dist/adapters/XSSAdapter.d.ts.map +1 -1
- package/dist/adapters/XSSAdapter.js +137 -20
- package/dist/adapters/XSSAdapter.js.map +1 -1
- package/dist/adapters/ZodAdapter.d.ts +1 -1
- package/dist/adapters/ZodAdapter.d.ts.map +1 -1
- package/dist/adapters/ZodAdapter.js +13 -8
- package/dist/adapters/ZodAdapter.js.map +1 -1
- package/dist/core/HiSecure.d.ts +3 -4
- package/dist/core/HiSecure.d.ts.map +1 -1
- package/dist/core/HiSecure.js +108 -121
- package/dist/core/HiSecure.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/dist/logging/index.d.ts.map +1 -1
- package/dist/logging/index.js +2 -0
- package/dist/logging/index.js.map +1 -1
- package/dist/logging/morganSetup.d.ts.map +1 -1
- package/dist/logging/morganSetup.js +22 -1
- package/dist/logging/morganSetup.js.map +1 -1
- package/dist/logging/winstonSetup.d.ts.map +1 -1
- package/dist/logging/winstonSetup.js +61 -3
- package/dist/logging/winstonSetup.js.map +1 -1
- package/dist/managers/AuthManager.d.ts +2 -2
- package/dist/managers/AuthManager.d.ts.map +1 -1
- package/dist/managers/AuthManager.js +167 -31
- package/dist/managers/AuthManager.js.map +1 -1
- package/dist/managers/CorsManager.d.ts.map +1 -1
- package/dist/managers/CorsManager.js +46 -11
- package/dist/managers/CorsManager.js.map +1 -1
- package/dist/managers/HashManager.d.ts +1 -1
- package/dist/managers/HashManager.d.ts.map +1 -1
- package/dist/managers/HashManager.js +127 -17
- package/dist/managers/HashManager.js.map +1 -1
- package/dist/managers/JsonManager.d.ts +1 -1
- package/dist/managers/JsonManager.d.ts.map +1 -1
- package/dist/managers/JsonManager.js +99 -16
- package/dist/managers/JsonManager.js.map +1 -1
- package/dist/managers/RateLimitManager.d.ts +1 -1
- package/dist/managers/RateLimitManager.d.ts.map +1 -1
- package/dist/managers/RateLimitManager.js +46 -22
- package/dist/managers/RateLimitManager.js.map +1 -1
- package/dist/managers/SanitizerManager.d.ts.map +1 -1
- package/dist/managers/SanitizerManager.js +112 -15
- package/dist/managers/SanitizerManager.js.map +1 -1
- package/dist/managers/ValidatorManager.d.ts.map +1 -1
- package/dist/managers/ValidatorManager.js +90 -7
- package/dist/managers/ValidatorManager.js.map +1 -1
- package/package.json +2 -6
- package/readme.md +3 -6
- package/src/adapters/ArgonAdapter.ts +55 -6
- package/src/adapters/BcryptAdapter.ts +56 -8
- package/src/adapters/ExpressRLAdapter.ts +62 -9
- package/src/adapters/ExpressValidatorAdapter.ts +67 -11
- package/src/adapters/GoogleAdapter.ts +106 -21
- package/src/adapters/JWTAdapter.ts +129 -21
- package/src/adapters/RLFlexibleAdapter.ts +113 -16
- package/src/adapters/SanitizeHtmlAdapter.ts +111 -18
- package/src/adapters/XSSAdapter.ts +183 -39
- package/src/adapters/ZodAdapter.ts +56 -10
- package/src/core/HiSecure.ts +496 -162
- package/src/index.ts +4 -0
- package/src/logging/index.ts +6 -0
- package/src/logging/morganSetup.ts +36 -1
- package/src/logging/winstonSetup.ts +97 -8
- package/src/managers/AuthManager.ts +205 -34
- package/src/managers/CorsManager.ts +63 -16
- package/src/managers/HashManager.ts +156 -19
- package/src/managers/JsonManager.ts +119 -15
- package/src/managers/RateLimitManager.ts +174 -29
- package/src/managers/SanitizerManager.ts +150 -25
- package/src/managers/ValidatorManager.ts +115 -15
package/src/core/HiSecure.ts
CHANGED
|
@@ -1,9 +1,368 @@
|
|
|
1
|
+
// import { HiSecureConfig } from "./types/HiSecureConfig.js";
|
|
2
|
+
// import { defaultConfig } from "./config.js";
|
|
3
|
+
// import { LIB_NAME, LIB_VERSION } from "./constants.js";
|
|
4
|
+
// import { deepMerge } from "../utils/deepMerge.js";
|
|
5
|
+
// import { deepFreeze } from "../utils/deepFreeze.js";
|
|
6
|
+
// import { logger } from "../logging/index.js";
|
|
7
|
+
|
|
8
|
+
// // Adapters
|
|
9
|
+
// import { ArgonAdapter } from "../adapters/ArgonAdapter.js";
|
|
10
|
+
// import { BcryptAdapter } from "../adapters/BcryptAdapter.js";
|
|
11
|
+
// import { RLFlexibleAdapter } from "../adapters/RLFlexibleAdapter.js";
|
|
12
|
+
// import { ExpressRLAdapter } from "../adapters/ExpressRLAdapter.js";
|
|
13
|
+
// import { ZodAdapter } from "../adapters/ZodAdapter.js";
|
|
14
|
+
// import { ExpressValidatorAdapter } from "../adapters/ExpressValidatorAdapter.js";
|
|
15
|
+
// import { SanitizeHtmlAdapter } from "../adapters/SanitizeHtmlAdapter.js";
|
|
16
|
+
// import { XSSAdapter } from "../adapters/XSSAdapter.js";
|
|
17
|
+
|
|
18
|
+
// // Managers
|
|
19
|
+
// import { HashManager } from "../managers/HashManager.js";
|
|
20
|
+
// import { RateLimitManager } from "../managers/RateLimitManager.js";
|
|
21
|
+
// import { ValidatorManager } from "../managers/ValidatorManager.js";
|
|
22
|
+
// import { SanitizerManager } from "../managers/SanitizerManager.js";
|
|
23
|
+
// import { JsonManager } from "../managers/JsonManager.js";
|
|
24
|
+
// import { CorsManager } from "../managers/CorsManager.js";
|
|
25
|
+
// import { AuthManager } from "../managers/AuthManager.js";
|
|
26
|
+
|
|
27
|
+
// // Middlewares
|
|
28
|
+
// import helmet from "helmet";
|
|
29
|
+
// import hpp from "hpp";
|
|
30
|
+
// import compression from "compression";
|
|
31
|
+
// import { errorHandler } from "../middlewares/errorHandler.js";
|
|
32
|
+
|
|
33
|
+
// // Types
|
|
34
|
+
// import { SecureOptions, ValidationSchema} from "./types/SecureOptions.js";
|
|
35
|
+
|
|
36
|
+
// export class HiSecure {
|
|
37
|
+
// private static instance: HiSecure | null = null;
|
|
38
|
+
// private config: HiSecureConfig;
|
|
39
|
+
// private initialized = false;
|
|
40
|
+
|
|
41
|
+
// // Managers
|
|
42
|
+
// public hashManager!: HashManager;
|
|
43
|
+
// public rateLimitManager!: RateLimitManager;
|
|
44
|
+
// public validatorManager!: ValidatorManager;
|
|
45
|
+
// public sanitizerManager!: SanitizerManager;
|
|
46
|
+
// public jsonManager!: JsonManager;
|
|
47
|
+
// public corsManager!: CorsManager;
|
|
48
|
+
// public authManager?: AuthManager;
|
|
49
|
+
|
|
50
|
+
// // Internal adapters
|
|
51
|
+
// private hashingPrimary: any;
|
|
52
|
+
// private hashingFallback: any;
|
|
53
|
+
// private rateLimiterPrimary: any;
|
|
54
|
+
// private rateLimiterFallback: any;
|
|
55
|
+
// private sanitizerPrimary: any;
|
|
56
|
+
// private sanitizerFallback: any;
|
|
57
|
+
|
|
58
|
+
// // Private constructor for singleton
|
|
59
|
+
// private constructor(userConfig: Partial<HiSecureConfig> = {}) {
|
|
60
|
+
// this.config = deepMerge(defaultConfig, userConfig);
|
|
61
|
+
// }
|
|
62
|
+
|
|
63
|
+
// // SINGLETON & INITIALIZATION
|
|
64
|
+
|
|
65
|
+
// static getInstance(config?: Partial<HiSecureConfig>): HiSecure {
|
|
66
|
+
// if (!HiSecure.instance) {
|
|
67
|
+
// HiSecure.instance = new HiSecure(config);
|
|
68
|
+
// HiSecure.instance.init();
|
|
69
|
+
// }
|
|
70
|
+
// return HiSecure.instance;
|
|
71
|
+
// }
|
|
72
|
+
|
|
73
|
+
// static resetInstance(): void {
|
|
74
|
+
// HiSecure.instance = null;
|
|
75
|
+
// }
|
|
76
|
+
|
|
77
|
+
// init(): void {
|
|
78
|
+
// if (this.initialized) {
|
|
79
|
+
// logger.warn(" HiSecure already initialized");
|
|
80
|
+
// return;
|
|
81
|
+
// }
|
|
82
|
+
|
|
83
|
+
// logger.info(` ${LIB_NAME} v${LIB_VERSION} initializing...`);
|
|
84
|
+
|
|
85
|
+
// this.setupAdapters();
|
|
86
|
+
// this.setupManagers();
|
|
87
|
+
// this.setupDynamicManagers();
|
|
88
|
+
|
|
89
|
+
// deepFreeze(this.config);
|
|
90
|
+
// // deep Freeze - for now we remove from manager it needs to manage the adapters
|
|
91
|
+
// // deepFreeze(this.hashManager);
|
|
92
|
+
// // deepFreeze(this.rateLimitManager);
|
|
93
|
+
// // deepFreeze(this.validatorManager);
|
|
94
|
+
// // deepFreeze(this.sanitizerManager);
|
|
95
|
+
// // deepFreeze(this.jsonManager);
|
|
96
|
+
// // deepFreeze(this.corsManager);
|
|
97
|
+
// // if (this.authManager) deepFreeze(this.authManager);
|
|
98
|
+
|
|
99
|
+
// this.initialized = true;
|
|
100
|
+
// logger.info("HiSecure initialized successfully");
|
|
101
|
+
// }
|
|
102
|
+
|
|
103
|
+
// isInitialized(): boolean {
|
|
104
|
+
// return this.initialized;
|
|
105
|
+
// }
|
|
106
|
+
|
|
107
|
+
// // FLUENT API METHODS (Route-level security)
|
|
108
|
+
|
|
109
|
+
// static auth(options?: { required?: boolean; roles?: string[] }) {
|
|
110
|
+
// const instance = this.getInstance();
|
|
111
|
+
// if (!instance.authManager) {
|
|
112
|
+
// throw new Error("Auth not enabled. Set auth.enabled=true in config.");
|
|
113
|
+
// }
|
|
114
|
+
// return instance.authManager.protect(options);
|
|
115
|
+
// }
|
|
116
|
+
|
|
117
|
+
// static validate(schema: ValidationSchema) {
|
|
118
|
+
// return this.getInstance().validatorManager.validate(schema);
|
|
119
|
+
// }
|
|
120
|
+
|
|
121
|
+
// static sanitize(options?: any) {
|
|
122
|
+
// return this.getInstance().sanitizerManager.middleware(options);
|
|
123
|
+
// }
|
|
124
|
+
|
|
125
|
+
// static rateLimit(preset: "strict" | "relaxed" | "api" | object) {
|
|
126
|
+
// const instance = this.getInstance();
|
|
127
|
+
|
|
128
|
+
// if (typeof preset === "string") {
|
|
129
|
+
// const presets: Record<string, { mode?: "strict" | "relaxed" | "api"; options?: any }> = {
|
|
130
|
+
// strict: { mode: "strict" },
|
|
131
|
+
// relaxed: { mode: "relaxed" },
|
|
132
|
+
// api: { mode: "api", options: { max: 100, windowMs: 60000 } }
|
|
133
|
+
// };
|
|
134
|
+
// return instance.rateLimitManager.middleware(presets[preset] || {});
|
|
135
|
+
// }
|
|
136
|
+
|
|
137
|
+
// return instance.rateLimitManager.middleware({ options: preset });
|
|
138
|
+
// }
|
|
139
|
+
|
|
140
|
+
// static cors(options?: any) {
|
|
141
|
+
// return this.getInstance().corsManager.middleware(options);
|
|
142
|
+
// }
|
|
143
|
+
|
|
144
|
+
// static json(options?: any) {
|
|
145
|
+
// const instance = this.getInstance();
|
|
146
|
+
// const chain = [];
|
|
147
|
+
// chain.push(instance.jsonManager.middleware(options));
|
|
148
|
+
// chain.push(instance.jsonManager.urlencoded());
|
|
149
|
+
// return chain;
|
|
150
|
+
// }
|
|
151
|
+
|
|
152
|
+
// // UTILITY METHODS - For direct use
|
|
153
|
+
|
|
154
|
+
// static async hash(password: string): Promise<string> {
|
|
155
|
+
// const instance = this.getInstance();
|
|
156
|
+
// const result = await instance.hashManager.hash(password, { allowFallback: true });
|
|
157
|
+
// return result.hash;
|
|
158
|
+
// }
|
|
159
|
+
|
|
160
|
+
// static async verify(password: string, hash: string): Promise<boolean> {
|
|
161
|
+
// return this.getInstance().hashManager.verify(password, hash);
|
|
162
|
+
// }
|
|
163
|
+
|
|
164
|
+
// static jwt = {
|
|
165
|
+
// sign: (payload: object, options?: any) => {
|
|
166
|
+
// const instance = HiSecure.getInstance();
|
|
167
|
+
// if (!instance.authManager) {
|
|
168
|
+
// throw new Error("Auth not enabled");
|
|
169
|
+
// }
|
|
170
|
+
// return instance.authManager.sign(payload, options);
|
|
171
|
+
// },
|
|
172
|
+
|
|
173
|
+
// verify: (token: string) => {
|
|
174
|
+
// const instance = HiSecure.getInstance();
|
|
175
|
+
// if (!instance.authManager) {
|
|
176
|
+
// throw new Error("Auth not enabled");
|
|
177
|
+
// }
|
|
178
|
+
// return instance.authManager.verify(token);
|
|
179
|
+
// },
|
|
180
|
+
|
|
181
|
+
// google: {
|
|
182
|
+
// verifyIdToken: (idToken: string) => {
|
|
183
|
+
// const instance = HiSecure.getInstance();
|
|
184
|
+
// if (!instance.authManager) {
|
|
185
|
+
// throw new Error("Auth not enabled");
|
|
186
|
+
// }
|
|
187
|
+
// return instance.authManager.verifyGoogleIdToken(idToken);
|
|
188
|
+
// }
|
|
189
|
+
// }
|
|
190
|
+
// };
|
|
191
|
+
|
|
192
|
+
// // GLOBAL MIDDLEWARE (app.use())
|
|
193
|
+
|
|
194
|
+
// static middleware(options?: SecureOptions | "api" | "strict" | "public") {
|
|
195
|
+
// const instance = this.getInstance();
|
|
196
|
+
|
|
197
|
+
// // Handle preset strings
|
|
198
|
+
// if (typeof options === "string") {
|
|
199
|
+
// const presets: Record<string, SecureOptions> = {
|
|
200
|
+
// api: { cors: true, rateLimit: "relaxed" as any, sanitize: true },
|
|
201
|
+
// strict: { cors: true, rateLimit: "strict" as any, sanitize: true, auth: true },
|
|
202
|
+
// public: { cors: true, rateLimit: true as any, sanitize: false }
|
|
203
|
+
// };
|
|
204
|
+
// const presetOptions = presets[options];
|
|
205
|
+
// if (presetOptions) {
|
|
206
|
+
// return instance.createMiddlewareChain(presetOptions);
|
|
207
|
+
// }
|
|
208
|
+
// return instance.createMiddlewareChain({});
|
|
209
|
+
// }
|
|
210
|
+
|
|
211
|
+
// return instance.createMiddlewareChain(options || {});
|
|
212
|
+
// }
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
// // Internal Methods
|
|
216
|
+
|
|
217
|
+
// private setupAdapters(): void {
|
|
218
|
+
// logger.info(" Setting up adapters...");
|
|
219
|
+
|
|
220
|
+
// // Hashing
|
|
221
|
+
// this.hashingPrimary = this.config.hashing.primary === "argon2"
|
|
222
|
+
// ? new ArgonAdapter()
|
|
223
|
+
// : new BcryptAdapter(this.config.hashing.saltRounds);
|
|
224
|
+
|
|
225
|
+
// this.hashingFallback = this.config.hashing.fallback === "bcrypt"
|
|
226
|
+
// ? new BcryptAdapter(this.config.hashing.saltRounds)
|
|
227
|
+
// : null;
|
|
228
|
+
|
|
229
|
+
// // Rate limiting
|
|
230
|
+
// this.rateLimiterPrimary = this.config.rateLimiter.useAdaptiveMode
|
|
231
|
+
// ? new RLFlexibleAdapter()
|
|
232
|
+
// : new ExpressRLAdapter();
|
|
233
|
+
// this.rateLimiterFallback = new ExpressRLAdapter();
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
// // // Validation - we handle this in d/f way for now
|
|
239
|
+
// // this.validatorPrimary = this.config.validation.mode === "zod"
|
|
240
|
+
// // ? new ZodAdapter()
|
|
241
|
+
// // : new ExpressValidatorAdapter();
|
|
242
|
+
// // this.validatorFallback = this.config.validation.fallback === "express-validator"
|
|
243
|
+
// // ? new ExpressValidatorAdapter()
|
|
244
|
+
// // : null;
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
// // Sanitization
|
|
248
|
+
// this.sanitizerPrimary = new SanitizeHtmlAdapter(this.config.sanitizer);
|
|
249
|
+
// this.sanitizerFallback = new XSSAdapter(this.config.sanitizer);
|
|
250
|
+
|
|
251
|
+
// logger.info("Adapters ready");
|
|
252
|
+
// }
|
|
253
|
+
|
|
254
|
+
// private setupManagers(): void {
|
|
255
|
+
// this.hashManager = new HashManager(
|
|
256
|
+
// this.config.hashing,
|
|
257
|
+
// this.hashingPrimary,
|
|
258
|
+
// this.hashingFallback
|
|
259
|
+
// );
|
|
260
|
+
|
|
261
|
+
// this.rateLimitManager = new RateLimitManager(
|
|
262
|
+
// this.config.rateLimiter,
|
|
263
|
+
// this.rateLimiterPrimary,
|
|
264
|
+
// this.rateLimiterFallback
|
|
265
|
+
// );
|
|
266
|
+
|
|
267
|
+
// this.validatorManager = new ValidatorManager(
|
|
268
|
+
// // this.config.validation,
|
|
269
|
+
// // this.validatorPrimary,
|
|
270
|
+
// // this.validatorFallback
|
|
271
|
+
// new ZodAdapter(),
|
|
272
|
+
// new ExpressValidatorAdapter()
|
|
273
|
+
// );
|
|
274
|
+
|
|
275
|
+
// this.sanitizerManager = new SanitizerManager(
|
|
276
|
+
// this.sanitizerPrimary,
|
|
277
|
+
// this.sanitizerFallback
|
|
278
|
+
// );
|
|
279
|
+
// }
|
|
280
|
+
|
|
281
|
+
// private setupDynamicManagers(): void {
|
|
282
|
+
// this.jsonManager = new JsonManager();
|
|
283
|
+
// this.corsManager = new CorsManager();
|
|
284
|
+
|
|
285
|
+
// // Auth manager (only if enabled)
|
|
286
|
+
// if (this.config.auth.enabled) {
|
|
287
|
+
// const jwtSecret = process.env.JWT_SECRET || this.config.auth.jwtSecret;
|
|
288
|
+
// if (!jwtSecret) {
|
|
289
|
+
// throw new Error("JWT_SECRET environment variable or jwtSecret in config is required when auth.enabled=true");
|
|
290
|
+
// }
|
|
291
|
+
|
|
292
|
+
// this.authManager = new AuthManager({
|
|
293
|
+
// jwtSecret,
|
|
294
|
+
// jwtExpiresIn: this.config.auth.jwtExpiresIn,
|
|
295
|
+
// googleClientId: process.env.GOOGLE_CLIENT_ID || this.config.auth.googleClientId
|
|
296
|
+
// });
|
|
297
|
+
// }
|
|
298
|
+
// }
|
|
299
|
+
|
|
300
|
+
// private createMiddlewareChain(options: SecureOptions): any[] {
|
|
301
|
+
// const chain: any[] = [];
|
|
302
|
+
|
|
303
|
+
// // JSON parsing
|
|
304
|
+
// chain.push(this.jsonManager.middleware(this.config.json));
|
|
305
|
+
// chain.push(this.jsonManager.urlencoded(this.config.urlencoded));
|
|
306
|
+
|
|
307
|
+
// // Security headers
|
|
308
|
+
// if (this.config.enableHelmet) chain.push(helmet());
|
|
309
|
+
// if (this.config.enableHPP) chain.push(hpp());
|
|
310
|
+
|
|
311
|
+
// // Compression (check if compression config exists)
|
|
312
|
+
// if (this.config.enableCompression && this.config.compression) {
|
|
313
|
+
// chain.push(compression(this.config.compression));
|
|
314
|
+
// } else if (this.config.enableCompression) {
|
|
315
|
+
// chain.push(compression()); // Use defaults
|
|
316
|
+
// }
|
|
317
|
+
|
|
318
|
+
// // CORS
|
|
319
|
+
// if (this.config.enableCORS || options.cors) {
|
|
320
|
+
// const corsOptions = options.cors === true ? this.config.cors :
|
|
321
|
+
// (typeof options.cors === 'object' ? options.cors : this.config.cors);
|
|
322
|
+
// chain.push(this.corsManager.middleware(corsOptions));
|
|
323
|
+
// }
|
|
324
|
+
|
|
325
|
+
// // Sanitization
|
|
326
|
+
// if (this.config.enableSanitizer || options.sanitize) {
|
|
327
|
+
// const sanitizeOptions = options.sanitize === true ? undefined :
|
|
328
|
+
// (typeof options.sanitize === 'object' ? options.sanitize : undefined);
|
|
329
|
+
// chain.push(this.sanitizerManager.middleware(sanitizeOptions));
|
|
330
|
+
// }
|
|
331
|
+
|
|
332
|
+
// // Rate limiting
|
|
333
|
+
// if (this.config.enableRateLimiter || options.rateLimit) {
|
|
334
|
+
// const rateLimitOpts = typeof options.rateLimit === 'object' ?
|
|
335
|
+
// { options: options.rateLimit } : {};
|
|
336
|
+
// chain.push(this.rateLimitManager.middleware(rateLimitOpts));
|
|
337
|
+
// }
|
|
338
|
+
|
|
339
|
+
// // Authentication
|
|
340
|
+
// if (options.auth && this.authManager) {
|
|
341
|
+
// const authOpts = options.auth === true ? undefined :
|
|
342
|
+
// (typeof options.auth === 'object' ? options.auth : undefined);
|
|
343
|
+
// chain.push(this.authManager.protect(authOpts));
|
|
344
|
+
// }
|
|
345
|
+
|
|
346
|
+
// // Error handler - at last usage
|
|
347
|
+
// chain.push(errorHandler);
|
|
348
|
+
|
|
349
|
+
// return chain;
|
|
350
|
+
// }
|
|
351
|
+
// }
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
|
|
1
360
|
import { HiSecureConfig } from "./types/HiSecureConfig.js";
|
|
2
361
|
import { defaultConfig } from "./config.js";
|
|
3
362
|
import { LIB_NAME, LIB_VERSION } from "./constants.js";
|
|
4
363
|
import { deepMerge } from "../utils/deepMerge.js";
|
|
5
364
|
import { deepFreeze } from "../utils/deepFreeze.js";
|
|
6
|
-
import { logger } from "../logging
|
|
365
|
+
import { logger } from "../logging";
|
|
7
366
|
|
|
8
367
|
// Adapters
|
|
9
368
|
import { ArgonAdapter } from "../adapters/ArgonAdapter.js";
|
|
@@ -31,7 +390,7 @@ import compression from "compression";
|
|
|
31
390
|
import { errorHandler } from "../middlewares/errorHandler.js";
|
|
32
391
|
|
|
33
392
|
// Types
|
|
34
|
-
import { SecureOptions, ValidationSchema} from "./types/SecureOptions.js";
|
|
393
|
+
import { SecureOptions, ValidationSchema } from "./types/SecureOptions.js";
|
|
35
394
|
|
|
36
395
|
export class HiSecure {
|
|
37
396
|
private static instance: HiSecure | null = null;
|
|
@@ -55,15 +414,19 @@ export class HiSecure {
|
|
|
55
414
|
private sanitizerPrimary: any;
|
|
56
415
|
private sanitizerFallback: any;
|
|
57
416
|
|
|
58
|
-
// Private constructor for singleton
|
|
59
417
|
private constructor(userConfig: Partial<HiSecureConfig> = {}) {
|
|
60
418
|
this.config = deepMerge(defaultConfig, userConfig);
|
|
61
419
|
}
|
|
62
420
|
|
|
63
|
-
//
|
|
64
|
-
|
|
421
|
+
// =========================
|
|
422
|
+
// Singleton & Init
|
|
423
|
+
// =========================
|
|
424
|
+
|
|
65
425
|
static getInstance(config?: Partial<HiSecureConfig>): HiSecure {
|
|
66
426
|
if (!HiSecure.instance) {
|
|
427
|
+
logger.info("Creating HiSecure singleton", {
|
|
428
|
+
layer: "hisecure-core"
|
|
429
|
+
});
|
|
67
430
|
HiSecure.instance = new HiSecure(config);
|
|
68
431
|
HiSecure.instance.init();
|
|
69
432
|
}
|
|
@@ -76,36 +439,34 @@ export class HiSecure {
|
|
|
76
439
|
|
|
77
440
|
init(): void {
|
|
78
441
|
if (this.initialized) {
|
|
79
|
-
logger.warn("
|
|
442
|
+
logger.warn("Initialization skipped (already initialized)", {
|
|
443
|
+
layer: "hisecure-core"
|
|
444
|
+
});
|
|
80
445
|
return;
|
|
81
446
|
}
|
|
82
447
|
|
|
83
|
-
logger.info(
|
|
448
|
+
logger.info("Framework initialization started", {
|
|
449
|
+
layer: "hisecure-core",
|
|
450
|
+
lib: LIB_NAME,
|
|
451
|
+
version: LIB_VERSION
|
|
452
|
+
});
|
|
84
453
|
|
|
85
454
|
this.setupAdapters();
|
|
86
455
|
this.setupManagers();
|
|
87
456
|
this.setupDynamicManagers();
|
|
88
457
|
|
|
89
458
|
deepFreeze(this.config);
|
|
90
|
-
// deep Freeze - for now we remove from manager it needs to manage the adapters
|
|
91
|
-
// deepFreeze(this.hashManager);
|
|
92
|
-
// deepFreeze(this.rateLimitManager);
|
|
93
|
-
// deepFreeze(this.validatorManager);
|
|
94
|
-
// deepFreeze(this.sanitizerManager);
|
|
95
|
-
// deepFreeze(this.jsonManager);
|
|
96
|
-
// deepFreeze(this.corsManager);
|
|
97
|
-
// if (this.authManager) deepFreeze(this.authManager);
|
|
98
|
-
|
|
99
459
|
this.initialized = true;
|
|
100
|
-
logger.info("HiSecure initialized successfully");
|
|
101
|
-
}
|
|
102
460
|
|
|
103
|
-
|
|
104
|
-
|
|
461
|
+
logger.info("Framework initialized successfully", {
|
|
462
|
+
layer: "hisecure-core"
|
|
463
|
+
});
|
|
105
464
|
}
|
|
106
465
|
|
|
107
|
-
//
|
|
108
|
-
|
|
466
|
+
// =========================
|
|
467
|
+
// Public Fluent APIs
|
|
468
|
+
// =========================
|
|
469
|
+
|
|
109
470
|
static auth(options?: { required?: boolean; roles?: string[] }) {
|
|
110
471
|
const instance = this.getInstance();
|
|
111
472
|
if (!instance.authManager) {
|
|
@@ -124,16 +485,21 @@ export class HiSecure {
|
|
|
124
485
|
|
|
125
486
|
static rateLimit(preset: "strict" | "relaxed" | "api" | object) {
|
|
126
487
|
const instance = this.getInstance();
|
|
127
|
-
|
|
488
|
+
|
|
128
489
|
if (typeof preset === "string") {
|
|
129
|
-
|
|
490
|
+
logger.info("Rate limit preset applied", {
|
|
491
|
+
layer: "hisecure-core",
|
|
492
|
+
preset
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
const presets: any = {
|
|
130
496
|
strict: { mode: "strict" },
|
|
131
497
|
relaxed: { mode: "relaxed" },
|
|
132
|
-
api: { mode: "api"
|
|
498
|
+
api: { mode: "api" }
|
|
133
499
|
};
|
|
134
|
-
return instance.rateLimitManager.middleware(presets[preset]
|
|
500
|
+
return instance.rateLimitManager.middleware(presets[preset]);
|
|
135
501
|
}
|
|
136
|
-
|
|
502
|
+
|
|
137
503
|
return instance.rateLimitManager.middleware({ options: preset });
|
|
138
504
|
}
|
|
139
505
|
|
|
@@ -143,115 +509,111 @@ export class HiSecure {
|
|
|
143
509
|
|
|
144
510
|
static json(options?: any) {
|
|
145
511
|
const instance = this.getInstance();
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
512
|
+
return [
|
|
513
|
+
instance.jsonManager.middleware(options),
|
|
514
|
+
instance.jsonManager.urlencoded()
|
|
515
|
+
];
|
|
150
516
|
}
|
|
151
517
|
|
|
152
|
-
//
|
|
153
|
-
|
|
154
|
-
|
|
518
|
+
// =========================
|
|
519
|
+
// Utilities
|
|
520
|
+
// =========================
|
|
521
|
+
|
|
522
|
+
static async hash(value: string): Promise<string> {
|
|
155
523
|
const instance = this.getInstance();
|
|
156
|
-
const result = await instance.hashManager.hash(
|
|
524
|
+
const result = await instance.hashManager.hash(value, { allowFallback: true });
|
|
157
525
|
return result.hash;
|
|
158
526
|
}
|
|
159
527
|
|
|
160
|
-
static async verify(
|
|
161
|
-
return this.getInstance().hashManager.verify(
|
|
528
|
+
static async verify(value: string, hash: string): Promise<boolean> {
|
|
529
|
+
return this.getInstance().hashManager.verify(value, hash);
|
|
162
530
|
}
|
|
163
531
|
|
|
164
532
|
static jwt = {
|
|
165
|
-
sign: (payload: object, options?: any) =>
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
},
|
|
172
|
-
|
|
173
|
-
verify: (token: string) => {
|
|
174
|
-
const instance = HiSecure.getInstance();
|
|
175
|
-
if (!instance.authManager) {
|
|
176
|
-
throw new Error("Auth not enabled");
|
|
177
|
-
}
|
|
178
|
-
return instance.authManager.verify(token);
|
|
179
|
-
},
|
|
180
|
-
|
|
533
|
+
sign: (payload: object, options?: any) =>
|
|
534
|
+
HiSecure.getInstance().authManager!.sign(payload, options),
|
|
535
|
+
|
|
536
|
+
verify: (token: string) =>
|
|
537
|
+
HiSecure.getInstance().authManager!.verify(token),
|
|
538
|
+
|
|
181
539
|
google: {
|
|
182
|
-
verifyIdToken: (idToken: string) =>
|
|
183
|
-
|
|
184
|
-
if (!instance.authManager) {
|
|
185
|
-
throw new Error("Auth not enabled");
|
|
186
|
-
}
|
|
187
|
-
return instance.authManager.verifyGoogleIdToken(idToken);
|
|
188
|
-
}
|
|
540
|
+
verifyIdToken: (idToken: string) =>
|
|
541
|
+
HiSecure.getInstance().authManager!.verifyGoogleIdToken(idToken)
|
|
189
542
|
}
|
|
190
543
|
};
|
|
191
544
|
|
|
192
|
-
//
|
|
193
|
-
|
|
545
|
+
// =========================
|
|
546
|
+
// Global Middleware
|
|
547
|
+
// =========================
|
|
548
|
+
|
|
194
549
|
static middleware(options?: SecureOptions | "api" | "strict" | "public") {
|
|
195
550
|
const instance = this.getInstance();
|
|
196
|
-
|
|
197
|
-
// Handle preset strings
|
|
551
|
+
|
|
198
552
|
if (typeof options === "string") {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
553
|
+
logger.info("Global middleware preset applied", {
|
|
554
|
+
layer: "hisecure-core",
|
|
555
|
+
preset: options
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
const presets: any = {
|
|
559
|
+
api: { cors: true, rateLimit: "relaxed", sanitize: true },
|
|
560
|
+
strict: { cors: true, rateLimit: "strict", sanitize: true, auth: true },
|
|
561
|
+
public: { cors: true, rateLimit: true, sanitize: false }
|
|
203
562
|
};
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
return instance.createMiddlewareChain(presetOptions);
|
|
207
|
-
}
|
|
208
|
-
return instance.createMiddlewareChain({});
|
|
563
|
+
|
|
564
|
+
return instance.createMiddlewareChain(presets[options] || {});
|
|
209
565
|
}
|
|
210
|
-
|
|
566
|
+
|
|
211
567
|
return instance.createMiddlewareChain(options || {});
|
|
212
568
|
}
|
|
213
569
|
|
|
214
|
-
|
|
215
|
-
// Internal
|
|
216
|
-
|
|
217
|
-
private setupAdapters(): void {
|
|
218
|
-
logger.info(" Setting up adapters...");
|
|
570
|
+
// =========================
|
|
571
|
+
// Internal Setup
|
|
572
|
+
// =========================
|
|
219
573
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
574
|
+
private setupAdapters() {
|
|
575
|
+
logger.info("Adapters setup started", {
|
|
576
|
+
layer: "hisecure-core"
|
|
577
|
+
});
|
|
224
578
|
|
|
225
|
-
this.
|
|
226
|
-
|
|
227
|
-
|
|
579
|
+
this.hashingPrimary =
|
|
580
|
+
this.config.hashing.primary === "argon2"
|
|
581
|
+
? new ArgonAdapter()
|
|
582
|
+
: new BcryptAdapter(this.config.hashing.saltRounds);
|
|
583
|
+
|
|
584
|
+
this.hashingFallback =
|
|
585
|
+
this.config.hashing.fallback === "bcrypt"
|
|
586
|
+
? new BcryptAdapter(this.config.hashing.saltRounds)
|
|
587
|
+
: null;
|
|
588
|
+
|
|
589
|
+
logger.info("Hashing adapters configured", {
|
|
590
|
+
layer: "hisecure-core",
|
|
591
|
+
primary: this.config.hashing.primary,
|
|
592
|
+
fallback: this.config.hashing.fallback ?? null
|
|
593
|
+
});
|
|
228
594
|
|
|
229
|
-
// Rate limiting
|
|
230
595
|
this.rateLimiterPrimary = this.config.rateLimiter.useAdaptiveMode
|
|
231
596
|
? new RLFlexibleAdapter()
|
|
232
597
|
: new ExpressRLAdapter();
|
|
233
|
-
this.rateLimiterFallback = new ExpressRLAdapter();
|
|
234
598
|
|
|
599
|
+
this.rateLimiterFallback = new ExpressRLAdapter();
|
|
235
600
|
|
|
601
|
+
logger.info("Rate limiter adapters configured", {
|
|
602
|
+
layer: "hisecure-core",
|
|
603
|
+
adaptive: this.config.rateLimiter.useAdaptiveMode
|
|
604
|
+
});
|
|
236
605
|
|
|
237
|
-
|
|
238
|
-
// // Validation - we handle this in d/f way for now
|
|
239
|
-
// this.validatorPrimary = this.config.validation.mode === "zod"
|
|
240
|
-
// ? new ZodAdapter()
|
|
241
|
-
// : new ExpressValidatorAdapter();
|
|
242
|
-
// this.validatorFallback = this.config.validation.fallback === "express-validator"
|
|
243
|
-
// ? new ExpressValidatorAdapter()
|
|
244
|
-
// : null;
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
// Sanitization
|
|
248
606
|
this.sanitizerPrimary = new SanitizeHtmlAdapter(this.config.sanitizer);
|
|
249
607
|
this.sanitizerFallback = new XSSAdapter(this.config.sanitizer);
|
|
250
608
|
|
|
251
|
-
logger.info("
|
|
609
|
+
logger.info("Sanitizer adapters configured", {
|
|
610
|
+
layer: "hisecure-core",
|
|
611
|
+
primary: "sanitize-html",
|
|
612
|
+
fallback: "xss"
|
|
613
|
+
});
|
|
252
614
|
}
|
|
253
615
|
|
|
254
|
-
private setupManagers()
|
|
616
|
+
private setupManagers() {
|
|
255
617
|
this.hashManager = new HashManager(
|
|
256
618
|
this.config.hashing,
|
|
257
619
|
this.hashingPrimary,
|
|
@@ -265,9 +627,6 @@ export class HiSecure {
|
|
|
265
627
|
);
|
|
266
628
|
|
|
267
629
|
this.validatorManager = new ValidatorManager(
|
|
268
|
-
// this.config.validation,
|
|
269
|
-
// this.validatorPrimary,
|
|
270
|
-
// this.validatorFallback
|
|
271
630
|
new ZodAdapter(),
|
|
272
631
|
new ExpressValidatorAdapter()
|
|
273
632
|
);
|
|
@@ -276,84 +635,59 @@ export class HiSecure {
|
|
|
276
635
|
this.sanitizerPrimary,
|
|
277
636
|
this.sanitizerFallback
|
|
278
637
|
);
|
|
638
|
+
|
|
639
|
+
logger.info("Core managers initialized", {
|
|
640
|
+
layer: "hisecure-core",
|
|
641
|
+
managers: ["hash", "rate-limit", "validator", "sanitizer"]
|
|
642
|
+
});
|
|
279
643
|
}
|
|
280
644
|
|
|
281
|
-
private setupDynamicManagers()
|
|
645
|
+
private setupDynamicManagers() {
|
|
282
646
|
this.jsonManager = new JsonManager();
|
|
283
647
|
this.corsManager = new CorsManager();
|
|
284
648
|
|
|
285
|
-
// Auth manager (only if enabled)
|
|
286
649
|
if (this.config.auth.enabled) {
|
|
287
|
-
const jwtSecret = process.env.JWT_SECRET || this.config.auth.jwtSecret;
|
|
288
|
-
if (!jwtSecret) {
|
|
289
|
-
throw new Error("JWT_SECRET environment variable or jwtSecret in config is required when auth.enabled=true");
|
|
290
|
-
}
|
|
291
|
-
|
|
292
650
|
this.authManager = new AuthManager({
|
|
293
|
-
jwtSecret
|
|
651
|
+
jwtSecret: process.env.JWT_SECRET || this.config.auth.jwtSecret!,
|
|
294
652
|
jwtExpiresIn: this.config.auth.jwtExpiresIn,
|
|
295
|
-
googleClientId:
|
|
653
|
+
googleClientId:
|
|
654
|
+
process.env.GOOGLE_CLIENT_ID || this.config.auth.googleClientId
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
logger.info("Authentication enabled", {
|
|
658
|
+
layer: "hisecure-core",
|
|
659
|
+
google: !!this.config.auth.googleClientId
|
|
660
|
+
});
|
|
661
|
+
} else {
|
|
662
|
+
logger.info("Authentication disabled", {
|
|
663
|
+
layer: "hisecure-core"
|
|
296
664
|
});
|
|
297
665
|
}
|
|
298
666
|
}
|
|
299
667
|
|
|
300
668
|
private createMiddlewareChain(options: SecureOptions): any[] {
|
|
301
669
|
const chain: any[] = [];
|
|
302
|
-
|
|
303
|
-
// JSON parsing
|
|
670
|
+
|
|
304
671
|
chain.push(this.jsonManager.middleware(this.config.json));
|
|
305
672
|
chain.push(this.jsonManager.urlencoded(this.config.urlencoded));
|
|
306
|
-
|
|
307
|
-
// Security headers
|
|
673
|
+
|
|
308
674
|
if (this.config.enableHelmet) chain.push(helmet());
|
|
309
675
|
if (this.config.enableHPP) chain.push(hpp());
|
|
310
|
-
|
|
311
|
-
// Compression (check if compression config exists)
|
|
312
|
-
if (this.config.enableCompression && this.config.compression) {
|
|
313
|
-
chain.push(compression(this.config.compression));
|
|
314
|
-
} else if (this.config.enableCompression) {
|
|
315
|
-
chain.push(compression()); // Use defaults
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
// CORS
|
|
319
|
-
if (this.config.enableCORS || options.cors) {
|
|
320
|
-
const corsOptions = options.cors === true ? this.config.cors :
|
|
321
|
-
(typeof options.cors === 'object' ? options.cors : this.config.cors);
|
|
322
|
-
chain.push(this.corsManager.middleware(corsOptions));
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
// Sanitization
|
|
326
|
-
if (this.config.enableSanitizer || options.sanitize) {
|
|
327
|
-
const sanitizeOptions = options.sanitize === true ? undefined :
|
|
328
|
-
(typeof options.sanitize === 'object' ? options.sanitize : undefined);
|
|
329
|
-
chain.push(this.sanitizerManager.middleware(sanitizeOptions));
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
// Rate limiting
|
|
333
|
-
if (this.config.enableRateLimiter || options.rateLimit) {
|
|
334
|
-
const rateLimitOpts = typeof options.rateLimit === 'object' ?
|
|
335
|
-
{ options: options.rateLimit } : {};
|
|
336
|
-
chain.push(this.rateLimitManager.middleware(rateLimitOpts));
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// Authentication
|
|
340
|
-
if (options.auth && this.authManager) {
|
|
341
|
-
const authOpts = options.auth === true ? undefined :
|
|
342
|
-
(typeof options.auth === 'object' ? options.auth : undefined);
|
|
343
|
-
chain.push(this.authManager.protect(authOpts));
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
// Error handler - at last usage
|
|
347
|
-
chain.push(errorHandler);
|
|
348
|
-
|
|
349
|
-
return chain;
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
676
|
|
|
677
|
+
if (this.config.enableCompression)
|
|
678
|
+
chain.push(compression(this.config.compression));
|
|
356
679
|
|
|
680
|
+
if (options.cors) chain.push(this.corsManager.middleware(this.config.cors));
|
|
681
|
+
if (options.sanitize)
|
|
682
|
+
chain.push(this.sanitizerManager.middleware());
|
|
357
683
|
|
|
684
|
+
if (options.rateLimit)
|
|
685
|
+
chain.push(this.rateLimitManager.middleware({}));
|
|
358
686
|
|
|
687
|
+
if (options.auth && this.authManager)
|
|
688
|
+
chain.push(this.authManager.protect());
|
|
359
689
|
|
|
690
|
+
chain.push(errorHandler);
|
|
691
|
+
return chain;
|
|
692
|
+
}
|
|
693
|
+
}
|