hi-secure 1.0.12 → 1.0.14
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.js +2 -2
- package/dist/adapters/ArgonAdapter.js.map +1 -1
- package/dist/adapters/BcryptAdapter.js +2 -2
- package/dist/adapters/BcryptAdapter.js.map +1 -1
- package/dist/adapters/ExpressRLAdapter.js +2 -2
- package/dist/adapters/ExpressRLAdapter.js.map +1 -1
- package/dist/adapters/ExpressValidatorAdapter.js +1 -1
- package/dist/adapters/ExpressValidatorAdapter.js.map +1 -1
- package/dist/adapters/GoogleAdapter.js +3 -3
- package/dist/adapters/GoogleAdapter.js.map +1 -1
- package/dist/adapters/JWTAdapter.js +2 -2
- package/dist/adapters/JWTAdapter.js.map +1 -1
- package/dist/adapters/RLFlexibleAdapter.js +2 -2
- package/dist/adapters/RLFlexibleAdapter.js.map +1 -1
- package/dist/adapters/SanitizeHtmlAdapter.js +3 -3
- package/dist/adapters/SanitizeHtmlAdapter.js.map +1 -1
- package/dist/adapters/XSSAdapter.js +3 -3
- package/dist/adapters/XSSAdapter.js.map +1 -1
- package/dist/adapters/ZodAdapter.js +1 -1
- package/dist/adapters/ZodAdapter.js.map +1 -1
- package/dist/core/HiSecure.d.ts.map +1 -1
- package/dist/core/HiSecure.js +9 -8
- package/dist/core/HiSecure.js.map +1 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +0 -7
- package/dist/core/config.js.map +1 -1
- package/dist/core/constants.d.ts.map +1 -1
- package/dist/core/constants.js +0 -27
- package/dist/core/constants.js.map +1 -1
- package/dist/core/errors/AdapterError.js +1 -1
- package/dist/core/errors/AdapterError.js.map +1 -1
- package/dist/core/types/HiSecureConfig.d.ts.map +1 -1
- package/dist/core/types/HiSecureConfig.js.map +1 -1
- package/dist/core/types/SecureOptions.d.ts.map +1 -1
- package/dist/core/types/SecureOptions.js +0 -1
- package/dist/core/types/SecureOptions.js.map +1 -1
- package/dist/core/useSecure.d.ts.map +1 -1
- package/dist/core/useSecure.js +1 -1
- package/dist/core/useSecure.js.map +1 -1
- package/dist/managers/AuthManager.js +2 -2
- package/dist/managers/AuthManager.js.map +1 -1
- package/dist/managers/CorsManager.d.ts.map +1 -1
- package/dist/managers/CorsManager.js +2 -2
- package/dist/managers/CorsManager.js.map +1 -1
- package/dist/managers/HashManager.js +7 -7
- package/dist/managers/HashManager.js.map +1 -1
- package/dist/managers/JsonManager.js +4 -4
- package/dist/managers/JsonManager.js.map +1 -1
- package/dist/managers/RateLimitManager.js +5 -5
- package/dist/managers/RateLimitManager.js.map +1 -1
- package/dist/managers/SanitizerManager.js +5 -5
- package/dist/managers/SanitizerManager.js.map +1 -1
- package/dist/managers/ValidatorManager.js +2 -2
- package/dist/managers/ValidatorManager.js.map +1 -1
- package/dist/middlewares/errorHandler.js +3 -3
- package/dist/middlewares/errorHandler.js.map +1 -1
- package/package.json +1 -1
- package/readme.md +566 -388
- package/src/adapters/ArgonAdapter.ts +2 -2
- package/src/adapters/BcryptAdapter.ts +2 -2
- package/src/adapters/ExpressRLAdapter.ts +2 -2
- package/src/adapters/ExpressValidatorAdapter.ts +1 -1
- package/src/adapters/GoogleAdapter.ts +3 -3
- package/src/adapters/JWTAdapter.ts +2 -2
- package/src/adapters/RLFlexibleAdapter.ts +2 -2
- package/src/adapters/SanitizeHtmlAdapter.ts +3 -3
- package/src/adapters/XSSAdapter.ts +3 -3
- package/src/adapters/ZodAdapter.ts +1 -1
- package/src/core/HiSecure.ts +10 -8
- package/src/core/config.ts +0 -105
- package/src/core/constants.ts +0 -33
- package/src/core/errors/AdapterError.ts +1 -1
- package/src/core/types/HiSecureConfig.ts +0 -1
- package/src/core/types/SecureOptions.ts +0 -27
- package/src/core/useSecure.ts +1 -3
- package/src/managers/AuthManager.ts +2 -2
- package/src/managers/CorsManager.ts +2 -2
- package/src/managers/HashManager.ts +7 -7
- package/src/managers/JsonManager.ts +4 -4
- package/src/managers/RateLimitManager.ts +5 -5
- package/src/managers/SanitizerManager.ts +5 -5
- package/src/managers/ValidatorManager.ts +2 -2
- package/src/middlewares/errorHandler.ts +3 -3
|
@@ -17,7 +17,7 @@ export class ArgonAdapter {
|
|
|
17
17
|
? await argon2.hash(value, this.options)
|
|
18
18
|
: await argon2.hash(value);
|
|
19
19
|
} catch (err: any) {
|
|
20
|
-
logger.error("
|
|
20
|
+
logger.error("Argon2 hashing failed", {
|
|
21
21
|
error: err?.message || err
|
|
22
22
|
});
|
|
23
23
|
throw new AdapterError("Argon2 hashing failed.");
|
|
@@ -32,7 +32,7 @@ export class ArgonAdapter {
|
|
|
32
32
|
|
|
33
33
|
return await argon2.verify(hashed, value);
|
|
34
34
|
} catch (err: any) {
|
|
35
|
-
logger.error("
|
|
35
|
+
logger.error("Argon2 verify failed", {
|
|
36
36
|
error: err?.message || err
|
|
37
37
|
});
|
|
38
38
|
throw new AdapterError("Argon2 verify failed.");
|
|
@@ -13,7 +13,7 @@ export class BcryptAdapter {
|
|
|
13
13
|
|
|
14
14
|
return await bcrypt.hash(value, this.saltRounds);
|
|
15
15
|
} catch (err: any) {
|
|
16
|
-
logger.error("
|
|
16
|
+
logger.error("Bcrypt hashing failed", {
|
|
17
17
|
error: err?.message || err,
|
|
18
18
|
saltRounds: this.saltRounds
|
|
19
19
|
});
|
|
@@ -34,7 +34,7 @@ export class BcryptAdapter {
|
|
|
34
34
|
|
|
35
35
|
return await bcrypt.compare(value, hashed);
|
|
36
36
|
} catch (err: any) {
|
|
37
|
-
logger.error("
|
|
37
|
+
logger.error("Bcrypt verify failed", {
|
|
38
38
|
error: err?.message || err
|
|
39
39
|
});
|
|
40
40
|
|
|
@@ -28,7 +28,7 @@ export class ExpressRLAdapter {
|
|
|
28
28
|
|
|
29
29
|
const limiter = rateLimit(finalOptions);
|
|
30
30
|
|
|
31
|
-
logger.debug("
|
|
31
|
+
logger.debug("Express rate limiter configured", {
|
|
32
32
|
windowMs: finalOptions.windowMs,
|
|
33
33
|
max: finalOptions.max
|
|
34
34
|
});
|
|
@@ -36,7 +36,7 @@ export class ExpressRLAdapter {
|
|
|
36
36
|
return limiter;
|
|
37
37
|
|
|
38
38
|
} catch (err: any) {
|
|
39
|
-
logger.error("
|
|
39
|
+
logger.error("ExpressRLAdapter: failed to create limiter", {
|
|
40
40
|
error: err?.message || err
|
|
41
41
|
});
|
|
42
42
|
throw new AdapterError("Express rate limiter creation failed.");
|
|
@@ -35,7 +35,7 @@ export class GoogleAdapter {
|
|
|
35
35
|
idToken
|
|
36
36
|
};
|
|
37
37
|
|
|
38
|
-
//
|
|
38
|
+
// audience only if clientId is provided and not empty
|
|
39
39
|
if (this.clientId && this.clientId.trim().length > 0) {
|
|
40
40
|
options.audience = this.clientId;
|
|
41
41
|
}
|
|
@@ -48,7 +48,7 @@ export class GoogleAdapter {
|
|
|
48
48
|
throw new AdapterError("Invalid Google ID token payload.");
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
//
|
|
51
|
+
// result object
|
|
52
52
|
const result: GoogleTokenPayload = {
|
|
53
53
|
sub: payload.sub,
|
|
54
54
|
email: payload.email || '',
|
|
@@ -57,7 +57,7 @@ export class GoogleAdapter {
|
|
|
57
57
|
picture: payload.picture
|
|
58
58
|
};
|
|
59
59
|
|
|
60
|
-
//
|
|
60
|
+
// remaining properties from payload
|
|
61
61
|
const { sub, email, email_verified, name, picture, ...rest } = payload;
|
|
62
62
|
Object.assign(result, rest);
|
|
63
63
|
|
|
@@ -33,8 +33,8 @@ export class JWTAdapter {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
if (options.secret.length < 32) {
|
|
36
|
-
logger.warn("
|
|
37
|
-
// logError("
|
|
36
|
+
logger.warn("JWT secret shorter than 32 chars. Consider using stronger secret.");
|
|
37
|
+
// logError("JWT secret is too short (minimum 32 characters recommended)");
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
this.secret = options.secret;
|
|
@@ -37,7 +37,7 @@ export class RLFlexibleAdapter {
|
|
|
37
37
|
} catch (err: any) {
|
|
38
38
|
const rlErr = err as RateLimiterRes;
|
|
39
39
|
|
|
40
|
-
logger.warn("
|
|
40
|
+
logger.warn("RLFlexibleAdapter: rate limit exceeded", {
|
|
41
41
|
ip,
|
|
42
42
|
path: req.path,
|
|
43
43
|
method: req.method,
|
|
@@ -56,7 +56,7 @@ export class RLFlexibleAdapter {
|
|
|
56
56
|
};
|
|
57
57
|
|
|
58
58
|
} catch (err: any) {
|
|
59
|
-
logger.error("
|
|
59
|
+
logger.error("RLFlexibleAdapter: failed to initialize limiter", {
|
|
60
60
|
error: err?.message || err
|
|
61
61
|
});
|
|
62
62
|
throw new AdapterError("RateLimiterFlexible creation failed.");
|
|
@@ -17,7 +17,7 @@ export class SanitizeHtmlAdapter {
|
|
|
17
17
|
return typeof clean === "string" ? clean : String(clean);
|
|
18
18
|
|
|
19
19
|
} catch (err: any) {
|
|
20
|
-
logger.error("
|
|
20
|
+
logger.error("sanitize-html failed", {
|
|
21
21
|
error: err?.message || err,
|
|
22
22
|
preview: typeof input === "string" ? input.slice(0, 100) : undefined
|
|
23
23
|
});
|
|
@@ -61,14 +61,14 @@ export class SanitizeHtmlAdapter {
|
|
|
61
61
|
if (req.body) {
|
|
62
62
|
req.body = this.deepSanitize(req.body, dynamicOptions);
|
|
63
63
|
|
|
64
|
-
logger.debug("
|
|
64
|
+
logger.debug("sanitize-html applied", {
|
|
65
65
|
keys: Object.keys(req.body)
|
|
66
66
|
});
|
|
67
67
|
}
|
|
68
68
|
next();
|
|
69
69
|
|
|
70
70
|
} catch (err: any) {
|
|
71
|
-
logger.error("
|
|
71
|
+
logger.error("sanitize-html middleware failed", {
|
|
72
72
|
error: err?.message || err
|
|
73
73
|
});
|
|
74
74
|
next(err);
|
|
@@ -60,7 +60,7 @@ export class XSSAdapter {
|
|
|
60
60
|
return customFilter.process(input);
|
|
61
61
|
|
|
62
62
|
} catch (err: any) {
|
|
63
|
-
logger.error("
|
|
63
|
+
logger.error("XSS sanitizer failed", {
|
|
64
64
|
error: err?.message,
|
|
65
65
|
preview: input?.slice?.(0, 80)
|
|
66
66
|
});
|
|
@@ -98,7 +98,7 @@ export class XSSAdapter {
|
|
|
98
98
|
|
|
99
99
|
req.sanitizedBody = sanitizedBody;
|
|
100
100
|
|
|
101
|
-
logger.debug("
|
|
101
|
+
logger.debug("XSS sanitizer applied", {
|
|
102
102
|
originalKeys: Object.keys(originalBody),
|
|
103
103
|
sanitizedKeys: Object.keys(sanitizedBody)
|
|
104
104
|
});
|
|
@@ -106,7 +106,7 @@ export class XSSAdapter {
|
|
|
106
106
|
|
|
107
107
|
next();
|
|
108
108
|
} catch (err: any) {
|
|
109
|
-
logger.error("
|
|
109
|
+
logger.error("XSS middleware failed", {
|
|
110
110
|
error: err?.message || err
|
|
111
111
|
});
|
|
112
112
|
next(err);
|
package/src/core/HiSecure.ts
CHANGED
|
@@ -76,17 +76,18 @@ export class HiSecure {
|
|
|
76
76
|
|
|
77
77
|
init(): void {
|
|
78
78
|
if (this.initialized) {
|
|
79
|
-
logger.warn("
|
|
79
|
+
logger.warn(" HiSecure already initialized");
|
|
80
80
|
return;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
logger.info(
|
|
83
|
+
logger.info(` ${LIB_NAME} v${LIB_VERSION} initializing...`);
|
|
84
84
|
|
|
85
85
|
this.setupAdapters();
|
|
86
86
|
this.setupManagers();
|
|
87
87
|
this.setupDynamicManagers();
|
|
88
88
|
|
|
89
89
|
deepFreeze(this.config);
|
|
90
|
+
// deep Freeze - for now we remove from manager it needs to manage the adapters
|
|
90
91
|
// deepFreeze(this.hashManager);
|
|
91
92
|
// deepFreeze(this.rateLimitManager);
|
|
92
93
|
// deepFreeze(this.validatorManager);
|
|
@@ -96,7 +97,7 @@ export class HiSecure {
|
|
|
96
97
|
// if (this.authManager) deepFreeze(this.authManager);
|
|
97
98
|
|
|
98
99
|
this.initialized = true;
|
|
99
|
-
logger.info("
|
|
100
|
+
logger.info("HiSecure initialized successfully");
|
|
100
101
|
}
|
|
101
102
|
|
|
102
103
|
isInitialized(): boolean {
|
|
@@ -148,7 +149,7 @@ export class HiSecure {
|
|
|
148
149
|
return chain;
|
|
149
150
|
}
|
|
150
151
|
|
|
151
|
-
// UTILITY METHODS
|
|
152
|
+
// UTILITY METHODS - For direct use
|
|
152
153
|
|
|
153
154
|
static async hash(password: string): Promise<string> {
|
|
154
155
|
const instance = this.getInstance();
|
|
@@ -214,7 +215,7 @@ export class HiSecure {
|
|
|
214
215
|
// Internal Methods
|
|
215
216
|
|
|
216
217
|
private setupAdapters(): void {
|
|
217
|
-
logger.info("
|
|
218
|
+
logger.info(" Setting up adapters...");
|
|
218
219
|
|
|
219
220
|
// Hashing
|
|
220
221
|
this.hashingPrimary = this.config.hashing.primary === "argon2"
|
|
@@ -233,7 +234,8 @@ export class HiSecure {
|
|
|
233
234
|
|
|
234
235
|
|
|
235
236
|
|
|
236
|
-
|
|
237
|
+
|
|
238
|
+
// // Validation - we handle this in d/f way for now
|
|
237
239
|
// this.validatorPrimary = this.config.validation.mode === "zod"
|
|
238
240
|
// ? new ZodAdapter()
|
|
239
241
|
// : new ExpressValidatorAdapter();
|
|
@@ -246,7 +248,7 @@ export class HiSecure {
|
|
|
246
248
|
this.sanitizerPrimary = new SanitizeHtmlAdapter(this.config.sanitizer);
|
|
247
249
|
this.sanitizerFallback = new XSSAdapter(this.config.sanitizer);
|
|
248
250
|
|
|
249
|
-
logger.info("
|
|
251
|
+
logger.info("Adapters ready");
|
|
250
252
|
}
|
|
251
253
|
|
|
252
254
|
private setupManagers(): void {
|
|
@@ -341,7 +343,7 @@ export class HiSecure {
|
|
|
341
343
|
chain.push(this.authManager.protect(authOpts));
|
|
342
344
|
}
|
|
343
345
|
|
|
344
|
-
// Error handler
|
|
346
|
+
// Error handler - at last usage
|
|
345
347
|
chain.push(errorHandler);
|
|
346
348
|
|
|
347
349
|
return chain;
|
package/src/core/config.ts
CHANGED
|
@@ -1,108 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
// export interface HiSecureConfig {
|
|
4
|
-
// enableHelmet: boolean;
|
|
5
|
-
// enableHPP: boolean;
|
|
6
|
-
// enableCORS: boolean;
|
|
7
|
-
// enableSanitizer: boolean;
|
|
8
|
-
// enableRateLimiter: boolean;
|
|
9
|
-
// enableValidation: boolean;
|
|
10
|
-
|
|
11
|
-
// hashing: {
|
|
12
|
-
// primary: "argon2" | "bcrypt";
|
|
13
|
-
// fallback: "bcrypt" | null;
|
|
14
|
-
// saltRounds: number;
|
|
15
|
-
// };
|
|
16
|
-
|
|
17
|
-
// rateLimiter: {
|
|
18
|
-
// windowMs: number;
|
|
19
|
-
// maxRequests: number;
|
|
20
|
-
// message: string;
|
|
21
|
-
// useAdaptiveMode: boolean;
|
|
22
|
-
// };
|
|
23
|
-
|
|
24
|
-
// validation: {
|
|
25
|
-
// mode: "zod" | "express-validator";
|
|
26
|
-
// fallback: "express-validator" | null;
|
|
27
|
-
// };
|
|
28
|
-
|
|
29
|
-
// sanitizer: {
|
|
30
|
-
// allowedTags: string[];
|
|
31
|
-
// allowedAttributes: Record<string, string[]>;
|
|
32
|
-
// };
|
|
33
|
-
|
|
34
|
-
// logging: {
|
|
35
|
-
// enabled: boolean;
|
|
36
|
-
// level: "info" | "warn" | "error" | "debug";
|
|
37
|
-
// };
|
|
38
|
-
|
|
39
|
-
// /** 🔥 ADD THIS */
|
|
40
|
-
// auth: {
|
|
41
|
-
// enabled: boolean;
|
|
42
|
-
// jwtExpiresIn: string | number | undefined;
|
|
43
|
-
// };
|
|
44
|
-
|
|
45
|
-
// /** 🔥 optional configs for dynamic JSON/CORS */
|
|
46
|
-
// json?: any;
|
|
47
|
-
// urlencoded?: any;
|
|
48
|
-
// cors?: any;
|
|
49
|
-
// }
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
// export const defaultConfig: HiSecureConfig = {
|
|
55
|
-
// enableHelmet: true,
|
|
56
|
-
// enableHPP: true,
|
|
57
|
-
// enableCORS: true,
|
|
58
|
-
// enableSanitizer: true,
|
|
59
|
-
// enableRateLimiter: true,
|
|
60
|
-
// enableValidation: true,
|
|
61
|
-
|
|
62
|
-
// hashing: {
|
|
63
|
-
// primary: "argon2",
|
|
64
|
-
// fallback: "bcrypt",
|
|
65
|
-
// saltRounds: 10,
|
|
66
|
-
// },
|
|
67
|
-
|
|
68
|
-
// rateLimiter: {
|
|
69
|
-
// windowMs: 15 * 60 * 1000,
|
|
70
|
-
// maxRequests: 100,
|
|
71
|
-
// message: "Too many requests, please try again later.",
|
|
72
|
-
// useAdaptiveMode: false,
|
|
73
|
-
// },
|
|
74
|
-
|
|
75
|
-
// validation: {
|
|
76
|
-
// mode: "zod",
|
|
77
|
-
// fallback: "express-validator",
|
|
78
|
-
// },
|
|
79
|
-
|
|
80
|
-
// sanitizer: {
|
|
81
|
-
// allowedTags: ["b", "i", "em", "strong", "a"],
|
|
82
|
-
// allowedAttributes: { a: ["href"] },
|
|
83
|
-
// },
|
|
84
|
-
|
|
85
|
-
// logging: {
|
|
86
|
-
// enabled: true,
|
|
87
|
-
// level: "info",
|
|
88
|
-
// },
|
|
89
|
-
|
|
90
|
-
// /** 🔥 NEW AUTH CONFIG */
|
|
91
|
-
// auth: {
|
|
92
|
-
// enabled: false, // user enables manually
|
|
93
|
-
// jwtExpiresIn: "1d", // default value
|
|
94
|
-
// },
|
|
95
|
-
|
|
96
|
-
// /** Optional parser configs */
|
|
97
|
-
// json: {},
|
|
98
|
-
// urlencoded: { extended: true },
|
|
99
|
-
// cors: {},
|
|
100
|
-
// };
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
// src/core/config.ts
|
|
106
1
|
import { HiSecureConfig } from "./types/HiSecureConfig";
|
|
107
2
|
|
|
108
3
|
export const defaultConfig: HiSecureConfig = {
|
package/src/core/constants.ts
CHANGED
|
@@ -1,36 +1,3 @@
|
|
|
1
|
-
// export const LIB_NAME = "HiSecure";
|
|
2
|
-
// export const LIB_VERSION = "1.0.0";
|
|
3
|
-
|
|
4
|
-
// // Adapter Names
|
|
5
|
-
// export const ADAPTERS = {
|
|
6
|
-
// HASHING_PRIMARY: "HASHING_PRIMARY",
|
|
7
|
-
// HASHING_FALLBACK: "HASHING_FALLBACK",
|
|
8
|
-
// RATE_LIMITER: "RATE_LIMITER",
|
|
9
|
-
// VALIDATOR: "VALIDATOR",
|
|
10
|
-
// SANITIZER: "SANITIZER"
|
|
11
|
-
// } as const;
|
|
12
|
-
|
|
13
|
-
// // Manager Names
|
|
14
|
-
// export const MANAGERS = {
|
|
15
|
-
// HASH_MANAGER: "HASH_MANAGER",
|
|
16
|
-
// RATE_LIMIT_MANAGER: "RATE_LIMIT_MANAGER",
|
|
17
|
-
// VALIDATION_MANAGER: "VALIDATION_MANAGER",
|
|
18
|
-
// SANITIZER_MANAGER:"SANITIZER_MANAGER"
|
|
19
|
-
// } as const;
|
|
20
|
-
|
|
21
|
-
// // Error Codes
|
|
22
|
-
// export const ERROR_CODES = {
|
|
23
|
-
// ADAPTER_FAILURE: "ADAPTER_FAILURE",
|
|
24
|
-
// VALIDATION_ERROR: "VALIDATION_ERROR",
|
|
25
|
-
// RATE_LIMIT_EXCEEDED: "RATE_LIMIT_EXCEEDED",
|
|
26
|
-
// SANITIZER_ERROR:"SANITIZER_ERROR",
|
|
27
|
-
// CONFIG_ERROR: "CONFIG_ERROR",
|
|
28
|
-
// UNKNOWN: "UNKNOWN"
|
|
29
|
-
// } as const;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
// src/core/constants.ts
|
|
34
1
|
export const LIB_NAME = "HiSecure";
|
|
35
2
|
export const LIB_VERSION = "1.0.0";
|
|
36
3
|
|
|
@@ -1,30 +1,3 @@
|
|
|
1
|
-
// // src/core/types/SecureOptions.ts
|
|
2
|
-
|
|
3
|
-
// export interface SecureOptions {
|
|
4
|
-
// /** Enable/override CORS for this route */
|
|
5
|
-
// cors?: boolean | object;
|
|
6
|
-
|
|
7
|
-
// /** Per-route rate limit */
|
|
8
|
-
// rateLimit?: boolean | "strict" | "relaxed" | object;
|
|
9
|
-
|
|
10
|
-
// /** Sanitize request body */
|
|
11
|
-
// sanitize?: boolean;
|
|
12
|
-
|
|
13
|
-
// /** Validation schema (Zod or express-validator) */
|
|
14
|
-
// validate?: any;
|
|
15
|
-
|
|
16
|
-
// /** Auto-JSON parsing (express.json) options */
|
|
17
|
-
// json?: boolean | object;
|
|
18
|
-
|
|
19
|
-
// /** NEW: Per-route authentication (JWT protect) */
|
|
20
|
-
// auth?: boolean | { required?: boolean };
|
|
21
|
-
// }
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
// src/core/types/SecureOptions.ts
|
|
28
1
|
import { z, ZodSchema } from 'zod';
|
|
29
2
|
import { ValidationChain } from 'express-validator';
|
|
30
3
|
|
package/src/core/useSecure.ts
CHANGED
|
@@ -6,14 +6,13 @@ import { SecureOptions } from "./types/SecureOptions.js";
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
export function useSecure(options?: SecureOptions | "api" | "strict" | "public") {
|
|
9
|
-
console.warn("
|
|
9
|
+
console.warn("useSecure() is deprecated. Use HiSecure.middleware() or fluent API methods.");
|
|
10
10
|
return HiSecure.middleware(options);
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
// Legacy support - route-level security
|
|
15
15
|
|
|
16
|
-
|
|
17
16
|
export function secureRoute(options?: SecureOptions) {
|
|
18
17
|
const chain: any[] = [];
|
|
19
18
|
|
|
@@ -45,7 +44,6 @@ export function secureRoute(options?: SecureOptions) {
|
|
|
45
44
|
typeof options.auth === 'object' ? options.auth : undefined
|
|
46
45
|
));
|
|
47
46
|
}
|
|
48
|
-
|
|
49
47
|
return chain;
|
|
50
48
|
}
|
|
51
49
|
|
|
@@ -27,7 +27,7 @@ export class AuthManager {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
if (opts.jwtSecret.length < 32) {
|
|
30
|
-
logger.warn("
|
|
30
|
+
logger.warn(" JWT secret is less than 32 characters - consider using a stronger secret");
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
logger.info("AuthManager initialized");
|
|
@@ -108,7 +108,7 @@ export class AuthManager {
|
|
|
108
108
|
(req as any).auth = decoded;
|
|
109
109
|
(req as any).user = decoded;
|
|
110
110
|
|
|
111
|
-
// Role-based authorization
|
|
111
|
+
// Role-based authorization - role added Middleware
|
|
112
112
|
if (roles && roles.length > 0) {
|
|
113
113
|
const userRole = (decoded as any).role || (decoded as any).roles?.[0];
|
|
114
114
|
if (!userRole || !roles.includes(userRole)) {
|
|
@@ -16,7 +16,7 @@ export class CorsManager {
|
|
|
16
16
|
|
|
17
17
|
const finalOptions = options ? { ...defaultOptions, ...options } : defaultOptions;
|
|
18
18
|
|
|
19
|
-
logger.debug("
|
|
19
|
+
logger.debug("CORS configured", {
|
|
20
20
|
origin: finalOptions.origin,
|
|
21
21
|
methods: finalOptions.methods
|
|
22
22
|
});
|
|
@@ -24,7 +24,7 @@ export class CorsManager {
|
|
|
24
24
|
return cors(finalOptions);
|
|
25
25
|
|
|
26
26
|
} catch (err: any) {
|
|
27
|
-
logger.error("
|
|
27
|
+
logger.error(" CORS Manager: failed to create CORS middleware", {
|
|
28
28
|
error: err?.message || err,
|
|
29
29
|
options
|
|
30
30
|
});
|
|
@@ -37,7 +37,7 @@ export class HashManager {
|
|
|
37
37
|
usedFallback: false
|
|
38
38
|
};
|
|
39
39
|
} catch (err: any) {
|
|
40
|
-
logger.warn("
|
|
40
|
+
logger.warn("Primary hashing failed", {
|
|
41
41
|
error: err.message,
|
|
42
42
|
algorithm: this.config.primary
|
|
43
43
|
});
|
|
@@ -52,7 +52,7 @@ export class HashManager {
|
|
|
52
52
|
const hash = await this.fallbackAdapter.hash(value);
|
|
53
53
|
|
|
54
54
|
// Log security downgrade warning
|
|
55
|
-
logger.warn("
|
|
55
|
+
logger.warn("SECURITY DOWNGRADE: Using fallback hashing", {
|
|
56
56
|
from: this.config.primary,
|
|
57
57
|
to: this.config.fallback
|
|
58
58
|
});
|
|
@@ -63,7 +63,7 @@ export class HashManager {
|
|
|
63
63
|
usedFallback: true
|
|
64
64
|
};
|
|
65
65
|
} catch (fallbackErr: any) {
|
|
66
|
-
logger.error("
|
|
66
|
+
logger.error("Fallback hashing failed", {
|
|
67
67
|
error: fallbackErr?.message,
|
|
68
68
|
});
|
|
69
69
|
throw new AdapterError(
|
|
@@ -74,20 +74,20 @@ export class HashManager {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
async verify(value: string, hashed: string): Promise<boolean> {
|
|
77
|
-
//
|
|
77
|
+
// primary adapter - first
|
|
78
78
|
try {
|
|
79
79
|
return await this.primaryAdapter.verify(value, hashed);
|
|
80
80
|
} catch (primaryErr: any) {
|
|
81
|
-
logger.warn("
|
|
81
|
+
logger.warn("Primary verify failed", {
|
|
82
82
|
error: primaryErr?.message,
|
|
83
83
|
});
|
|
84
84
|
|
|
85
|
-
//
|
|
85
|
+
// fallback exists - try it
|
|
86
86
|
if (this.fallbackAdapter) {
|
|
87
87
|
try {
|
|
88
88
|
return await this.fallbackAdapter.verify(value, hashed);
|
|
89
89
|
} catch (fallbackErr: any) {
|
|
90
|
-
logger.error("
|
|
90
|
+
logger.error(" Fallback verify failed", {
|
|
91
91
|
error: fallbackErr?.message,
|
|
92
92
|
});
|
|
93
93
|
throw new AdapterError(
|
|
@@ -13,7 +13,7 @@ export class JsonManager {
|
|
|
13
13
|
};
|
|
14
14
|
return express.json({ ...defaultOptions, ...(options || {}) });
|
|
15
15
|
} catch (err: any) {
|
|
16
|
-
logger.error("
|
|
16
|
+
logger.error("JSON Manager: failed to create JSON parser");
|
|
17
17
|
throw new AdapterError("JSON parser initialization failed.");
|
|
18
18
|
}
|
|
19
19
|
}
|
|
@@ -28,7 +28,7 @@ export class JsonManager {
|
|
|
28
28
|
const opts = { ...defaultOptions, ...(options || {}) };
|
|
29
29
|
return express.urlencoded(opts);
|
|
30
30
|
} catch (err: any) {
|
|
31
|
-
logger.error("
|
|
31
|
+
logger.error("URL-encoded parser failed");
|
|
32
32
|
throw new AdapterError("URL-encoded parser initialization failed.");
|
|
33
33
|
}
|
|
34
34
|
}
|
|
@@ -45,13 +45,13 @@ export class JsonManager {
|
|
|
45
45
|
});
|
|
46
46
|
|
|
47
47
|
req.parsedQuery = parsed;
|
|
48
|
-
logger.debug("
|
|
48
|
+
logger.debug(" Query parsed", {
|
|
49
49
|
keys: Object.keys(parsed)
|
|
50
50
|
});
|
|
51
51
|
}
|
|
52
52
|
next();
|
|
53
53
|
} catch (err: any) {
|
|
54
|
-
logger.error("
|
|
54
|
+
logger.error("Failed to parse query", { error: err?.message });
|
|
55
55
|
next(new AdapterError("Query parsing failed."));
|
|
56
56
|
}
|
|
57
57
|
};
|
|
@@ -64,7 +64,7 @@ export class RateLimitManager {
|
|
|
64
64
|
k => !allowedOverrides.includes(k) && k !== 'mode'
|
|
65
65
|
);
|
|
66
66
|
if (attemptedOverrides.length > 0) {
|
|
67
|
-
logger.warn("
|
|
67
|
+
logger.warn("Rate limit overrides ignored", {
|
|
68
68
|
preset: opts?.mode || 'default',
|
|
69
69
|
ignoredOptions: attemptedOverrides
|
|
70
70
|
});
|
|
@@ -79,7 +79,7 @@ export class RateLimitManager {
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
try {
|
|
82
|
-
logger.info("
|
|
82
|
+
logger.info("Applying rate limiting", {
|
|
83
83
|
mode: opts?.mode || 'default',
|
|
84
84
|
windowMs: finalOptions.windowMs,
|
|
85
85
|
max: finalOptions.max
|
|
@@ -87,7 +87,7 @@ export class RateLimitManager {
|
|
|
87
87
|
|
|
88
88
|
return this.primaryAdapter.getMiddleware(finalOptions);
|
|
89
89
|
} catch (err: any) {
|
|
90
|
-
logger.warn("
|
|
90
|
+
logger.warn("Primary rate limiter failed → fallback", {
|
|
91
91
|
error: err?.message
|
|
92
92
|
});
|
|
93
93
|
|
|
@@ -96,10 +96,10 @@ export class RateLimitManager {
|
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
try {
|
|
99
|
-
logger.info("
|
|
99
|
+
logger.info("Using fallback rate limiter");
|
|
100
100
|
return this.fallbackAdapter.getMiddleware(finalOptions);
|
|
101
101
|
} catch (fallbackErr: any) {
|
|
102
|
-
logger.error("
|
|
102
|
+
logger.error("Fallback limiter also failed", {
|
|
103
103
|
error: fallbackErr?.message
|
|
104
104
|
});
|
|
105
105
|
throw new AdapterError("Both primary and fallback limiters failed.");
|