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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JWTAdapter.d.ts","sourceRoot":"","sources":["../../src/adapters/JWTAdapter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"JWTAdapter.d.ts","sourceRoot":"","sources":["../../src/adapters/JWTAdapter.ts"],"names":[],"mappings":"AAgGA,OAAO,GAAG,MAAM,cAAc,CAAC;AAK/B,MAAM,WAAW,iBAAiB;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,SAAS,CAAC,EAAE,GAAG,CAAC,SAAS,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,WAAW;IACxB,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAChC;AAED,qBAAa,UAAU;IACnB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAC,CAAkB;IACpC,OAAO,CAAC,SAAS,CAAgB;IACjC,OAAO,CAAC,MAAM,CAAC,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAC,CAAoB;gBAEzB,OAAO,EAAE,iBAAiB;IAoBtC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW;IA6B3C,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;KAAE;CA4BnE"}
|
|
@@ -1,26 +1,107 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
// import jwt from "jsonwebtoken";
|
|
3
|
+
// import { randomUUID } from "crypto";
|
|
4
|
+
// import { AdapterError } from "../core/errors/AdapterError.js";
|
|
5
|
+
// import { logError } from "../logging/index.js";
|
|
6
|
+
// import { logger } from "../logging";
|
|
2
7
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
8
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
9
|
};
|
|
5
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
11
|
exports.JWTAdapter = void 0;
|
|
12
|
+
// export interface JWTAdapterOptions {
|
|
13
|
+
// secret: string;
|
|
14
|
+
// expiresIn?: string | number;
|
|
15
|
+
// algorithm?: jwt.Algorithm;
|
|
16
|
+
// issuer?: string;
|
|
17
|
+
// audience?: string | string[];
|
|
18
|
+
// }
|
|
19
|
+
// export interface SignOptions {
|
|
20
|
+
// expiresIn?: string | number;
|
|
21
|
+
// jti?: string;
|
|
22
|
+
// subject?: string;
|
|
23
|
+
// issuer?: string;
|
|
24
|
+
// audience?: string | string[];
|
|
25
|
+
// }
|
|
26
|
+
// export class JWTAdapter {
|
|
27
|
+
// private secret: string;
|
|
28
|
+
// private expiresIn?: string | number;
|
|
29
|
+
// private algorithm: jwt.Algorithm;
|
|
30
|
+
// private issuer?: string;
|
|
31
|
+
// private audience?: string | string[];
|
|
32
|
+
// constructor(options: JWTAdapterOptions) {
|
|
33
|
+
// if (!options.secret) {
|
|
34
|
+
// throw new AdapterError("JWT secret is required");
|
|
35
|
+
// }
|
|
36
|
+
// if (options.secret.length < 32) {
|
|
37
|
+
// logger.warn("JWT secret shorter than 32 chars. Consider using stronger secret.");
|
|
38
|
+
// // logError("JWT secret is too short (minimum 32 characters recommended)");
|
|
39
|
+
// }
|
|
40
|
+
// this.secret = options.secret;
|
|
41
|
+
// this.expiresIn = options.expiresIn;
|
|
42
|
+
// this.algorithm = options.algorithm || 'HS256'; // Default algorithm
|
|
43
|
+
// this.issuer = options.issuer;
|
|
44
|
+
// this.audience = options.audience;
|
|
45
|
+
// }
|
|
46
|
+
// sign(payload: object, options?: SignOptions) {
|
|
47
|
+
// try {
|
|
48
|
+
// const jwtOptions: jwt.SignOptions = {
|
|
49
|
+
// algorithm: this.algorithm,
|
|
50
|
+
// issuer: options?.issuer || this.issuer,
|
|
51
|
+
// audience: options?.audience || this.audience,
|
|
52
|
+
// jwtid: options?.jti || randomUUID(),
|
|
53
|
+
// subject: options?.subject
|
|
54
|
+
// };
|
|
55
|
+
// if (options?.expiresIn !== undefined) {
|
|
56
|
+
// jwtOptions.expiresIn = options.expiresIn as number;
|
|
57
|
+
// } else if (this.expiresIn !== undefined) {
|
|
58
|
+
// jwtOptions.expiresIn = this.expiresIn as number;
|
|
59
|
+
// }
|
|
60
|
+
// return jwt.sign(payload, this.secret, jwtOptions);
|
|
61
|
+
// } catch (err: any) {
|
|
62
|
+
// logError("JWTAdapter.sign failed", { error: err?.message });
|
|
63
|
+
// throw new AdapterError(err?.message || "JWT sign failed");
|
|
64
|
+
// }
|
|
65
|
+
// }
|
|
66
|
+
// verify(token: string, options?: { audience?: string | string[] }) {
|
|
67
|
+
// try {
|
|
68
|
+
// const verifyOptions: jwt.VerifyOptions = {
|
|
69
|
+
// algorithms: [this.algorithm],
|
|
70
|
+
// issuer: this.issuer,
|
|
71
|
+
// audience: options?.audience as string || this.audience as string
|
|
72
|
+
// };
|
|
73
|
+
// return jwt.verify(token, this.secret, verifyOptions);
|
|
74
|
+
// } catch (err: any) {
|
|
75
|
+
// logError("JWTAdapter.verify failed", { error: err?.message });
|
|
76
|
+
// if (err.name === 'TokenExpiredError') {
|
|
77
|
+
// throw new AdapterError("JWT token has expired");
|
|
78
|
+
// }
|
|
79
|
+
// if (err.name === 'JsonWebTokenError') {
|
|
80
|
+
// throw new AdapterError("Invalid JWT token");
|
|
81
|
+
// }
|
|
82
|
+
// throw new AdapterError(err?.message || "JWT verification failed");
|
|
83
|
+
// }
|
|
84
|
+
// }
|
|
85
|
+
// }
|
|
7
86
|
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
8
87
|
const crypto_1 = require("crypto");
|
|
9
|
-
const
|
|
10
|
-
const index_js_1 = require("../logging/index.js");
|
|
88
|
+
const AdapterError_1 = require("../core/errors/AdapterError");
|
|
11
89
|
const logging_1 = require("../logging");
|
|
12
90
|
class JWTAdapter {
|
|
13
91
|
constructor(options) {
|
|
14
92
|
if (!options.secret) {
|
|
15
|
-
throw new
|
|
93
|
+
throw new AdapterError_1.AdapterError("JWT secret is required");
|
|
16
94
|
}
|
|
17
95
|
if (options.secret.length < 32) {
|
|
18
|
-
logging_1.logger.warn("JWT secret
|
|
19
|
-
|
|
96
|
+
logging_1.logger.warn("Weak JWT secret detected", {
|
|
97
|
+
adapter: "jwt",
|
|
98
|
+
operation: "init",
|
|
99
|
+
secretLength: options.secret.length
|
|
100
|
+
});
|
|
20
101
|
}
|
|
21
102
|
this.secret = options.secret;
|
|
22
103
|
this.expiresIn = options.expiresIn;
|
|
23
|
-
this.algorithm = options.algorithm ||
|
|
104
|
+
this.algorithm = options.algorithm || "HS256";
|
|
24
105
|
this.issuer = options.issuer;
|
|
25
106
|
this.audience = options.audience;
|
|
26
107
|
}
|
|
@@ -42,8 +123,12 @@ class JWTAdapter {
|
|
|
42
123
|
return jsonwebtoken_1.default.sign(payload, this.secret, jwtOptions);
|
|
43
124
|
}
|
|
44
125
|
catch (err) {
|
|
45
|
-
|
|
46
|
-
|
|
126
|
+
logging_1.logger.error("JWT signing failed", {
|
|
127
|
+
adapter: "jwt",
|
|
128
|
+
operation: "sign",
|
|
129
|
+
reason: err?.message
|
|
130
|
+
});
|
|
131
|
+
throw new AdapterError_1.AdapterError("JWT sign failed");
|
|
47
132
|
}
|
|
48
133
|
}
|
|
49
134
|
verify(token, options) {
|
|
@@ -51,19 +136,23 @@ class JWTAdapter {
|
|
|
51
136
|
const verifyOptions = {
|
|
52
137
|
algorithms: [this.algorithm],
|
|
53
138
|
issuer: this.issuer,
|
|
54
|
-
audience: options?.audience || this.audience
|
|
139
|
+
audience: (options?.audience || this.audience)
|
|
55
140
|
};
|
|
56
141
|
return jsonwebtoken_1.default.verify(token, this.secret, verifyOptions);
|
|
57
142
|
}
|
|
58
143
|
catch (err) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
144
|
+
logging_1.logger.error("JWT verification failed", {
|
|
145
|
+
adapter: "jwt",
|
|
146
|
+
operation: "verify",
|
|
147
|
+
reason: err?.message
|
|
148
|
+
});
|
|
149
|
+
if (err?.name === "TokenExpiredError") {
|
|
150
|
+
throw new AdapterError_1.AdapterError("JWT token has expired");
|
|
62
151
|
}
|
|
63
|
-
if (err
|
|
64
|
-
throw new
|
|
152
|
+
if (err?.name === "JsonWebTokenError") {
|
|
153
|
+
throw new AdapterError_1.AdapterError("Invalid JWT token");
|
|
65
154
|
}
|
|
66
|
-
throw new
|
|
155
|
+
throw new AdapterError_1.AdapterError("JWT verification failed");
|
|
67
156
|
}
|
|
68
157
|
}
|
|
69
158
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JWTAdapter.js","sourceRoot":"","sources":["../../src/adapters/JWTAdapter.ts"],"names":[],"mappings":"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","sourcesContent":["import jwt from \"jsonwebtoken\";\r\nimport { randomUUID } from \"crypto\"; \r\nimport { AdapterError } from \"../core/errors/AdapterError.js\";\r\nimport { logError } from \"../logging/index.js\";\r\nimport { logger } from \"../logging\";\r\n\r\nexport interface JWTAdapterOptions {\r\n secret: string;\r\n expiresIn?: string | number;\r\n algorithm?: jwt.Algorithm;\r\n issuer?: string;\r\n audience?: string | string[];\r\n}\r\n\r\nexport interface SignOptions {\r\n expiresIn?: string | number;\r\n jti?: string; \r\n subject?: string;\r\n issuer?: string;\r\n audience?: string | string[];\r\n}\r\n\r\nexport class JWTAdapter {\r\n private secret: string;\r\n private expiresIn?: string | number;\r\n private algorithm: jwt.Algorithm;\r\n private issuer?: string;\r\n private audience?: string | string[];\r\n\r\n constructor(options: JWTAdapterOptions) {\r\n if (!options.secret) {\r\n throw new AdapterError(\"JWT secret is required\");\r\n }\r\n\r\n if (options.secret.length < 32) {\r\n logger.warn(\"JWT secret shorter than 32 chars. Consider using stronger secret.\");\r\n // logError(\"JWT secret is too short (minimum 32 characters recommended)\");\r\n }\r\n\r\n this.secret = options.secret;\r\n this.expiresIn = options.expiresIn;\r\n this.algorithm = options.algorithm || 'HS256'; // Default algorithm \r\n this.issuer = options.issuer;\r\n this.audience = options.audience;\r\n }\r\n\r\n sign(payload: object, options?: SignOptions) {\r\n try {\r\n const jwtOptions: jwt.SignOptions = {\r\n algorithm: this.algorithm,\r\n issuer: options?.issuer || this.issuer,\r\n audience: options?.audience || this.audience,\r\n jwtid: options?.jti || randomUUID(), \r\n subject: options?.subject\r\n };\r\n\r\n if (options?.expiresIn !== undefined) {\r\n jwtOptions.expiresIn = options.expiresIn as number;\r\n } else if (this.expiresIn !== undefined) {\r\n jwtOptions.expiresIn = this.expiresIn as number;\r\n }\r\n\r\n return jwt.sign(payload, this.secret, jwtOptions);\r\n\r\n } catch (err: any) {\r\n logError(\"JWTAdapter.sign failed\", { error: err?.message });\r\n throw new AdapterError(err?.message || \"JWT sign failed\");\r\n }\r\n }\r\n\r\n verify(token: string, options?: { audience?: string | string[] }) {\r\n try {\r\n const verifyOptions: jwt.VerifyOptions = {\r\n algorithms: [this.algorithm],\r\n issuer: this.issuer,\r\n audience: options?.audience as string || this.audience as string\r\n };\r\n\r\n return jwt.verify(token, this.secret, verifyOptions);\r\n } catch (err: any) {\r\n logError(\"JWTAdapter.verify failed\", { error: err?.message });\r\n \r\n \r\n if (err.name === 'TokenExpiredError') {\r\n throw new AdapterError(\"JWT token has expired\");\r\n }\r\n if (err.name === 'JsonWebTokenError') {\r\n throw new AdapterError(\"Invalid JWT token\");\r\n }\r\n \r\n throw new AdapterError(err?.message || \"JWT verification failed\");\r\n }\r\n }\r\n}"]}
|
|
1
|
+
{"version":3,"file":"JWTAdapter.js","sourceRoot":"","sources":["../../src/adapters/JWTAdapter.ts"],"names":[],"mappings":"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","sourcesContent":["// import jwt from \"jsonwebtoken\";\r\n// import { randomUUID } from \"crypto\"; \r\n// import { AdapterError } from \"../core/errors/AdapterError.js\";\r\n// import { logError } from \"../logging/index.js\";\r\n// import { logger } from \"../logging\";\r\n\r\n// export interface JWTAdapterOptions {\r\n// secret: string;\r\n// expiresIn?: string | number;\r\n// algorithm?: jwt.Algorithm;\r\n// issuer?: string;\r\n// audience?: string | string[];\r\n// }\r\n\r\n// export interface SignOptions {\r\n// expiresIn?: string | number;\r\n// jti?: string; \r\n// subject?: string;\r\n// issuer?: string;\r\n// audience?: string | string[];\r\n// }\r\n\r\n// export class JWTAdapter {\r\n// private secret: string;\r\n// private expiresIn?: string | number;\r\n// private algorithm: jwt.Algorithm;\r\n// private issuer?: string;\r\n// private audience?: string | string[];\r\n\r\n// constructor(options: JWTAdapterOptions) {\r\n// if (!options.secret) {\r\n// throw new AdapterError(\"JWT secret is required\");\r\n// }\r\n\r\n// if (options.secret.length < 32) {\r\n// logger.warn(\"JWT secret shorter than 32 chars. Consider using stronger secret.\");\r\n// // logError(\"JWT secret is too short (minimum 32 characters recommended)\");\r\n// }\r\n\r\n// this.secret = options.secret;\r\n// this.expiresIn = options.expiresIn;\r\n// this.algorithm = options.algorithm || 'HS256'; // Default algorithm \r\n// this.issuer = options.issuer;\r\n// this.audience = options.audience;\r\n// }\r\n\r\n// sign(payload: object, options?: SignOptions) {\r\n// try {\r\n// const jwtOptions: jwt.SignOptions = {\r\n// algorithm: this.algorithm,\r\n// issuer: options?.issuer || this.issuer,\r\n// audience: options?.audience || this.audience,\r\n// jwtid: options?.jti || randomUUID(), \r\n// subject: options?.subject\r\n// };\r\n\r\n// if (options?.expiresIn !== undefined) {\r\n// jwtOptions.expiresIn = options.expiresIn as number;\r\n// } else if (this.expiresIn !== undefined) {\r\n// jwtOptions.expiresIn = this.expiresIn as number;\r\n// }\r\n\r\n// return jwt.sign(payload, this.secret, jwtOptions);\r\n\r\n// } catch (err: any) {\r\n// logError(\"JWTAdapter.sign failed\", { error: err?.message });\r\n// throw new AdapterError(err?.message || \"JWT sign failed\");\r\n// }\r\n// }\r\n\r\n// verify(token: string, options?: { audience?: string | string[] }) {\r\n// try {\r\n// const verifyOptions: jwt.VerifyOptions = {\r\n// algorithms: [this.algorithm],\r\n// issuer: this.issuer,\r\n// audience: options?.audience as string || this.audience as string\r\n// };\r\n\r\n// return jwt.verify(token, this.secret, verifyOptions);\r\n// } catch (err: any) {\r\n// logError(\"JWTAdapter.verify failed\", { error: err?.message });\r\n \r\n \r\n// if (err.name === 'TokenExpiredError') {\r\n// throw new AdapterError(\"JWT token has expired\");\r\n// }\r\n// if (err.name === 'JsonWebTokenError') {\r\n// throw new AdapterError(\"Invalid JWT token\");\r\n// }\r\n \r\n// throw new AdapterError(err?.message || \"JWT verification failed\");\r\n// }\r\n// }\r\n// }\r\n\r\n\r\nimport jwt from \"jsonwebtoken\";\r\nimport { randomUUID } from \"crypto\";\r\nimport { AdapterError } from \"../core/errors/AdapterError\";\r\nimport { logger } from \"../logging\";\r\n\r\nexport interface JWTAdapterOptions {\r\n secret: string;\r\n expiresIn?: string | number;\r\n algorithm?: jwt.Algorithm;\r\n issuer?: string;\r\n audience?: string | string[];\r\n}\r\n\r\nexport interface SignOptions {\r\n expiresIn?: string | number;\r\n jti?: string;\r\n subject?: string;\r\n issuer?: string;\r\n audience?: string | string[];\r\n}\r\n\r\nexport class JWTAdapter {\r\n private secret: string;\r\n private expiresIn?: string | number;\r\n private algorithm: jwt.Algorithm;\r\n private issuer?: string;\r\n private audience?: string | string[];\r\n\r\n constructor(options: JWTAdapterOptions) {\r\n if (!options.secret) {\r\n throw new AdapterError(\"JWT secret is required\");\r\n }\r\n\r\n if (options.secret.length < 32) {\r\n logger.warn(\"Weak JWT secret detected\", {\r\n adapter: \"jwt\",\r\n operation: \"init\",\r\n secretLength: options.secret.length\r\n });\r\n }\r\n\r\n this.secret = options.secret;\r\n this.expiresIn = options.expiresIn;\r\n this.algorithm = options.algorithm || \"HS256\";\r\n this.issuer = options.issuer;\r\n this.audience = options.audience;\r\n }\r\n\r\n sign(payload: object, options?: SignOptions) {\r\n try {\r\n const jwtOptions: jwt.SignOptions = {\r\n algorithm: this.algorithm,\r\n issuer: options?.issuer || this.issuer,\r\n audience: options?.audience || this.audience,\r\n jwtid: options?.jti || randomUUID(),\r\n subject: options?.subject\r\n };\r\n\r\n if (options?.expiresIn !== undefined) {\r\n jwtOptions.expiresIn = options.expiresIn as any;\r\n } else if (this.expiresIn !== undefined) {\r\n jwtOptions.expiresIn = this.expiresIn as any;\r\n }\r\n\r\n return jwt.sign(payload, this.secret, jwtOptions);\r\n\r\n } catch (err: any) {\r\n logger.error(\"JWT signing failed\", {\r\n adapter: \"jwt\",\r\n operation: \"sign\",\r\n reason: err?.message\r\n });\r\n\r\n throw new AdapterError(\"JWT sign failed\");\r\n }\r\n }\r\n\r\n verify(token: string, options?: { audience?: string | string[] }) {\r\n try {\r\n const verifyOptions: jwt.VerifyOptions = {\r\n algorithms: [this.algorithm],\r\n issuer: this.issuer,\r\n audience: (options?.audience || this.audience) as string\r\n };\r\n\r\n return jwt.verify(token, this.secret, verifyOptions);\r\n\r\n } catch (err: any) {\r\n logger.error(\"JWT verification failed\", {\r\n adapter: \"jwt\",\r\n operation: \"verify\",\r\n reason: err?.message\r\n });\r\n\r\n if (err?.name === \"TokenExpiredError\") {\r\n throw new AdapterError(\"JWT token has expired\");\r\n }\r\n\r\n if (err?.name === \"JsonWebTokenError\") {\r\n throw new AdapterError(\"Invalid JWT token\");\r\n }\r\n\r\n throw new AdapterError(\"JWT verification failed\");\r\n }\r\n }\r\n}\r\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RLFlexibleAdapter.d.ts","sourceRoot":"","sources":["../../src/adapters/RLFlexibleAdapter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"RLFlexibleAdapter.d.ts","sourceRoot":"","sources":["../../src/adapters/RLFlexibleAdapter.ts"],"names":[],"mappings":"AAsFA,MAAM,WAAW,SAAS;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,qBAAa,iBAAiB;IAC1B,aAAa,CAAC,OAAO,GAAE,SAAc,SA0BV,GAAG,OAAO,GAAG,QAAQ,GAAG;IA0CnD,OAAO,CAAC,SAAS;CAUpB"}
|
|
@@ -1,9 +1,73 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
// import { RateLimiterMemory, RateLimiterRes } from "rate-limiter-flexible";
|
|
3
|
+
// import { logger } from "../logging/index.js";
|
|
4
|
+
// import { AdapterError } from "../core/errors/AdapterError.js";
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.RLFlexibleAdapter = void 0;
|
|
7
|
+
// export interface RLOptions {
|
|
8
|
+
// points?: number;
|
|
9
|
+
// duration?: number;
|
|
10
|
+
// message?: any;
|
|
11
|
+
// blockDuration?: number;
|
|
12
|
+
// }
|
|
13
|
+
// export class RLFlexibleAdapter {
|
|
14
|
+
// getMiddleware(options: RLOptions = {}) {
|
|
15
|
+
// try {
|
|
16
|
+
// const defaultOptions = {
|
|
17
|
+
// points: 100,
|
|
18
|
+
// duration: 60,
|
|
19
|
+
// message: "Too many requests, slow down.",
|
|
20
|
+
// blockDuration: 0
|
|
21
|
+
// };
|
|
22
|
+
// const finalOptions = { ...defaultOptions, ...options };
|
|
23
|
+
// const limiter = new RateLimiterMemory({
|
|
24
|
+
// points: finalOptions.points,
|
|
25
|
+
// duration: finalOptions.duration,
|
|
26
|
+
// blockDuration: finalOptions.blockDuration
|
|
27
|
+
// });
|
|
28
|
+
// return async (req: any, res: any, next: any) => {
|
|
29
|
+
// const ip = this.extractIP(req);
|
|
30
|
+
// try {
|
|
31
|
+
// await limiter.consume(ip);
|
|
32
|
+
// next();
|
|
33
|
+
// } catch (err: any) {
|
|
34
|
+
// const rlErr = err as RateLimiterRes;
|
|
35
|
+
// logger.warn("RLFlexibleAdapter: rate limit exceeded", {
|
|
36
|
+
// ip,
|
|
37
|
+
// path: req.path,
|
|
38
|
+
// method: req.method,
|
|
39
|
+
// retryAfter: rlErr.msBeforeNext
|
|
40
|
+
// });
|
|
41
|
+
// res.setHeader('Retry-After', Math.ceil(rlErr.msBeforeNext / 1000));
|
|
42
|
+
// return res.status(429).json({
|
|
43
|
+
// success: false,
|
|
44
|
+
// error: "RATE_LIMIT_EXCEEDED",
|
|
45
|
+
// retryAfter: Math.ceil(rlErr.msBeforeNext / 1000),
|
|
46
|
+
// message: finalOptions.message
|
|
47
|
+
// });
|
|
48
|
+
// }
|
|
49
|
+
// };
|
|
50
|
+
// } catch (err: any) {
|
|
51
|
+
// logger.error("RLFlexibleAdapter: failed to initialize limiter", {
|
|
52
|
+
// error: err?.message || err
|
|
53
|
+
// });
|
|
54
|
+
// throw new AdapterError("RateLimiterFlexible creation failed.");
|
|
55
|
+
// }
|
|
56
|
+
// }
|
|
57
|
+
// private extractIP(req: any): string {
|
|
58
|
+
// return (
|
|
59
|
+
// req.headers['x-real-ip'] ||
|
|
60
|
+
// req.headers['x-forwarded-for']?.split(',')[0]?.trim() ||
|
|
61
|
+
// req.ip ||
|
|
62
|
+
// req.connection?.remoteAddress ||
|
|
63
|
+
// req.socket?.remoteAddress ||
|
|
64
|
+
// 'unknown'
|
|
65
|
+
// );
|
|
66
|
+
// }
|
|
67
|
+
// }
|
|
4
68
|
const rate_limiter_flexible_1 = require("rate-limiter-flexible");
|
|
5
|
-
const
|
|
6
|
-
const
|
|
69
|
+
const logging_1 = require("../logging");
|
|
70
|
+
const AdapterError_1 = require("../core/errors/AdapterError");
|
|
7
71
|
class RLFlexibleAdapter {
|
|
8
72
|
getMiddleware(options = {}) {
|
|
9
73
|
try {
|
|
@@ -19,6 +83,13 @@ class RLFlexibleAdapter {
|
|
|
19
83
|
duration: finalOptions.duration,
|
|
20
84
|
blockDuration: finalOptions.blockDuration
|
|
21
85
|
});
|
|
86
|
+
logging_1.logger.info("Rate limiter initialized", {
|
|
87
|
+
adapter: "rate-limiter-flexible",
|
|
88
|
+
operation: "init",
|
|
89
|
+
points: finalOptions.points,
|
|
90
|
+
duration: finalOptions.duration,
|
|
91
|
+
blockDuration: finalOptions.blockDuration
|
|
92
|
+
});
|
|
22
93
|
return async (req, res, next) => {
|
|
23
94
|
const ip = this.extractIP(req);
|
|
24
95
|
try {
|
|
@@ -27,13 +98,15 @@ class RLFlexibleAdapter {
|
|
|
27
98
|
}
|
|
28
99
|
catch (err) {
|
|
29
100
|
const rlErr = err;
|
|
30
|
-
|
|
101
|
+
logging_1.logger.warn("Rate limit exceeded", {
|
|
102
|
+
adapter: "rate-limiter-flexible",
|
|
103
|
+
operation: "consume",
|
|
31
104
|
ip,
|
|
32
|
-
path: req.path,
|
|
33
105
|
method: req.method,
|
|
34
|
-
|
|
106
|
+
path: req.path,
|
|
107
|
+
retryAfterMs: rlErr.msBeforeNext
|
|
35
108
|
});
|
|
36
|
-
res.setHeader(
|
|
109
|
+
res.setHeader("Retry-After", Math.ceil(rlErr.msBeforeNext / 1000));
|
|
37
110
|
return res.status(429).json({
|
|
38
111
|
success: false,
|
|
39
112
|
error: "RATE_LIMIT_EXCEEDED",
|
|
@@ -44,19 +117,21 @@ class RLFlexibleAdapter {
|
|
|
44
117
|
};
|
|
45
118
|
}
|
|
46
119
|
catch (err) {
|
|
47
|
-
|
|
48
|
-
|
|
120
|
+
logging_1.logger.error("Rate limiter initialization failed", {
|
|
121
|
+
adapter: "rate-limiter-flexible",
|
|
122
|
+
operation: "init",
|
|
123
|
+
reason: err?.message
|
|
49
124
|
});
|
|
50
|
-
throw new
|
|
125
|
+
throw new AdapterError_1.AdapterError("RateLimiterFlexible creation failed.");
|
|
51
126
|
}
|
|
52
127
|
}
|
|
53
128
|
extractIP(req) {
|
|
54
|
-
return (req.headers[
|
|
55
|
-
req.headers[
|
|
129
|
+
return (req.headers["x-real-ip"] ||
|
|
130
|
+
req.headers["x-forwarded-for"]?.split(",")[0]?.trim() ||
|
|
56
131
|
req.ip ||
|
|
57
132
|
req.connection?.remoteAddress ||
|
|
58
133
|
req.socket?.remoteAddress ||
|
|
59
|
-
|
|
134
|
+
"unknown");
|
|
60
135
|
}
|
|
61
136
|
}
|
|
62
137
|
exports.RLFlexibleAdapter = RLFlexibleAdapter;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RLFlexibleAdapter.js","sourceRoot":"","sources":["../../src/adapters/RLFlexibleAdapter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"RLFlexibleAdapter.js","sourceRoot":"","sources":["../../src/adapters/RLFlexibleAdapter.ts"],"names":[],"mappings":"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","sourcesContent":["// import { RateLimiterMemory, RateLimiterRes } from \"rate-limiter-flexible\";\r\n// import { logger } from \"../logging/index.js\";\r\n// import { AdapterError } from \"../core/errors/AdapterError.js\";\r\n\r\n// export interface RLOptions {\r\n// points?: number;\r\n// duration?: number; \r\n// message?: any;\r\n// blockDuration?: number;\r\n// }\r\n\r\n// export class RLFlexibleAdapter {\r\n// getMiddleware(options: RLOptions = {}) {\r\n// try {\r\n// const defaultOptions = {\r\n// points: 100,\r\n// duration: 60,\r\n// message: \"Too many requests, slow down.\",\r\n// blockDuration: 0\r\n// };\r\n\r\n// const finalOptions = { ...defaultOptions, ...options };\r\n\r\n// const limiter = new RateLimiterMemory({\r\n// points: finalOptions.points,\r\n// duration: finalOptions.duration,\r\n// blockDuration: finalOptions.blockDuration\r\n// });\r\n\r\n// return async (req: any, res: any, next: any) => {\r\n \r\n// const ip = this.extractIP(req);\r\n\r\n// try {\r\n// await limiter.consume(ip);\r\n// next();\r\n// } catch (err: any) {\r\n// const rlErr = err as RateLimiterRes;\r\n\r\n// logger.warn(\"RLFlexibleAdapter: rate limit exceeded\", {\r\n// ip,\r\n// path: req.path,\r\n// method: req.method,\r\n// retryAfter: rlErr.msBeforeNext\r\n// });\r\n\r\n// res.setHeader('Retry-After', Math.ceil(rlErr.msBeforeNext / 1000));\r\n \r\n// return res.status(429).json({\r\n// success: false,\r\n// error: \"RATE_LIMIT_EXCEEDED\",\r\n// retryAfter: Math.ceil(rlErr.msBeforeNext / 1000),\r\n// message: finalOptions.message\r\n// });\r\n// }\r\n// };\r\n\r\n// } catch (err: any) {\r\n// logger.error(\"RLFlexibleAdapter: failed to initialize limiter\", {\r\n// error: err?.message || err\r\n// });\r\n// throw new AdapterError(\"RateLimiterFlexible creation failed.\");\r\n// }\r\n// }\r\n\r\n// private extractIP(req: any): string {\r\n// return (\r\n// req.headers['x-real-ip'] ||\r\n// req.headers['x-forwarded-for']?.split(',')[0]?.trim() ||\r\n// req.ip ||\r\n// req.connection?.remoteAddress ||\r\n// req.socket?.remoteAddress ||\r\n// 'unknown'\r\n// );\r\n// }\r\n// }\r\n\r\n\r\n\r\n\r\n\r\n\r\nimport { RateLimiterMemory, RateLimiterRes } from \"rate-limiter-flexible\";\r\nimport { logger } from \"../logging\";\r\nimport { AdapterError } from \"../core/errors/AdapterError\";\r\n\r\nexport interface RLOptions {\r\n points?: number;\r\n duration?: number;\r\n message?: any;\r\n blockDuration?: number;\r\n}\r\n\r\nexport class RLFlexibleAdapter {\r\n getMiddleware(options: RLOptions = {}) {\r\n try {\r\n const defaultOptions = {\r\n points: 100,\r\n duration: 60,\r\n message: \"Too many requests, slow down.\",\r\n blockDuration: 0\r\n };\r\n\r\n const finalOptions = { ...defaultOptions, ...options };\r\n\r\n const limiter = new RateLimiterMemory({\r\n points: finalOptions.points,\r\n duration: finalOptions.duration,\r\n blockDuration: finalOptions.blockDuration\r\n });\r\n\r\n \r\n logger.info(\"Rate limiter initialized\", {\r\n adapter: \"rate-limiter-flexible\",\r\n operation: \"init\",\r\n points: finalOptions.points,\r\n duration: finalOptions.duration,\r\n blockDuration: finalOptions.blockDuration\r\n });\r\n\r\n return async (req: any, res: any, next: any) => {\r\n const ip = this.extractIP(req);\r\n\r\n try {\r\n await limiter.consume(ip);\r\n next();\r\n } catch (err: any) {\r\n const rlErr = err as RateLimiterRes;\r\n\r\n logger.warn(\"Rate limit exceeded\", {\r\n adapter: \"rate-limiter-flexible\",\r\n operation: \"consume\",\r\n ip,\r\n method: req.method,\r\n path: req.path,\r\n retryAfterMs: rlErr.msBeforeNext\r\n });\r\n\r\n res.setHeader(\r\n \"Retry-After\",\r\n Math.ceil(rlErr.msBeforeNext / 1000)\r\n );\r\n\r\n return res.status(429).json({\r\n success: false,\r\n error: \"RATE_LIMIT_EXCEEDED\",\r\n retryAfter: Math.ceil(rlErr.msBeforeNext / 1000),\r\n message: finalOptions.message\r\n });\r\n }\r\n };\r\n } catch (err: any) {\r\n logger.error(\"Rate limiter initialization failed\", {\r\n adapter: \"rate-limiter-flexible\",\r\n operation: \"init\",\r\n reason: err?.message\r\n });\r\n\r\n throw new AdapterError(\"RateLimiterFlexible creation failed.\");\r\n }\r\n }\r\n\r\n private extractIP(req: any): string {\r\n return (\r\n req.headers[\"x-real-ip\"] ||\r\n req.headers[\"x-forwarded-for\"]?.split(\",\")[0]?.trim() ||\r\n req.ip ||\r\n req.connection?.remoteAddress ||\r\n req.socket?.remoteAddress ||\r\n \"unknown\"\r\n );\r\n }\r\n}\r\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SanitizeHtmlAdapter.d.ts","sourceRoot":"","sources":["../../src/adapters/SanitizeHtmlAdapter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"SanitizeHtmlAdapter.d.ts","sourceRoot":"","sources":["../../src/adapters/SanitizeHtmlAdapter.ts"],"names":[],"mappings":"AAmFA,OAAO,YAAY,MAAM,eAAe,CAAC;AAIzC,qBAAa,mBAAmB;IAC5B,OAAO,CAAC,aAAa,CAAwB;gBAEjC,OAAO,GAAE,YAAY,CAAC,QAAa;IAI/C,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,GAAG,GAAG,MAAM;IAmBrD,OAAO,CAAC,YAAY;IA+BpB,UAAU,CAAC,cAAc,CAAC,EAAE,GAAG,IACnB,KAAK,GAAG,EAAE,MAAM,GAAG,EAAE,MAAM,GAAG;CAyB7C"}
|
|
@@ -1,12 +1,75 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
// import sanitizeHtml from "sanitize-html";
|
|
3
|
+
// import { AdapterError } from "../core/errors/AdapterError.js";
|
|
4
|
+
// import { logger } from "../logging/index.js";
|
|
2
5
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
6
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
7
|
};
|
|
5
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
9
|
exports.SanitizeHtmlAdapter = void 0;
|
|
10
|
+
// export class SanitizeHtmlAdapter {
|
|
11
|
+
// private globalOptions: sanitizeHtml.IOptions;
|
|
12
|
+
// constructor(options: sanitizeHtml.IOptions = {}) {
|
|
13
|
+
// this.globalOptions = options;
|
|
14
|
+
// }
|
|
15
|
+
// sanitize(input: string, dynamicOptions?: any): string {
|
|
16
|
+
// try {
|
|
17
|
+
// const opts = { ...this.globalOptions, ...(dynamicOptions || {}) };
|
|
18
|
+
// const clean = sanitizeHtml(input, opts);
|
|
19
|
+
// return typeof clean === "string" ? clean : String(clean);
|
|
20
|
+
// } catch (err: any) {
|
|
21
|
+
// logger.error("sanitize-html failed", {
|
|
22
|
+
// error: err?.message || err,
|
|
23
|
+
// preview: typeof input === "string" ? input.slice(0, 100) : undefined
|
|
24
|
+
// });
|
|
25
|
+
// throw new AdapterError("sanitize-html adapter failed.");
|
|
26
|
+
// }
|
|
27
|
+
// }
|
|
28
|
+
// // Deep Sanitization - Recursively
|
|
29
|
+
// private deepSanitize(obj: any, dynamicOptions?: any, visited = new WeakSet()): any {
|
|
30
|
+
// if (obj && typeof obj === "object") {
|
|
31
|
+
// if (visited.has(obj)) {
|
|
32
|
+
// return obj;
|
|
33
|
+
// }
|
|
34
|
+
// visited.add(obj);
|
|
35
|
+
// }
|
|
36
|
+
// if (typeof obj === "string") {
|
|
37
|
+
// return this.sanitize(obj, dynamicOptions);
|
|
38
|
+
// }
|
|
39
|
+
// if (Array.isArray(obj)) {
|
|
40
|
+
// return obj.map((item) => this.deepSanitize(item, dynamicOptions, visited));
|
|
41
|
+
// }
|
|
42
|
+
// if (obj && typeof obj === "object") {
|
|
43
|
+
// const result: any = {};
|
|
44
|
+
// for (const key of Object.keys(obj)) {
|
|
45
|
+
// result[key] = this.deepSanitize(obj[key], dynamicOptions, visited);
|
|
46
|
+
// }
|
|
47
|
+
// return result;
|
|
48
|
+
// }
|
|
49
|
+
// return obj;
|
|
50
|
+
// }
|
|
51
|
+
// middleware(dynamicOptions?: any) {
|
|
52
|
+
// return (req: any, _res: any, next: any) => {
|
|
53
|
+
// try {
|
|
54
|
+
// if (req.body) {
|
|
55
|
+
// req.body = this.deepSanitize(req.body, dynamicOptions);
|
|
56
|
+
// logger.debug("sanitize-html applied", {
|
|
57
|
+
// keys: Object.keys(req.body)
|
|
58
|
+
// });
|
|
59
|
+
// }
|
|
60
|
+
// next();
|
|
61
|
+
// } catch (err: any) {
|
|
62
|
+
// logger.error("sanitize-html middleware failed", {
|
|
63
|
+
// error: err?.message || err
|
|
64
|
+
// });
|
|
65
|
+
// next(err);
|
|
66
|
+
// }
|
|
67
|
+
// };
|
|
68
|
+
// }
|
|
69
|
+
// }
|
|
7
70
|
const sanitize_html_1 = __importDefault(require("sanitize-html"));
|
|
8
|
-
const
|
|
9
|
-
const
|
|
71
|
+
const AdapterError_1 = require("../core/errors/AdapterError");
|
|
72
|
+
const logging_1 = require("../logging");
|
|
10
73
|
class SanitizeHtmlAdapter {
|
|
11
74
|
constructor(options = {}) {
|
|
12
75
|
this.globalOptions = options;
|
|
@@ -18,26 +81,26 @@ class SanitizeHtmlAdapter {
|
|
|
18
81
|
return typeof clean === "string" ? clean : String(clean);
|
|
19
82
|
}
|
|
20
83
|
catch (err) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
84
|
+
logging_1.logger.error("HTML sanitization failed", {
|
|
85
|
+
adapter: "sanitize-html",
|
|
86
|
+
operation: "sanitize",
|
|
87
|
+
reason: err?.message
|
|
24
88
|
});
|
|
25
|
-
throw new
|
|
89
|
+
throw new AdapterError_1.AdapterError("sanitize-html adapter failed.");
|
|
26
90
|
}
|
|
27
91
|
}
|
|
28
|
-
// Deep Sanitization -
|
|
92
|
+
// Deep Sanitization - recursively
|
|
29
93
|
deepSanitize(obj, dynamicOptions, visited = new WeakSet()) {
|
|
30
94
|
if (obj && typeof obj === "object") {
|
|
31
|
-
if (visited.has(obj))
|
|
95
|
+
if (visited.has(obj))
|
|
32
96
|
return obj;
|
|
33
|
-
}
|
|
34
97
|
visited.add(obj);
|
|
35
98
|
}
|
|
36
99
|
if (typeof obj === "string") {
|
|
37
100
|
return this.sanitize(obj, dynamicOptions);
|
|
38
101
|
}
|
|
39
102
|
if (Array.isArray(obj)) {
|
|
40
|
-
return obj.map(
|
|
103
|
+
return obj.map(item => this.deepSanitize(item, dynamicOptions, visited));
|
|
41
104
|
}
|
|
42
105
|
if (obj && typeof obj === "object") {
|
|
43
106
|
const result = {};
|
|
@@ -53,15 +116,20 @@ class SanitizeHtmlAdapter {
|
|
|
53
116
|
try {
|
|
54
117
|
if (req.body) {
|
|
55
118
|
req.body = this.deepSanitize(req.body, dynamicOptions);
|
|
56
|
-
|
|
119
|
+
// ✅ visible + safe info log
|
|
120
|
+
logging_1.logger.info("HTML sanitization applied", {
|
|
121
|
+
adapter: "sanitize-html",
|
|
122
|
+
operation: "middleware",
|
|
57
123
|
keys: Object.keys(req.body)
|
|
58
124
|
});
|
|
59
125
|
}
|
|
60
126
|
next();
|
|
61
127
|
}
|
|
62
128
|
catch (err) {
|
|
63
|
-
|
|
64
|
-
|
|
129
|
+
logging_1.logger.error("HTML sanitization middleware failed", {
|
|
130
|
+
adapter: "sanitize-html",
|
|
131
|
+
operation: "middleware",
|
|
132
|
+
reason: err?.message
|
|
65
133
|
});
|
|
66
134
|
next(err);
|
|
67
135
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SanitizeHtmlAdapter.js","sourceRoot":"","sources":["../../src/adapters/SanitizeHtmlAdapter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"SanitizeHtmlAdapter.js","sourceRoot":"","sources":["../../src/adapters/SanitizeHtmlAdapter.ts"],"names":[],"mappings":";AAAA,4CAA4C;AAC5C,iEAAiE;AACjE,gDAAgD;;;;;;AAEhD,qCAAqC;AACrC,oDAAoD;AAEpD,yDAAyD;AACzD,wCAAwC;AACxC,QAAQ;AAER,8DAA8D;AAC9D,gBAAgB;AAChB,iFAAiF;AAEjF,uDAAuD;AACvD,wEAAwE;AAExE,+BAA+B;AAC/B,qDAAqD;AACrD,8CAA8C;AAC9C,uFAAuF;AACvF,kBAAkB;AAElB,uEAAuE;AACvE,YAAY;AACZ,QAAQ;AAER,0CAA0C;AAC1C,2FAA2F;AAE3F,gDAAgD;AAChD,sCAAsC;AACtC,+BAA+B;AAC/B,gBAAgB;AAChB,gCAAgC;AAChC,YAAY;AAEZ,yCAAyC;AACzC,yDAAyD;AACzD,YAAY;AAEZ,oCAAoC;AACpC,0FAA0F;AAC1F,YAAY;AAEZ,gDAAgD;AAChD,sCAAsC;AACtC,oDAAoD;AACpD,sFAAsF;AACtF,gBAAgB;AAChB,6BAA6B;AAC7B,YAAY;AAEZ,sBAAsB;AACtB,QAAQ;AAER,yCAAyC;AACzC,uDAAuD;AACvD,oBAAoB;AACpB,kCAAkC;AAClC,8EAA8E;AAE9E,8DAA8D;AAC9D,sDAAsD;AACtD,0BAA0B;AAC1B,oBAAoB;AACpB,0BAA0B;AAE1B,mCAAmC;AACnC,oEAAoE;AACpE,iDAAiD;AACjD,sBAAsB;AACtB,6BAA6B;AAC7B,gBAAgB;AAChB,aAAa;AACb,QAAQ;AACR,IAAI;AAMJ,kEAAyC;AACzC,8DAA2D;AAC3D,wCAAoC;AAEpC,MAAa,mBAAmB;IAG5B,YAAY,UAAiC,EAAE;QAC3C,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC;IACjC,CAAC;IAED,QAAQ,CAAC,KAAa,EAAE,cAAoB;QACxC,IAAI,CAAC;YACD,MAAM,IAAI,GAAG,EAAE,GAAG,IAAI,CAAC,aAAa,EAAE,GAAG,CAAC,cAAc,IAAI,EAAE,CAAC,EAAE,CAAC;YAClE,MAAM,KAAK,GAAG,IAAA,uBAAY,EAAC,KAAK,EAAE,IAAI,CAAC,CAAC;YAExC,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAE7D,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAChB,gBAAM,CAAC,KAAK,CAAC,0BAA0B,EAAE;gBACrC,OAAO,EAAE,eAAe;gBACxB,SAAS,EAAE,UAAU;gBACrB,MAAM,EAAE,GAAG,EAAE,OAAO;aACvB,CAAC,CAAC;YAEH,MAAM,IAAI,2BAAY,CAAC,+BAA+B,CAAC,CAAC;QAC5D,CAAC;IACL,CAAC;IAED,kCAAkC;IAC1B,YAAY,CAAC,GAAQ,EAAE,cAAoB,EAAE,OAAO,GAAG,IAAI,OAAO,EAAE;QACxE,IAAI,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;YACjC,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,OAAO,GAAG,CAAC;YACjC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACrB,CAAC;QAED,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;YAC1B,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;QAC9C,CAAC;QAED,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YACrB,OAAO,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAClB,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,CAAC,CACnD,CAAC;QACN,CAAC;QAED,IAAI,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;YACjC,MAAM,MAAM,GAAQ,EAAE,CAAC;YACvB,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBACjC,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,YAAY,CAC3B,GAAG,CAAC,GAAG,CAAC,EACR,cAAc,EACd,OAAO,CACV,CAAC;YACN,CAAC;YACD,OAAO,MAAM,CAAC;QAClB,CAAC;QAED,OAAO,GAAG,CAAC;IACf,CAAC;IAED,UAAU,CAAC,cAAoB;QAC3B,OAAO,CAAC,GAAQ,EAAE,IAAS,EAAE,IAAS,EAAE,EAAE;YACtC,IAAI,CAAC;gBACD,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;oBACX,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;oBAEvD,4BAA4B;oBAC5B,gBAAM,CAAC,IAAI,CAAC,2BAA2B,EAAE;wBACrC,OAAO,EAAE,eAAe;wBACxB,SAAS,EAAE,YAAY;wBACvB,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;qBAC9B,CAAC,CAAC;gBACP,CAAC;gBAED,IAAI,EAAE,CAAC;YACX,CAAC;YAAC,OAAO,GAAQ,EAAE,CAAC;gBAChB,gBAAM,CAAC,KAAK,CAAC,qCAAqC,EAAE;oBAChD,OAAO,EAAE,eAAe;oBACxB,SAAS,EAAE,YAAY;oBACvB,MAAM,EAAE,GAAG,EAAE,OAAO;iBACvB,CAAC,CAAC;gBAEH,IAAI,CAAC,GAAG,CAAC,CAAC;YACd,CAAC;QACL,CAAC,CAAC;IACN,CAAC;CACJ;AAnFD,kDAmFC","sourcesContent":["// import sanitizeHtml from \"sanitize-html\";\r\n// import { AdapterError } from \"../core/errors/AdapterError.js\";\r\n// import { logger } from \"../logging/index.js\";\r\n\r\n// export class SanitizeHtmlAdapter {\r\n// private globalOptions: sanitizeHtml.IOptions;\r\n\r\n// constructor(options: sanitizeHtml.IOptions = {}) {\r\n// this.globalOptions = options;\r\n// }\r\n\r\n// sanitize(input: string, dynamicOptions?: any): string {\r\n// try {\r\n// const opts = { ...this.globalOptions, ...(dynamicOptions || {}) };\r\n\r\n// const clean = sanitizeHtml(input, opts);\r\n// return typeof clean === \"string\" ? clean : String(clean);\r\n\r\n// } catch (err: any) {\r\n// logger.error(\"sanitize-html failed\", {\r\n// error: err?.message || err,\r\n// preview: typeof input === \"string\" ? input.slice(0, 100) : undefined\r\n// });\r\n\r\n// throw new AdapterError(\"sanitize-html adapter failed.\");\r\n// }\r\n// }\r\n\r\n// // Deep Sanitization - Recursively \r\n// private deepSanitize(obj: any, dynamicOptions?: any, visited = new WeakSet()): any {\r\n \r\n// if (obj && typeof obj === \"object\") {\r\n// if (visited.has(obj)) {\r\n// return obj; \r\n// }\r\n// visited.add(obj);\r\n// }\r\n\r\n// if (typeof obj === \"string\") {\r\n// return this.sanitize(obj, dynamicOptions);\r\n// }\r\n\r\n// if (Array.isArray(obj)) {\r\n// return obj.map((item) => this.deepSanitize(item, dynamicOptions, visited));\r\n// }\r\n\r\n// if (obj && typeof obj === \"object\") {\r\n// const result: any = {};\r\n// for (const key of Object.keys(obj)) {\r\n// result[key] = this.deepSanitize(obj[key], dynamicOptions, visited);\r\n// }\r\n// return result;\r\n// }\r\n\r\n// return obj;\r\n// }\r\n\r\n// middleware(dynamicOptions?: any) {\r\n// return (req: any, _res: any, next: any) => {\r\n// try {\r\n// if (req.body) {\r\n// req.body = this.deepSanitize(req.body, dynamicOptions);\r\n\r\n// logger.debug(\"sanitize-html applied\", {\r\n// keys: Object.keys(req.body)\r\n// });\r\n// }\r\n// next();\r\n\r\n// } catch (err: any) {\r\n// logger.error(\"sanitize-html middleware failed\", {\r\n// error: err?.message || err\r\n// });\r\n// next(err);\r\n// }\r\n// };\r\n// }\r\n// }\r\n\r\n\r\n\r\n\r\n\r\nimport sanitizeHtml from \"sanitize-html\";\r\nimport { AdapterError } from \"../core/errors/AdapterError\";\r\nimport { logger } from \"../logging\";\r\n\r\nexport class SanitizeHtmlAdapter {\r\n private globalOptions: sanitizeHtml.IOptions;\r\n\r\n constructor(options: sanitizeHtml.IOptions = {}) {\r\n this.globalOptions = options;\r\n }\r\n\r\n sanitize(input: string, dynamicOptions?: any): string {\r\n try {\r\n const opts = { ...this.globalOptions, ...(dynamicOptions || {}) };\r\n const clean = sanitizeHtml(input, opts);\r\n\r\n return typeof clean === \"string\" ? clean : String(clean);\r\n\r\n } catch (err: any) {\r\n logger.error(\"HTML sanitization failed\", {\r\n adapter: \"sanitize-html\",\r\n operation: \"sanitize\",\r\n reason: err?.message\r\n });\r\n\r\n throw new AdapterError(\"sanitize-html adapter failed.\");\r\n }\r\n }\r\n\r\n // Deep Sanitization - recursively\r\n private deepSanitize(obj: any, dynamicOptions?: any, visited = new WeakSet()): any {\r\n if (obj && typeof obj === \"object\") {\r\n if (visited.has(obj)) return obj;\r\n visited.add(obj);\r\n }\r\n\r\n if (typeof obj === \"string\") {\r\n return this.sanitize(obj, dynamicOptions);\r\n }\r\n\r\n if (Array.isArray(obj)) {\r\n return obj.map(item =>\r\n this.deepSanitize(item, dynamicOptions, visited)\r\n );\r\n }\r\n\r\n if (obj && typeof obj === \"object\") {\r\n const result: any = {};\r\n for (const key of Object.keys(obj)) {\r\n result[key] = this.deepSanitize(\r\n obj[key],\r\n dynamicOptions,\r\n visited\r\n );\r\n }\r\n return result;\r\n }\r\n\r\n return obj;\r\n }\r\n\r\n middleware(dynamicOptions?: any) {\r\n return (req: any, _res: any, next: any) => {\r\n try {\r\n if (req.body) {\r\n req.body = this.deepSanitize(req.body, dynamicOptions);\r\n\r\n // ✅ visible + safe info log\r\n logger.info(\"HTML sanitization applied\", {\r\n adapter: \"sanitize-html\",\r\n operation: \"middleware\",\r\n keys: Object.keys(req.body)\r\n });\r\n }\r\n\r\n next();\r\n } catch (err: any) {\r\n logger.error(\"HTML sanitization middleware failed\", {\r\n adapter: \"sanitize-html\",\r\n operation: \"middleware\",\r\n reason: err?.message\r\n });\r\n\r\n next(err);\r\n }\r\n };\r\n }\r\n}\r\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"XSSAdapter.d.ts","sourceRoot":"","sources":["../../src/adapters/XSSAdapter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"XSSAdapter.d.ts","sourceRoot":"","sources":["../../src/adapters/XSSAdapter.ts"],"names":[],"mappings":"AAqJA,OAAO,EAAkC,SAAS,EAAE,MAAM,KAAK,CAAC;AAIhE,MAAM,WAAW,UAAU;IACvB,SAAS,CAAC,EAAE,OAAO,SAAS,CAAC;IAC7B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,GAAG,CAAC,EAAE,OAAO,GAAG;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;IAC3C,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,KAAK,MAAM,CAAC;IAC5D,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,KAAK,MAAM,CAAC;IACvF,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,KAAK,MAAM,CAAC;IAClE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACtB;AAED,qBAAa,UAAU;IACnB,OAAO,CAAC,aAAa,CAAa;IAClC,OAAO,CAAC,aAAa,CAAY;gBAErB,OAAO,GAAE,UAAe;IAwBpC,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,UAAU,GAAG,MAAM;IAwB5D,UAAU,CAAC,cAAc,CAAC,EAAE,UAAU,IAC1B,KAAK,GAAG,EAAE,MAAM,GAAG,EAAE,MAAM,GAAG;IA8C1C,OAAO,CAAC,YAAY;CAwBvB"}
|