hi-secure 1.0.6 → 1.0.10
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 +1 -1
- package/dist/adapters/ArgonAdapter.js.map +1 -1
- package/dist/adapters/ExpressRLAdapter.d.ts.map +1 -1
- package/dist/adapters/ExpressRLAdapter.js +1 -2
- package/dist/adapters/ExpressRLAdapter.js.map +1 -1
- package/dist/adapters/ExpressValidatorAdapter.d.ts.map +1 -1
- package/dist/adapters/ExpressValidatorAdapter.js +1 -39
- package/dist/adapters/ExpressValidatorAdapter.js.map +1 -1
- package/dist/adapters/GoogleAdapter.d.ts.map +1 -1
- package/dist/adapters/GoogleAdapter.js +0 -101
- package/dist/adapters/GoogleAdapter.js.map +1 -1
- package/dist/adapters/JWTAdapter.d.ts.map +1 -1
- package/dist/adapters/JWTAdapter.js +3 -210
- package/dist/adapters/JWTAdapter.js.map +1 -1
- package/dist/adapters/RLFlexibleAdapter.d.ts.map +1 -1
- package/dist/adapters/RLFlexibleAdapter.js +0 -52
- package/dist/adapters/RLFlexibleAdapter.js.map +1 -1
- package/dist/adapters/SanitizeHtmlAdapter.d.ts +0 -3
- package/dist/adapters/SanitizeHtmlAdapter.d.ts.map +1 -1
- package/dist/adapters/SanitizeHtmlAdapter.js +2 -71
- package/dist/adapters/SanitizeHtmlAdapter.js.map +1 -1
- package/dist/adapters/XSSAdapter.d.ts +0 -10
- package/dist/adapters/XSSAdapter.d.ts.map +1 -1
- package/dist/adapters/XSSAdapter.js +2 -19
- package/dist/adapters/XSSAdapter.js.map +1 -1
- package/dist/adapters/ZodAdapter.d.ts.map +1 -1
- package/dist/adapters/ZodAdapter.js +2 -6
- package/dist/adapters/ZodAdapter.js.map +1 -1
- package/dist/core/HiSecure.d.ts +15 -2
- package/dist/core/HiSecure.d.ts.map +1 -1
- package/dist/core/HiSecure.js +130 -37
- package/dist/core/HiSecure.js.map +1 -1
- package/dist/core/useSecure.d.ts +4 -0
- package/dist/core/useSecure.d.ts.map +1 -1
- package/dist/core/useSecure.js +19 -114
- package/dist/core/useSecure.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -19
- package/dist/index.js.map +1 -1
- package/dist/managers/AuthManager.d.ts.map +1 -1
- package/dist/managers/AuthManager.js +1 -89
- package/dist/managers/AuthManager.js.map +1 -1
- package/dist/managers/CorsManager.d.ts.map +1 -1
- package/dist/managers/CorsManager.js +1 -19
- package/dist/managers/CorsManager.js.map +1 -1
- package/dist/managers/HashManager.d.ts.map +1 -1
- package/dist/managers/HashManager.js +0 -243
- package/dist/managers/HashManager.js.map +1 -1
- package/dist/managers/JsonManager.d.ts.map +1 -1
- package/dist/managers/JsonManager.js +1 -77
- package/dist/managers/JsonManager.js.map +1 -1
- package/dist/managers/RateLimitManager.d.ts.map +1 -1
- package/dist/managers/RateLimitManager.js +3 -17
- package/dist/managers/RateLimitManager.js.map +1 -1
- package/dist/managers/SanitizerManager.d.ts +0 -6
- package/dist/managers/SanitizerManager.d.ts.map +1 -1
- package/dist/managers/SanitizerManager.js +1 -213
- package/dist/managers/SanitizerManager.js.map +1 -1
- package/dist/managers/ValidatorManager.d.ts.map +1 -1
- package/dist/managers/ValidatorManager.js +1 -109
- package/dist/managers/ValidatorManager.js.map +1 -1
- package/dist/middlewares/errorHandler.d.ts.map +1 -1
- package/dist/middlewares/errorHandler.js +0 -19
- package/dist/middlewares/errorHandler.js.map +1 -1
- package/dist/utils/deepFreeze.d.ts.map +1 -1
- package/dist/utils/deepFreeze.js +0 -25
- package/dist/utils/deepFreeze.js.map +1 -1
- package/dist/utils/deepMerge.d.ts.map +1 -1
- package/dist/utils/deepMerge.js +0 -26
- package/dist/utils/deepMerge.js.map +1 -1
- package/dist/utils/normalizeOptions.d.ts +1 -3
- package/dist/utils/normalizeOptions.d.ts.map +1 -1
- package/dist/utils/normalizeOptions.js +0 -1
- package/dist/utils/normalizeOptions.js.map +1 -1
- package/package.json +1 -1
- package/src/adapters/ArgonAdapter.ts +1 -1
- package/src/adapters/ExpressRLAdapter.ts +1 -2
- package/src/adapters/ExpressValidatorAdapter.ts +1 -54
- package/src/adapters/GoogleAdapter.ts +0 -129
- package/src/adapters/JWTAdapter.ts +5 -259
- package/src/adapters/RLFlexibleAdapter.ts +2 -65
- package/src/adapters/SanitizeHtmlAdapter.ts +3 -87
- package/src/adapters/XSSAdapter.ts +11 -19
- package/src/adapters/ZodAdapter.ts +2 -51
- package/src/core/HiSecure.ts +25 -36
- package/src/core/useSecure.ts +5 -7
- package/src/index.ts +4 -5
- package/src/managers/AuthManager.ts +5 -109
- package/src/managers/CorsManager.ts +1 -25
- package/src/managers/HashManager.ts +0 -286
- package/src/managers/JsonManager.ts +1 -91
- package/src/managers/RateLimitManager.ts +3 -262
- package/src/managers/SanitizerManager.ts +4 -263
- package/src/managers/ValidatorManager.ts +53 -187
- package/src/middlewares/errorHandler.ts +1 -176
- package/src/utils/deepFreeze.ts +0 -32
- package/src/utils/deepMerge.ts +0 -35
- package/src/utils/normalizeOptions.ts +16 -133
- package/src/examples/e1.ts +0 -1
- package/src/test/t1.ts +0 -1
|
@@ -1,56 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// import { RateLimiterMemory, RateLimiterRes } from "rate-limiter-flexible";
|
|
3
|
-
// import { logger } from "../logging";
|
|
4
|
-
// import { AdapterError } from "../core/errors/AdapterError";
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.RLFlexibleAdapter = void 0;
|
|
7
|
-
// export class RLFlexibleAdapter {
|
|
8
|
-
// /**
|
|
9
|
-
// * Create middleware dynamically using options.
|
|
10
|
-
// */
|
|
11
|
-
// getMiddleware(options: {
|
|
12
|
-
// points?: number;
|
|
13
|
-
// duration?: number; // seconds
|
|
14
|
-
// message?: any;
|
|
15
|
-
// } = {}) {
|
|
16
|
-
// try {
|
|
17
|
-
// const limiter = new RateLimiterMemory({
|
|
18
|
-
// points: options.points ?? 100,
|
|
19
|
-
// duration: options.duration ?? 60,
|
|
20
|
-
// });
|
|
21
|
-
// return async (req: any, res: any, next: any) => {
|
|
22
|
-
// const ip = req.ip ||
|
|
23
|
-
// req.headers["x-forwarded-for"] ||
|
|
24
|
-
// req.connection?.remoteAddress ||
|
|
25
|
-
// "unknown";
|
|
26
|
-
// try {
|
|
27
|
-
// await limiter.consume(ip);
|
|
28
|
-
// next();
|
|
29
|
-
// } catch (err: any) {
|
|
30
|
-
// const rlErr = err as RateLimiterRes;
|
|
31
|
-
// logger.warn("⚠ RLFlexibleAdapter: rate limit exceeded", {
|
|
32
|
-
// ip,
|
|
33
|
-
// path: req.path,
|
|
34
|
-
// method: req.method,
|
|
35
|
-
// retryAfter: rlErr.msBeforeNext
|
|
36
|
-
// });
|
|
37
|
-
// return res.status(429).json({
|
|
38
|
-
// success: false,
|
|
39
|
-
// error: "RATE_LIMIT_EXCEEDED",
|
|
40
|
-
// retryAfter: Math.ceil(rlErr.msBeforeNext / 1000),
|
|
41
|
-
// message: options.message ?? "Too many requests, slow down."
|
|
42
|
-
// });
|
|
43
|
-
// }
|
|
44
|
-
// };
|
|
45
|
-
// } catch (err: any) {
|
|
46
|
-
// logger.error("❌ RLFlexibleAdapter: failed to initialize limiter", {
|
|
47
|
-
// error: err?.message || err
|
|
48
|
-
// });
|
|
49
|
-
// throw new AdapterError("RateLimiterFlexible creation failed.");
|
|
50
|
-
// }
|
|
51
|
-
// }
|
|
52
|
-
// }
|
|
53
|
-
// src/adapters/RLFlexibleAdapter.ts - IMPROVED
|
|
54
4
|
const rate_limiter_flexible_1 = require("rate-limiter-flexible");
|
|
55
5
|
const index_js_1 = require("../logging/index.js");
|
|
56
6
|
const AdapterError_js_1 = require("../core/errors/AdapterError.js");
|
|
@@ -70,7 +20,6 @@ class RLFlexibleAdapter {
|
|
|
70
20
|
blockDuration: finalOptions.blockDuration
|
|
71
21
|
});
|
|
72
22
|
return async (req, res, next) => {
|
|
73
|
-
// Better IP extraction
|
|
74
23
|
const ip = this.extractIP(req);
|
|
75
24
|
try {
|
|
76
25
|
await limiter.consume(ip);
|
|
@@ -102,7 +51,6 @@ class RLFlexibleAdapter {
|
|
|
102
51
|
}
|
|
103
52
|
}
|
|
104
53
|
extractIP(req) {
|
|
105
|
-
// Order of priority for IP extraction
|
|
106
54
|
return (req.headers['x-real-ip'] ||
|
|
107
55
|
req.headers['x-forwarded-for']?.split(',')[0]?.trim() ||
|
|
108
56
|
req.ip ||
|
|
@@ -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":";;;AAAA,iEAA0E;AAC1E,kDAA6C;AAC7C,oEAA8D;AAS9D,MAAa,iBAAiB;IAC1B,aAAa,CAAC,UAAqB,EAAE;QACjC,IAAI,CAAC;YACD,MAAM,cAAc,GAAG;gBACnB,MAAM,EAAE,GAAG;gBACX,QAAQ,EAAE,EAAE;gBACZ,OAAO,EAAE,+BAA+B;gBACxC,aAAa,EAAE,CAAC;aACnB,CAAC;YAEF,MAAM,YAAY,GAAG,EAAE,GAAG,cAAc,EAAE,GAAG,OAAO,EAAE,CAAC;YAEvD,MAAM,OAAO,GAAG,IAAI,yCAAiB,CAAC;gBAClC,MAAM,EAAE,YAAY,CAAC,MAAM;gBAC3B,QAAQ,EAAE,YAAY,CAAC,QAAQ;gBAC/B,aAAa,EAAE,YAAY,CAAC,aAAa;aAC5C,CAAC,CAAC;YAEH,OAAO,KAAK,EAAE,GAAQ,EAAE,GAAQ,EAAE,IAAS,EAAE,EAAE;gBAE3C,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;gBAE/B,IAAI,CAAC;oBACD,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;oBAC1B,IAAI,EAAE,CAAC;gBACX,CAAC;gBAAC,OAAO,GAAQ,EAAE,CAAC;oBAChB,MAAM,KAAK,GAAG,GAAqB,CAAC;oBAEpC,iBAAM,CAAC,IAAI,CAAC,0CAA0C,EAAE;wBACpD,EAAE;wBACF,IAAI,EAAE,GAAG,CAAC,IAAI;wBACd,MAAM,EAAE,GAAG,CAAC,MAAM;wBAClB,UAAU,EAAE,KAAK,CAAC,YAAY;qBACjC,CAAC,CAAC;oBAEH,GAAG,CAAC,SAAS,CAAC,aAAa,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC,CAAC;oBAEnE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;wBACxB,OAAO,EAAE,KAAK;wBACd,KAAK,EAAE,qBAAqB;wBAC5B,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC;wBAChD,OAAO,EAAE,YAAY,CAAC,OAAO;qBAChC,CAAC,CAAC;gBACP,CAAC;YACL,CAAC,CAAC;QAEN,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAChB,iBAAM,CAAC,KAAK,CAAC,mDAAmD,EAAE;gBAC9D,KAAK,EAAE,GAAG,EAAE,OAAO,IAAI,GAAG;aAC7B,CAAC,CAAC;YACH,MAAM,IAAI,8BAAY,CAAC,sCAAsC,CAAC,CAAC;QACnE,CAAC;IACL,CAAC;IAEO,SAAS,CAAC,GAAQ;QACtB,OAAO,CACH,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC;YACxB,GAAG,CAAC,OAAO,CAAC,iBAAiB,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE;YACrD,GAAG,CAAC,EAAE;YACN,GAAG,CAAC,UAAU,EAAE,aAAa;YAC7B,GAAG,CAAC,MAAM,EAAE,aAAa;YACzB,SAAS,CACZ,CAAC;IACN,CAAC;CACJ;AAhED,8CAgEC","sourcesContent":["import { RateLimiterMemory, RateLimiterRes } from \"rate-limiter-flexible\";\r\nimport { logger } from \"../logging/index.js\";\r\nimport { AdapterError } from \"../core/errors/AdapterError.js\";\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 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}"]}
|
|
@@ -3,9 +3,6 @@ export declare class SanitizeHtmlAdapter {
|
|
|
3
3
|
private globalOptions;
|
|
4
4
|
constructor(options?: sanitizeHtml.IOptions);
|
|
5
5
|
sanitize(input: string, dynamicOptions?: any): string;
|
|
6
|
-
/**
|
|
7
|
-
* Deep sanitize with recursion protection
|
|
8
|
-
*/
|
|
9
6
|
private deepSanitize;
|
|
10
7
|
middleware(dynamicOptions?: any): (req: any, _res: any, next: any) => void;
|
|
11
8
|
}
|
|
@@ -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":"AAAA,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;IAkBrD,OAAO,CAAC,YAAY;IA4BpB,UAAU,CAAC,cAAc,CAAC,EAAE,GAAG,IACnB,KAAK,GAAG,EAAE,MAAM,GAAG,EAAE,MAAM,GAAG;CAmB7C"}
|
|
@@ -1,75 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// import sanitizeHtml from "sanitize-html";
|
|
3
|
-
// import { AdapterError } from "../core/errors/AdapterError";
|
|
4
|
-
// import { logger } from "../logging";
|
|
5
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
6
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
7
4
|
};
|
|
8
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
6
|
exports.SanitizeHtmlAdapter = void 0;
|
|
10
|
-
// export class SanitizeHtmlAdapter {
|
|
11
|
-
// private globalOptions: sanitizeHtml.IOptions;
|
|
12
|
-
// constructor(options: sanitizeHtml.IOptions = {}) {
|
|
13
|
-
// this.globalOptions = options;
|
|
14
|
-
// }
|
|
15
|
-
// /**
|
|
16
|
-
// * Sanitize a string with merged global + dynamic options
|
|
17
|
-
// */
|
|
18
|
-
// sanitize(input: string, dynamicOptions?: any): string {
|
|
19
|
-
// try {
|
|
20
|
-
// const opts = { ...this.globalOptions, ...(dynamicOptions || {}) };
|
|
21
|
-
// const clean = sanitizeHtml(input, opts);
|
|
22
|
-
// return typeof clean === "string" ? clean : String(clean);
|
|
23
|
-
// } catch (err: any) {
|
|
24
|
-
// logger.error("❌ sanitize-html failed", {
|
|
25
|
-
// error: err?.message || err,
|
|
26
|
-
// preview: typeof input === "string" ? input.slice(0, 100) : undefined
|
|
27
|
-
// });
|
|
28
|
-
// throw new AdapterError("sanitize-html adapter failed.");
|
|
29
|
-
// }
|
|
30
|
-
// }
|
|
31
|
-
// /**
|
|
32
|
-
// * Deep sanitize nested objects, arrays, strings
|
|
33
|
-
// */
|
|
34
|
-
// private deepSanitize(obj: any, dynamicOptions?: any): any {
|
|
35
|
-
// if (typeof obj === "string") {
|
|
36
|
-
// return this.sanitize(obj, dynamicOptions);
|
|
37
|
-
// }
|
|
38
|
-
// if (Array.isArray(obj)) {
|
|
39
|
-
// return obj.map((item) => this.deepSanitize(item, dynamicOptions));
|
|
40
|
-
// }
|
|
41
|
-
// if (obj && typeof obj === "object") {
|
|
42
|
-
// const result: any = {};
|
|
43
|
-
// for (const key of Object.keys(obj)) {
|
|
44
|
-
// result[key] = this.deepSanitize(obj[key], dynamicOptions);
|
|
45
|
-
// }
|
|
46
|
-
// return result;
|
|
47
|
-
// }
|
|
48
|
-
// return obj;
|
|
49
|
-
// }
|
|
50
|
-
// /**
|
|
51
|
-
// * Middleware wrapper with dynamic per-route options
|
|
52
|
-
// */
|
|
53
|
-
// middleware(dynamicOptions?: any) {
|
|
54
|
-
// return (req: any, _res: any, next: any) => {
|
|
55
|
-
// try {
|
|
56
|
-
// if (req.body) {
|
|
57
|
-
// req.body = this.deepSanitize(req.body, dynamicOptions);
|
|
58
|
-
// logger.debug("🧼 sanitize-html applied", {
|
|
59
|
-
// keys: Object.keys(req.body)
|
|
60
|
-
// });
|
|
61
|
-
// }
|
|
62
|
-
// next();
|
|
63
|
-
// } catch (err: any) {
|
|
64
|
-
// logger.error("❌ sanitize-html middleware failed", {
|
|
65
|
-
// error: err?.message || err
|
|
66
|
-
// });
|
|
67
|
-
// next(err);
|
|
68
|
-
// }
|
|
69
|
-
// };
|
|
70
|
-
// }
|
|
71
|
-
// }
|
|
72
|
-
// src/adapters/SanitizeHtmlAdapter.ts - FIXED
|
|
73
7
|
const sanitize_html_1 = __importDefault(require("sanitize-html"));
|
|
74
8
|
const AdapterError_js_1 = require("../core/errors/AdapterError.js");
|
|
75
9
|
const index_js_1 = require("../logging/index.js");
|
|
@@ -91,14 +25,11 @@ class SanitizeHtmlAdapter {
|
|
|
91
25
|
throw new AdapterError_js_1.AdapterError("sanitize-html adapter failed.");
|
|
92
26
|
}
|
|
93
27
|
}
|
|
94
|
-
|
|
95
|
-
* Deep sanitize with recursion protection
|
|
96
|
-
*/
|
|
28
|
+
// Deep Sanitization - Recursively
|
|
97
29
|
deepSanitize(obj, dynamicOptions, visited = new WeakSet()) {
|
|
98
|
-
// Handle circular references
|
|
99
30
|
if (obj && typeof obj === "object") {
|
|
100
31
|
if (visited.has(obj)) {
|
|
101
|
-
return obj;
|
|
32
|
+
return obj;
|
|
102
33
|
}
|
|
103
34
|
visited.add(obj);
|
|
104
35
|
}
|
|
@@ -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":"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","sourcesContent":["import sanitizeHtml from \"sanitize-html\";\r\nimport { AdapterError } from \"../core/errors/AdapterError.js\";\r\nimport { logger } from \"../logging/index.js\";\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\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}"]}
|
|
@@ -16,18 +16,8 @@ export declare class XSSAdapter {
|
|
|
16
16
|
private globalOptions;
|
|
17
17
|
private defaultFilter;
|
|
18
18
|
constructor(options?: XSSOptions);
|
|
19
|
-
/**
|
|
20
|
-
* Sanitize a string with global + dynamic merged options
|
|
21
|
-
*/
|
|
22
19
|
sanitize(input: string, dynamicOptions?: XSSOptions): string;
|
|
23
|
-
/**
|
|
24
|
-
* Middleware wrapper WITH dynamic options
|
|
25
|
-
* Doesn't mutate original request - creates sanitized copy
|
|
26
|
-
*/
|
|
27
20
|
middleware(dynamicOptions?: XSSOptions): (req: any, _res: any, next: any) => void;
|
|
28
|
-
/**
|
|
29
|
-
* Deep sanitize nested objects/arrays
|
|
30
|
-
*/
|
|
31
21
|
private deepSanitize;
|
|
32
22
|
}
|
|
33
23
|
//# sourceMappingURL=XSSAdapter.d.ts.map
|
|
@@ -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":"AAAA,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;IA2B5D,UAAU,CAAC,cAAc,CAAC,EAAE,UAAU,IAC1B,KAAK,GAAG,EAAE,MAAM,GAAG,EAAE,MAAM,GAAG;IA6C1C,OAAO,CAAC,YAAY;CA2BvB"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.XSSAdapter = void 0;
|
|
4
|
-
// src/adapters/XSSAdapter.ts - NEW FILE
|
|
5
4
|
const xss_1 = require("xss");
|
|
6
5
|
const AdapterError_js_1 = require("../core/errors/AdapterError.js");
|
|
7
6
|
const index_js_1 = require("../logging/index.js");
|
|
@@ -11,12 +10,11 @@ class XSSAdapter {
|
|
|
11
10
|
// Default safe configuration
|
|
12
11
|
const defaultOptions = {
|
|
13
12
|
whiteList: (0, xss_1.getDefaultWhiteList)(),
|
|
14
|
-
stripIgnoreTag: true,
|
|
13
|
+
stripIgnoreTag: true,
|
|
15
14
|
stripIgnoreTagBody: ['script', 'style', 'iframe', 'object', 'embed'],
|
|
16
15
|
allowCommentTag: false,
|
|
17
|
-
css: false,
|
|
16
|
+
css: false,
|
|
18
17
|
onTag: (tag, html, options) => {
|
|
19
|
-
// Add noopener/noreferrer to links for security
|
|
20
18
|
if (tag === 'a') {
|
|
21
19
|
return html.replace(/<a /i, '<a target="_blank" rel="noopener noreferrer" ');
|
|
22
20
|
}
|
|
@@ -26,19 +24,14 @@ class XSSAdapter {
|
|
|
26
24
|
const finalOptions = { ...defaultOptions, ...options };
|
|
27
25
|
this.defaultFilter = new xss_1.FilterXSS(finalOptions);
|
|
28
26
|
}
|
|
29
|
-
/**
|
|
30
|
-
* Sanitize a string with global + dynamic merged options
|
|
31
|
-
*/
|
|
32
27
|
sanitize(input, dynamicOptions) {
|
|
33
28
|
try {
|
|
34
29
|
if (typeof input !== "string") {
|
|
35
30
|
return input;
|
|
36
31
|
}
|
|
37
|
-
// If no dynamic options, use default filter
|
|
38
32
|
if (!dynamicOptions || Object.keys(dynamicOptions).length === 0) {
|
|
39
33
|
return this.defaultFilter.process(input);
|
|
40
34
|
}
|
|
41
|
-
// Merge options for this specific call
|
|
42
35
|
const mergedOptions = { ...this.globalOptions, ...dynamicOptions };
|
|
43
36
|
const customFilter = new xss_1.FilterXSS(mergedOptions);
|
|
44
37
|
return customFilter.process(input);
|
|
@@ -51,10 +44,6 @@ class XSSAdapter {
|
|
|
51
44
|
throw new AdapterError_js_1.AdapterError("XSS sanitizer failed.");
|
|
52
45
|
}
|
|
53
46
|
}
|
|
54
|
-
/**
|
|
55
|
-
* Middleware wrapper WITH dynamic options
|
|
56
|
-
* Doesn't mutate original request - creates sanitized copy
|
|
57
|
-
*/
|
|
58
47
|
middleware(dynamicOptions) {
|
|
59
48
|
return (req, _res, next) => {
|
|
60
49
|
try {
|
|
@@ -72,14 +61,12 @@ class XSSAdapter {
|
|
|
72
61
|
: v);
|
|
73
62
|
}
|
|
74
63
|
else if (val && typeof val === "object") {
|
|
75
|
-
// Handle nested objects (simple recursion)
|
|
76
64
|
sanitizedBody[key] = this.deepSanitize(val, dynamicOptions);
|
|
77
65
|
}
|
|
78
66
|
else {
|
|
79
67
|
sanitizedBody[key] = val;
|
|
80
68
|
}
|
|
81
69
|
}
|
|
82
|
-
// Store sanitized version separately
|
|
83
70
|
req.sanitizedBody = sanitizedBody;
|
|
84
71
|
index_js_1.logger.debug("🛡️ XSS sanitizer applied", {
|
|
85
72
|
originalKeys: Object.keys(originalBody),
|
|
@@ -96,11 +83,7 @@ class XSSAdapter {
|
|
|
96
83
|
}
|
|
97
84
|
};
|
|
98
85
|
}
|
|
99
|
-
/**
|
|
100
|
-
* Deep sanitize nested objects/arrays
|
|
101
|
-
*/
|
|
102
86
|
deepSanitize(obj, options, visited = new WeakSet()) {
|
|
103
|
-
// Handle circular references
|
|
104
87
|
if (obj && typeof obj === "object") {
|
|
105
88
|
if (visited.has(obj)) {
|
|
106
89
|
return obj;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"XSSAdapter.js","sourceRoot":"","sources":["../../src/adapters/XSSAdapter.ts"],"names":[],"mappings":";;;AAAA,
|
|
1
|
+
{"version":3,"file":"XSSAdapter.js","sourceRoot":"","sources":["../../src/adapters/XSSAdapter.ts"],"names":[],"mappings":"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","sourcesContent":["import { FilterXSS, getDefaultWhiteList, whiteList } from 'xss';\r\nimport { AdapterError } from \"../core/errors/AdapterError.js\";\r\nimport { logger } from \"../logging/index.js\";\r\n\r\nexport interface XSSOptions {\r\n whiteList?: typeof whiteList;\r\n stripIgnoreTag?: boolean;\r\n stripIgnoreTagBody?: string[];\r\n allowCommentTag?: boolean;\r\n css?: boolean | { [key: string]: boolean };\r\n onTag?: (tag: string, html: string, options: any) => string;\r\n onTagAttr?: (tag: string, name: string, value: string, isWhiteAttr: boolean) => string;\r\n onIgnoreTag?: (tag: string, html: string, options: any) => string;\r\n [key: string]: any;\r\n}\r\n\r\nexport class XSSAdapter {\r\n private globalOptions: XSSOptions;\r\n private defaultFilter: FilterXSS;\r\n\r\n constructor(options: XSSOptions = {}) {\r\n this.globalOptions = options;\r\n \r\n // Default safe configuration\r\n const defaultOptions: XSSOptions = {\r\n whiteList: getDefaultWhiteList(),\r\n stripIgnoreTag: true, \r\n stripIgnoreTagBody: ['script', 'style', 'iframe', 'object', 'embed'],\r\n allowCommentTag: false,\r\n css: false, \r\n onTag: (tag, html, options) => {\r\n \r\n if (tag === 'a') {\r\n return html.replace(/<a /i, '<a target=\"_blank\" rel=\"noopener noreferrer\" ');\r\n }\r\n return html;\r\n }\r\n };\r\n\r\n const finalOptions = { ...defaultOptions, ...options };\r\n this.defaultFilter = new FilterXSS(finalOptions);\r\n }\r\n\r\n \r\n sanitize(input: string, dynamicOptions?: XSSOptions): string {\r\n try {\r\n if (typeof input !== \"string\") {\r\n return input as any;\r\n }\r\n\r\n \r\n if (!dynamicOptions || Object.keys(dynamicOptions).length === 0) {\r\n return this.defaultFilter.process(input);\r\n }\r\n\r\n \r\n const mergedOptions = { ...this.globalOptions, ...dynamicOptions };\r\n const customFilter = new FilterXSS(mergedOptions);\r\n \r\n return customFilter.process(input);\r\n\r\n } catch (err: any) {\r\n logger.error(\"❌ XSS sanitizer failed\", {\r\n error: err?.message,\r\n preview: input?.slice?.(0, 80)\r\n });\r\n throw new AdapterError(\"XSS sanitizer failed.\");\r\n }\r\n }\r\n\r\n \r\n middleware(dynamicOptions?: XSSOptions) {\r\n return (req: any, _res: any, next: any) => {\r\n try {\r\n if (req.body && typeof req.body === \"object\") {\r\n const originalBody = req.body;\r\n const sanitizedBody: any = Array.isArray(originalBody) ? [] : {};\r\n \r\n for (const key of Object.keys(originalBody)) {\r\n const val = originalBody[key];\r\n\r\n if (typeof val === \"string\") {\r\n sanitizedBody[key] = this.sanitize(val, dynamicOptions);\r\n } else if (Array.isArray(val)) {\r\n sanitizedBody[key] = val.map((v) =>\r\n typeof v === \"string\"\r\n ? this.sanitize(v, dynamicOptions)\r\n : v\r\n );\r\n } else if (val && typeof val === \"object\") {\r\n \r\n sanitizedBody[key] = this.deepSanitize(val, dynamicOptions);\r\n } else {\r\n sanitizedBody[key] = val;\r\n }\r\n }\r\n \r\n \r\n req.sanitizedBody = sanitizedBody;\r\n \r\n logger.debug(\"🛡️ XSS sanitizer applied\", {\r\n originalKeys: Object.keys(originalBody),\r\n sanitizedKeys: Object.keys(sanitizedBody)\r\n });\r\n }\r\n\r\n next();\r\n } catch (err: any) {\r\n logger.error(\"❌ XSS 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 private deepSanitize(obj: any, options?: XSSOptions, 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, options);\r\n }\r\n\r\n if (Array.isArray(obj)) {\r\n return obj.map(item => this.deepSanitize(item, options, 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], options, visited);\r\n }\r\n return result;\r\n }\r\n\r\n return obj;\r\n }\r\n}"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ZodAdapter.d.ts","sourceRoot":"","sources":["../../src/adapters/ZodAdapter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ZodAdapter.d.ts","sourceRoot":"","sources":["../../src/adapters/ZodAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAY,MAAM,KAAK,CAAC;AAI1C,qBAAa,UAAU;IACnB,OAAO,CAAC,YAAY,CAAC,CAAY;gBAErB,YAAY,CAAC,EAAE,SAAS;IAIpC,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,IACtB,KAAK,GAAG,EAAE,KAAK,GAAG,EAAE,MAAM,GAAG;CA6B5C"}
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// import { ZodSchema, ZodError } from "zod";
|
|
3
|
-
// import { ValidationError } from "../core/errors/ValidationError";
|
|
4
|
-
// import { logger } from "../logging";
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.ZodAdapter = void 0;
|
|
7
|
-
const ValidationError_js_1 = require("../core/errors/ValidationError.js");
|
|
4
|
+
const ValidationError_js_1 = require("../core/errors/ValidationError.js");
|
|
8
5
|
const index_js_1 = require("../logging/index.js");
|
|
9
6
|
class ZodAdapter {
|
|
10
7
|
constructor(globalSchema) {
|
|
@@ -30,8 +27,7 @@ class ZodAdapter {
|
|
|
30
27
|
issues,
|
|
31
28
|
preview: JSON.stringify(req.body).slice(0, 200)
|
|
32
29
|
});
|
|
33
|
-
return next(new ValidationError_js_1.ValidationError("Validation failed.", issues)
|
|
34
|
-
);
|
|
30
|
+
return next(new ValidationError_js_1.ValidationError("Validation failed.", issues));
|
|
35
31
|
};
|
|
36
32
|
}
|
|
37
33
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ZodAdapter.js","sourceRoot":"","sources":["../../src/adapters/ZodAdapter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ZodAdapter.js","sourceRoot":"","sources":["../../src/adapters/ZodAdapter.ts"],"names":[],"mappings":";;;AACA,0EAAoE;AACpE,kDAA6C;AAE7C,MAAa,UAAU;IAGnB,YAAY,YAAwB;QAChC,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;IACrC,CAAC;IAED,QAAQ,CAAC,aAAyB;QAC9B,OAAO,CAAC,GAAQ,EAAE,GAAQ,EAAE,IAAS,EAAE,EAAE;YACrC,MAAM,MAAM,GAAG,aAAa,IAAI,IAAI,CAAC,YAAY,CAAC;YAElD,IAAI,CAAC,MAAM;gBAAE,OAAO,IAAI,EAAE,CAAC;YAE3B,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAE1C,IAAI,MAAM,CAAC,OAAO;gBAAE,OAAO,IAAI,EAAE,CAAC;YAElC,MAAM,MAAM,GAAa,MAAM,CAAC,KAAK,CAAC;YAEtC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;gBACvC,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;gBAC1B,IAAI,EAAE,KAAK,CAAC,IAAI;aACnB,CAAC,CAAC,CAAC;YAEJ,iBAAM,CAAC,IAAI,CAAC,yBAAyB,EAAE;gBACnC,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,MAAM;gBACN,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;aAClD,CAAC,CAAC;YAEH,OAAO,IAAI,CACP,IAAI,oCAAe,CAAC,oBAAoB,EAAE,MAAa,CAAC,CAC3D,CAAC;QACN,CAAC,CAAC;IACN,CAAC;CACJ;AArCD,gCAqCC","sourcesContent":["import { ZodSchema, ZodError } from \"zod\";\r\nimport { ValidationError } from \"../core/errors/ValidationError.js\"; \r\nimport { logger } from \"../logging/index.js\";\r\n\r\nexport class ZodAdapter {\r\n private globalSchema?: ZodSchema;\r\n\r\n constructor(globalSchema?: ZodSchema) {\r\n this.globalSchema = globalSchema;\r\n }\r\n\r\n validate(dynamicSchema?: ZodSchema) {\r\n return (req: any, res: any, next: any) => {\r\n const schema = dynamicSchema || this.globalSchema;\r\n\r\n if (!schema) return next();\r\n\r\n const result = schema.safeParse(req.body);\r\n\r\n if (result.success) return next();\r\n\r\n const zodErr: ZodError = result.error;\r\n\r\n const issues = zodErr.issues.map(issue => ({\r\n message: issue.message,\r\n path: issue.path.join(\".\"),\r\n code: issue.code\r\n }));\r\n\r\n logger.warn(\"⚠ Zod validation failed\", {\r\n path: req.path,\r\n method: req.method,\r\n issues,\r\n preview: JSON.stringify(req.body).slice(0, 200)\r\n });\r\n\r\n return next(\r\n new ValidationError(\"Validation failed.\", issues as any) \r\n );\r\n };\r\n }\r\n}"]}
|
package/dist/core/HiSecure.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { SanitizerManager } from "../managers/SanitizerManager.js";
|
|
|
6
6
|
import { JsonManager } from "../managers/JsonManager.js";
|
|
7
7
|
import { CorsManager } from "../managers/CorsManager.js";
|
|
8
8
|
import { AuthManager } from "../managers/AuthManager.js";
|
|
9
|
-
import { ValidationSchema } from "./types/SecureOptions.js";
|
|
9
|
+
import { SecureOptions, ValidationSchema } from "./types/SecureOptions.js";
|
|
10
10
|
export declare class HiSecure {
|
|
11
11
|
private static instance;
|
|
12
12
|
private config;
|
|
@@ -22,23 +22,36 @@ export declare class HiSecure {
|
|
|
22
22
|
private hashingFallback;
|
|
23
23
|
private rateLimiterPrimary;
|
|
24
24
|
private rateLimiterFallback;
|
|
25
|
+
private sanitizerPrimary;
|
|
26
|
+
private sanitizerFallback;
|
|
25
27
|
private constructor();
|
|
26
28
|
static getInstance(config?: Partial<HiSecureConfig>): HiSecure;
|
|
27
29
|
static resetInstance(): void;
|
|
28
30
|
init(): void;
|
|
31
|
+
isInitialized(): boolean;
|
|
29
32
|
static auth(options?: {
|
|
30
33
|
required?: boolean;
|
|
31
34
|
roles?: string[];
|
|
32
35
|
}): (req: import("express").Request, res: import("express").Response, next: import("express").NextFunction) => void;
|
|
33
36
|
static validate(schema: ValidationSchema): (req: any, res: any, next: any) => any;
|
|
34
37
|
static sanitize(options?: any): (req: any, _res: any, next: any) => void;
|
|
38
|
+
static rateLimit(preset: "strict" | "relaxed" | "api" | object): any;
|
|
35
39
|
static cors(options?: any): (req: import("cors").CorsRequest, res: {
|
|
36
40
|
statusCode?: number | undefined;
|
|
37
41
|
setHeader(key: string, value: string): any;
|
|
38
42
|
end(): any;
|
|
39
43
|
}, next: (err?: any) => any) => void;
|
|
40
|
-
static rateLimit(preset: "strict" | "relaxed" | "api" | object): any;
|
|
41
44
|
static json(options?: any): import("connect").NextHandleFunction[];
|
|
45
|
+
static hash(password: string): Promise<string>;
|
|
46
|
+
static verify(password: string, hash: string): Promise<boolean>;
|
|
47
|
+
static jwt: {
|
|
48
|
+
sign: (payload: object, options?: any) => string;
|
|
49
|
+
verify: (token: string) => string | import("jsonwebtoken").Jwt | import("jsonwebtoken").JwtPayload;
|
|
50
|
+
google: {
|
|
51
|
+
verifyIdToken: (idToken: string) => Promise<import("../adapters/GoogleAdapter.js").GoogleTokenPayload>;
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
static middleware(options?: SecureOptions | "api" | "strict" | "public"): any[];
|
|
42
55
|
private setupAdapters;
|
|
43
56
|
private setupManagers;
|
|
44
57
|
private setupDynamicManagers;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HiSecure.d.ts","sourceRoot":"","sources":["../../src/core/HiSecure.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"HiSecure.d.ts","sourceRoot":"","sources":["../../src/core/HiSecure.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAkB3D,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AASzD,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAC,MAAM,0BAA0B,CAAC;AAE1E,qBAAa,QAAQ;IACjB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAyB;IAChD,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,WAAW,CAAS;IAGrB,WAAW,EAAG,WAAW,CAAC;IAC1B,gBAAgB,EAAG,gBAAgB,CAAC;IACpC,gBAAgB,EAAG,gBAAgB,CAAC;IACpC,gBAAgB,EAAG,gBAAgB,CAAC;IACpC,WAAW,EAAG,WAAW,CAAC;IAC1B,WAAW,EAAG,WAAW,CAAC;IAC1B,WAAW,CAAC,EAAE,WAAW,CAAC;IAGjC,OAAO,CAAC,cAAc,CAAM;IAC5B,OAAO,CAAC,eAAe,CAAM;IAC7B,OAAO,CAAC,kBAAkB,CAAM;IAChC,OAAO,CAAC,mBAAmB,CAAM;IACjC,OAAO,CAAC,gBAAgB,CAAM;IAC9B,OAAO,CAAC,iBAAiB,CAAM;IAG/B,OAAO;IAMP,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,QAAQ;IAQ9D,MAAM,CAAC,aAAa,IAAI,IAAI;IAI5B,IAAI,IAAI,IAAI;IAyBZ,aAAa,IAAI,OAAO;IAMxB,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE;IAQ9D,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,gBAAgB;IAIxC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,GAAG;IAI7B,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,QAAQ,GAAG,SAAS,GAAG,KAAK,GAAG,MAAM;IAe9D,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,GAAG;kBA7GJ,CAAC;;;iBAID,CAAC;IA6GtB,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,GAAG;WAUZ,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;WAMvC,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIrE,MAAM,CAAC,GAAG;wBACU,MAAM,YAAY,GAAG;wBAQrB,MAAM;;qCASO,MAAM;;MAQrC;IAIF,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,aAAa,GAAG,KAAK,GAAG,QAAQ,GAAG,QAAQ;IAuBvE,OAAO,CAAC,aAAa;IAoCrB,OAAO,CAAC,aAAa;IA2BrB,OAAO,CAAC,oBAAoB;IAmB5B,OAAO,CAAC,qBAAqB;CAmDhC"}
|