@riocrypto/common-server 1.0.2275 → 1.0.2278
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/build/helpers/sanitize-bank-account-doc.d.ts +1 -0
- package/build/helpers/sanitize-crypto-address-doc.d.ts +1 -0
- package/build/helpers/sanitize-order-doc.d.ts +1 -0
- package/build/helpers/sanitize-user-doc.d.ts +1 -0
- package/build/index.d.ts +2 -0
- package/build/index.js +2 -0
- package/build/middlewares/dynamic-rate-limiter.d.ts +10 -0
- package/build/middlewares/dynamic-rate-limiter.js +156 -0
- package/build/models/admin-webauthn-credential.d.ts +1 -0
- package/build/models/custom-rate-limit.d.ts +22 -0
- package/build/models/custom-rate-limit.js +65 -0
- package/build/models/webauthn-credential.d.ts +1 -0
- package/build/services/logger.js +38 -23
- package/package.json +2 -1
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
/// <reference types="mongoose/types/virtuals" />
|
|
48
48
|
/// <reference types="mongoose" />
|
|
49
49
|
/// <reference types="mongoose/types/inferschematype" />
|
|
50
|
+
/// <reference types="mongoose/types/inferrawdoctype" />
|
|
50
51
|
/// <reference types="mongoose/types/inferschematype" />
|
|
51
52
|
/// <reference types="mongoose/types/inferrawdoctype" />
|
|
52
53
|
import { BankAccountDoc } from "@riocrypto/common-server";
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
/// <reference types="mongoose/types/virtuals" />
|
|
48
48
|
/// <reference types="mongoose" />
|
|
49
49
|
/// <reference types="mongoose/types/inferschematype" />
|
|
50
|
+
/// <reference types="mongoose/types/inferrawdoctype" />
|
|
50
51
|
/// <reference types="mongoose/types/inferschematype" />
|
|
51
52
|
/// <reference types="mongoose/types/inferrawdoctype" />
|
|
52
53
|
import { CryptoAddressDoc } from "@riocrypto/common-server";
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
/// <reference types="mongoose/types/virtuals" />
|
|
48
48
|
/// <reference types="mongoose" />
|
|
49
49
|
/// <reference types="mongoose/types/inferschematype" />
|
|
50
|
+
/// <reference types="mongoose/types/inferrawdoctype" />
|
|
50
51
|
/// <reference types="mongoose/types/inferschematype" />
|
|
51
52
|
/// <reference types="mongoose/types/inferrawdoctype" />
|
|
52
53
|
import { OrderDoc } from "@riocrypto/common-server";
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
/// <reference types="mongoose/types/virtuals" />
|
|
48
48
|
/// <reference types="mongoose" />
|
|
49
49
|
/// <reference types="mongoose/types/inferschematype" />
|
|
50
|
+
/// <reference types="mongoose/types/inferrawdoctype" />
|
|
50
51
|
/// <reference types="mongoose/types/inferschematype" />
|
|
51
52
|
/// <reference types="mongoose/types/inferrawdoctype" />
|
|
52
53
|
import { UserDoc } from "@riocrypto/common-server";
|
package/build/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export * from "./middlewares/set-trace";
|
|
|
7
7
|
export * from "./middlewares/require-min-role";
|
|
8
8
|
export * from "./middlewares/require-min-admin-role";
|
|
9
9
|
export * from "./middlewares/verify-csrf-token";
|
|
10
|
+
export * from "./middlewares/dynamic-rate-limiter";
|
|
10
11
|
export * from "./services/apiKey";
|
|
11
12
|
export * from "./services/password";
|
|
12
13
|
export * from "./services/logger";
|
|
@@ -91,6 +92,7 @@ export * from "./models/STP-settings";
|
|
|
91
92
|
export * from "./models/dashboard-push-notification-subscription";
|
|
92
93
|
export * from "./models/webauthn-credential";
|
|
93
94
|
export * from "./models/admin-webauthn-credential";
|
|
95
|
+
export * from "./models/custom-rate-limit";
|
|
94
96
|
export * from "./clients/axios-with-logging";
|
|
95
97
|
export * from "./clients/slack-client";
|
|
96
98
|
export * from "./clients/fireblocks-client";
|
package/build/index.js
CHANGED
|
@@ -23,6 +23,7 @@ __exportStar(require("./middlewares/set-trace"), exports);
|
|
|
23
23
|
__exportStar(require("./middlewares/require-min-role"), exports);
|
|
24
24
|
__exportStar(require("./middlewares/require-min-admin-role"), exports);
|
|
25
25
|
__exportStar(require("./middlewares/verify-csrf-token"), exports);
|
|
26
|
+
__exportStar(require("./middlewares/dynamic-rate-limiter"), exports);
|
|
26
27
|
__exportStar(require("./services/apiKey"), exports);
|
|
27
28
|
__exportStar(require("./services/password"), exports);
|
|
28
29
|
__exportStar(require("./services/logger"), exports);
|
|
@@ -107,6 +108,7 @@ __exportStar(require("./models/STP-settings"), exports);
|
|
|
107
108
|
__exportStar(require("./models/dashboard-push-notification-subscription"), exports);
|
|
108
109
|
__exportStar(require("./models/webauthn-credential"), exports);
|
|
109
110
|
__exportStar(require("./models/admin-webauthn-credential"), exports);
|
|
111
|
+
__exportStar(require("./models/custom-rate-limit"), exports);
|
|
110
112
|
__exportStar(require("./clients/axios-with-logging"), exports);
|
|
111
113
|
__exportStar(require("./clients/slack-client"), exports);
|
|
112
114
|
__exportStar(require("./clients/fireblocks-client"), exports);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from "express";
|
|
2
|
+
import { Options as RateLimitOptions } from "express-rate-limit";
|
|
3
|
+
import { Mongoose } from "mongoose";
|
|
4
|
+
export interface DynamicRateLimiterOptions extends Partial<RateLimitOptions> {
|
|
5
|
+
windowMs: number;
|
|
6
|
+
max: number;
|
|
7
|
+
mongooseConnection: Mongoose;
|
|
8
|
+
cacheTtlMs?: number;
|
|
9
|
+
}
|
|
10
|
+
export declare function createDynamicRateLimiter(options: DynamicRateLimiterOptions): (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
12
|
+
var t = {};
|
|
13
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
14
|
+
t[p] = s[p];
|
|
15
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
16
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
17
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
18
|
+
t[p[i]] = s[p[i]];
|
|
19
|
+
}
|
|
20
|
+
return t;
|
|
21
|
+
};
|
|
22
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
23
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.createDynamicRateLimiter = void 0;
|
|
27
|
+
const express_rate_limit_1 = __importDefault(require("express-rate-limit"));
|
|
28
|
+
// Removed path-to-regexp import
|
|
29
|
+
// Adjust the import path according to your project structure
|
|
30
|
+
const custom_rate_limit_1 = require("../models/custom-rate-limit");
|
|
31
|
+
// Cache for rate limiter instances
|
|
32
|
+
const limiterInstanceCache = new Map();
|
|
33
|
+
// Cache for compiled RegExp objects from path strings
|
|
34
|
+
// Key: regex string pattern, Value: compiled RegExp object
|
|
35
|
+
const regexCache = new Map();
|
|
36
|
+
// In-memory cache for custom IP rules
|
|
37
|
+
let customIpRulesCache = new Map();
|
|
38
|
+
let lastCacheRefreshTime = 0;
|
|
39
|
+
const DEFAULT_CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
|
|
40
|
+
function refreshCustomIpRulesCache(CustomRateLimit, filePathForLog) {
|
|
41
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
42
|
+
try {
|
|
43
|
+
const allRules = yield CustomRateLimit.find({}).lean();
|
|
44
|
+
const newCache = new Map();
|
|
45
|
+
allRules.forEach((rule) => {
|
|
46
|
+
if (rule.ipAddress) {
|
|
47
|
+
// Pre-compile and cache regexes when rules are loaded/refreshed
|
|
48
|
+
if (rule.routes && Array.isArray(rule.routes)) {
|
|
49
|
+
rule.routes.forEach((routeRule) => {
|
|
50
|
+
// Use any temporarily if lean typing is tricky
|
|
51
|
+
if (typeof routeRule.path === "string" &&
|
|
52
|
+
!regexCache.has(routeRule.path)) {
|
|
53
|
+
try {
|
|
54
|
+
regexCache.set(routeRule.path, new RegExp(routeRule.path));
|
|
55
|
+
}
|
|
56
|
+
catch (e) {
|
|
57
|
+
console.error(`[DynamicRateLimiter Refresh] Failed to compile regex: '${routeRule.path}' for IP ${rule.ipAddress}. Skipping rule path. Error:`, e);
|
|
58
|
+
// Optionally mark the rule path as invalid or handle differently
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
newCache.set(rule.ipAddress, rule);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
customIpRulesCache = newCache;
|
|
67
|
+
lastCacheRefreshTime = Date.now();
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
console.error(`[DynamicRateLimiter ${filePathForLog}] Error refreshing custom IP rules cache:`, error);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
function createDynamicRateLimiter(options) {
|
|
75
|
+
const { windowMs: defaultWindowMs, max: defaultMax, mongooseConnection, cacheTtlMs = DEFAULT_CACHE_TTL_MS, message, standardHeaders, legacyHeaders, skip, requestPropertyName } = options, otherRateLimitOptions = __rest(options, ["windowMs", "max", "mongooseConnection", "cacheTtlMs", "message", "standardHeaders", "legacyHeaders", "skip", "requestPropertyName"]);
|
|
76
|
+
if (!mongooseConnection) {
|
|
77
|
+
throw new Error("[DynamicRateLimiter] Mongoose connection is required.");
|
|
78
|
+
}
|
|
79
|
+
const CustomRateLimit = (0, custom_rate_limit_1.buildCustomRateLimit)(mongooseConnection);
|
|
80
|
+
// Initial cache load (regex compilation happens here too)
|
|
81
|
+
refreshCustomIpRulesCache(CustomRateLimit, "initialization");
|
|
82
|
+
return (req, res, next) => __awaiter(this, void 0, void 0, function* () {
|
|
83
|
+
try {
|
|
84
|
+
// Refresh cache if stale
|
|
85
|
+
if (Date.now() - lastCacheRefreshTime > cacheTtlMs ||
|
|
86
|
+
(customIpRulesCache.size === 0 && lastCacheRefreshTime === 0)) {
|
|
87
|
+
yield refreshCustomIpRulesCache(CustomRateLimit, req.path);
|
|
88
|
+
}
|
|
89
|
+
let clientIp = req.ip;
|
|
90
|
+
let normalizedIp;
|
|
91
|
+
if (clientIp) {
|
|
92
|
+
// Normalize IPv4-mapped IPv6 addresses (::ffff:x.x.x.x)
|
|
93
|
+
if (clientIp.startsWith("::ffff:")) {
|
|
94
|
+
normalizedIp = clientIp.substring(7);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
normalizedIp = clientIp;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
let currentLimit = defaultMax;
|
|
101
|
+
let currentWindowMs = defaultWindowMs;
|
|
102
|
+
let ruleIdentifier = "default"; // Start with default identifier
|
|
103
|
+
let customRuleDoc;
|
|
104
|
+
let matchingRule = undefined;
|
|
105
|
+
if (normalizedIp) {
|
|
106
|
+
customRuleDoc = customIpRulesCache.get(normalizedIp);
|
|
107
|
+
}
|
|
108
|
+
if (customRuleDoc &&
|
|
109
|
+
customRuleDoc.routes &&
|
|
110
|
+
customRuleDoc.routes.length > 0) {
|
|
111
|
+
// Find the first specific rule that matches the request path and method
|
|
112
|
+
matchingRule = customRuleDoc.routes.find((rule) => {
|
|
113
|
+
const methodMatch = rule.action === "*" || rule.action === req.method.toUpperCase();
|
|
114
|
+
if (!methodMatch)
|
|
115
|
+
return false;
|
|
116
|
+
const regex = regexCache.get(rule.path);
|
|
117
|
+
if (!regex)
|
|
118
|
+
return false; // Should have been compiled on refresh
|
|
119
|
+
return regex.test(req.path);
|
|
120
|
+
});
|
|
121
|
+
if (matchingRule) {
|
|
122
|
+
// Apply limit and window from the *specific matching rule*
|
|
123
|
+
currentLimit = matchingRule.limit;
|
|
124
|
+
currentWindowMs = matchingRule.windowMs;
|
|
125
|
+
// Create a more specific identifier including the matched rule path/action
|
|
126
|
+
ruleIdentifier = `custom_ip:${normalizedIp}_rule:${matchingRule.action}:${matchingRule.path}`;
|
|
127
|
+
}
|
|
128
|
+
// If no specific rule in the array matches, we just fall through
|
|
129
|
+
// and the defaults (currentLimit = defaultMax etc.) will be used.
|
|
130
|
+
}
|
|
131
|
+
// If customRuleDoc is undefined or routes array is empty, defaults apply.
|
|
132
|
+
// Generate cache key for the limiter instance. This will be unique for:
|
|
133
|
+
// - Default limit (if no rule found/matched)
|
|
134
|
+
// - Each specific matching rule (due to unique ruleIdentifier)
|
|
135
|
+
const limiterCacheKey = `${currentLimit}-${currentWindowMs}-${ruleIdentifier}`;
|
|
136
|
+
let selectedLimiter = limiterInstanceCache.get(limiterCacheKey);
|
|
137
|
+
if (!selectedLimiter) {
|
|
138
|
+
// Create a new limiter instance with the determined limit/window
|
|
139
|
+
selectedLimiter = (0, express_rate_limit_1.default)(Object.assign({ windowMs: currentWindowMs, max: currentLimit, keyGenerator: (request) => {
|
|
140
|
+
let ipForKey = request.ip;
|
|
141
|
+
if (ipForKey === null || ipForKey === void 0 ? void 0 : ipForKey.startsWith("::ffff:")) {
|
|
142
|
+
ipForKey = ipForKey.substring(7);
|
|
143
|
+
}
|
|
144
|
+
return ipForKey || "unknown_ip_for_rate_limit";
|
|
145
|
+
}, message: message, standardHeaders: standardHeaders, legacyHeaders: legacyHeaders, skip: skip, requestPropertyName: requestPropertyName }, otherRateLimitOptions));
|
|
146
|
+
limiterInstanceCache.set(limiterCacheKey, selectedLimiter);
|
|
147
|
+
}
|
|
148
|
+
return selectedLimiter(req, res, next);
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
console.error(`[DynamicRateLimiter ${req.path}] Error in rate limiting middleware (IP: ${req.ip}):`, error);
|
|
152
|
+
next();
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
exports.createDynamicRateLimiter = createDynamicRateLimiter;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import mongoose from "mongoose";
|
|
2
|
+
interface RouteRule {
|
|
3
|
+
path: string;
|
|
4
|
+
action: string;
|
|
5
|
+
limit: number;
|
|
6
|
+
windowMs: number;
|
|
7
|
+
}
|
|
8
|
+
interface CustomRateLimitAttrs {
|
|
9
|
+
createdAt: Date;
|
|
10
|
+
ipAddress: string;
|
|
11
|
+
routes: RouteRule[];
|
|
12
|
+
}
|
|
13
|
+
interface CustomRateLimitDoc extends mongoose.Document {
|
|
14
|
+
createdAt: Date;
|
|
15
|
+
ipAddress: string;
|
|
16
|
+
routes: RouteRule[];
|
|
17
|
+
}
|
|
18
|
+
interface CustomRateLimitModel extends mongoose.Model<CustomRateLimitDoc> {
|
|
19
|
+
build(attrs: CustomRateLimitAttrs): CustomRateLimitDoc;
|
|
20
|
+
}
|
|
21
|
+
declare const buildCustomRateLimit: (mongoose: typeof mongoose) => CustomRateLimitModel;
|
|
22
|
+
export { buildCustomRateLimit, CustomRateLimitDoc, CustomRateLimitAttrs, RouteRule, };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildCustomRateLimit = void 0;
|
|
4
|
+
const buildCustomRateLimit = (mongoose) => {
|
|
5
|
+
if (mongoose.models.CustomRateLimit) {
|
|
6
|
+
return mongoose.model("CustomRateLimit");
|
|
7
|
+
}
|
|
8
|
+
// Updated: Define the schema for the nested RouteRule object including limits
|
|
9
|
+
const RouteRuleSchema = new mongoose.Schema({
|
|
10
|
+
path: {
|
|
11
|
+
type: String,
|
|
12
|
+
required: [true, "Path (regex string) is required for a route rule"],
|
|
13
|
+
},
|
|
14
|
+
action: {
|
|
15
|
+
type: String,
|
|
16
|
+
required: [true, "Action (HTTP method) is required for a route rule"],
|
|
17
|
+
uppercase: true,
|
|
18
|
+
// enum: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', '*']
|
|
19
|
+
},
|
|
20
|
+
limit: {
|
|
21
|
+
type: Number,
|
|
22
|
+
required: [true, "Limit is required for a specific route rule"],
|
|
23
|
+
min: [0, "Limit cannot be negative"], // Allow 0 for blocking?
|
|
24
|
+
},
|
|
25
|
+
windowMs: {
|
|
26
|
+
type: Number,
|
|
27
|
+
required: [true, "windowMs is required for a specific route rule"],
|
|
28
|
+
min: [1000, "windowMs must be at least 1000 (1 second)"],
|
|
29
|
+
},
|
|
30
|
+
}, { _id: false });
|
|
31
|
+
const CustomRateLimitSchema = new mongoose.Schema({
|
|
32
|
+
createdAt: {
|
|
33
|
+
type: mongoose.Schema.Types.Date,
|
|
34
|
+
default: Date.now,
|
|
35
|
+
},
|
|
36
|
+
ipAddress: {
|
|
37
|
+
type: String,
|
|
38
|
+
required: true,
|
|
39
|
+
// Add index for faster IP lookups
|
|
40
|
+
index: true,
|
|
41
|
+
},
|
|
42
|
+
// Removed limit and windowMs from top level schema
|
|
43
|
+
routes: {
|
|
44
|
+
type: [RouteRuleSchema],
|
|
45
|
+
required: true,
|
|
46
|
+
// Default to empty array. If empty, default limiter settings apply.
|
|
47
|
+
default: [],
|
|
48
|
+
},
|
|
49
|
+
}, {
|
|
50
|
+
toJSON: {
|
|
51
|
+
transform(doc, ret) {
|
|
52
|
+
ret.id = ret._id;
|
|
53
|
+
delete ret._id;
|
|
54
|
+
delete ret.__v;
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
CustomRateLimitSchema.statics.build = (attrs) => {
|
|
59
|
+
var _a;
|
|
60
|
+
return new CustomRateLimit(Object.assign(Object.assign({}, attrs), { createdAt: (_a = attrs.createdAt) !== null && _a !== void 0 ? _a : new Date() }));
|
|
61
|
+
};
|
|
62
|
+
const CustomRateLimit = mongoose.model("CustomRateLimit", CustomRateLimitSchema);
|
|
63
|
+
return CustomRateLimit;
|
|
64
|
+
};
|
|
65
|
+
exports.buildCustomRateLimit = buildCustomRateLimit;
|
package/build/services/logger.js
CHANGED
|
@@ -14,8 +14,10 @@ const winston_1 = require("winston");
|
|
|
14
14
|
class LoggerService {
|
|
15
15
|
constructor() {
|
|
16
16
|
this.maskSensitiveData = (0, winston_1.format)((info) => {
|
|
17
|
+
var _a, _b, _c, _d, _e;
|
|
17
18
|
// Mask sensitive headers
|
|
18
|
-
if (info.meta
|
|
19
|
+
if ((_b = (_a = info.meta) === null || _a === void 0 ? void 0 : _a.req) === null || _b === void 0 ? void 0 : _b.headers) {
|
|
20
|
+
// Use optional chaining
|
|
19
21
|
const headers = info.meta.req.headers;
|
|
20
22
|
const headerKeysToMask = [
|
|
21
23
|
"x-api-key",
|
|
@@ -24,7 +26,8 @@ class LoggerService {
|
|
|
24
26
|
];
|
|
25
27
|
headerKeysToMask.forEach((key) => {
|
|
26
28
|
if (headers[key]) {
|
|
27
|
-
|
|
29
|
+
// Type assertion might be needed if headers[key] is not strictly string
|
|
30
|
+
headers[key] = this.maskValue(String(headers[key]));
|
|
28
31
|
}
|
|
29
32
|
});
|
|
30
33
|
// Mask cookies specifically
|
|
@@ -38,22 +41,26 @@ class LoggerService {
|
|
|
38
41
|
key === "refreshToken" ||
|
|
39
42
|
key === "adminRefreshToken" ||
|
|
40
43
|
key === "phoneVerifiedJWT") {
|
|
41
|
-
return `${key}=${this.maskValue(value, true)}`;
|
|
44
|
+
return `${key}=${this.maskValue(value, true)}`;
|
|
42
45
|
}
|
|
43
|
-
return cookie;
|
|
46
|
+
return cookie;
|
|
44
47
|
})
|
|
45
48
|
.join("; ");
|
|
46
49
|
}
|
|
47
50
|
}
|
|
48
51
|
// Mask sensitive request body fields
|
|
49
|
-
if (info.meta
|
|
52
|
+
if (((_d = (_c = info.meta) === null || _c === void 0 ? void 0 : _c.req) === null || _d === void 0 ? void 0 : _d.body) &&
|
|
53
|
+
typeof info.meta.req.body === "object" &&
|
|
54
|
+
info.meta.req.body !== null) {
|
|
55
|
+
const requestBody = info.meta.req.body; // Type assertion
|
|
50
56
|
const requestBodyFieldsToMaskWithLastFour = [
|
|
51
57
|
"accountNumber",
|
|
52
58
|
"routingNumber",
|
|
53
59
|
];
|
|
54
60
|
requestBodyFieldsToMaskWithLastFour.forEach((field) => {
|
|
55
|
-
if (
|
|
56
|
-
|
|
61
|
+
if (requestBody.hasOwnProperty(field) &&
|
|
62
|
+
typeof requestBody[field] === "string") {
|
|
63
|
+
requestBody[field] = this.maskValue(requestBody[field], true);
|
|
57
64
|
}
|
|
58
65
|
});
|
|
59
66
|
const requestBodyFieldsToMaskWithoutLastFour = [
|
|
@@ -64,26 +71,34 @@ class LoggerService {
|
|
|
64
71
|
"authenticatorCode",
|
|
65
72
|
];
|
|
66
73
|
requestBodyFieldsToMaskWithoutLastFour.forEach((field) => {
|
|
67
|
-
if (
|
|
68
|
-
|
|
74
|
+
if (requestBody.hasOwnProperty(field) &&
|
|
75
|
+
typeof requestBody[field] === "string") {
|
|
76
|
+
requestBody[field] = this.maskValue(requestBody[field]);
|
|
69
77
|
}
|
|
70
78
|
});
|
|
71
79
|
}
|
|
72
80
|
// Mask sensitive response body fields
|
|
73
|
-
|
|
81
|
+
const resAsAny = (_e = info.meta) === null || _e === void 0 ? void 0 : _e.res;
|
|
82
|
+
if ((resAsAny === null || resAsAny === void 0 ? void 0 : resAsAny.body) &&
|
|
83
|
+
typeof resAsAny.body === "object" &&
|
|
84
|
+
resAsAny.body !== null) {
|
|
85
|
+
const responseBody = resAsAny.body; // Keep this assertion for object access
|
|
74
86
|
const responseBodyFieldsToMask = ["secret", "value"];
|
|
75
87
|
responseBodyFieldsToMask.forEach((field) => {
|
|
76
|
-
if (
|
|
77
|
-
|
|
88
|
+
if (responseBody.hasOwnProperty(field) &&
|
|
89
|
+
typeof responseBody[field] === "string") {
|
|
90
|
+
responseBody[field] = this.maskValue(responseBody[field]);
|
|
78
91
|
}
|
|
79
92
|
});
|
|
80
93
|
}
|
|
81
94
|
return info;
|
|
82
95
|
});
|
|
83
96
|
this.requestMethodFilter = (0, winston_1.format)((info) => {
|
|
97
|
+
var _a, _b;
|
|
84
98
|
const allowedMethods = ["POST", "PATCH", "DELETE"];
|
|
85
99
|
// Only apply filter if req.method is defined
|
|
86
|
-
if (info.meta
|
|
100
|
+
if ((_b = (_a = info.meta) === null || _a === void 0 ? void 0 : _a.req) === null || _b === void 0 ? void 0 : _b.method) {
|
|
101
|
+
// Use optional chaining
|
|
87
102
|
if (allowedMethods.includes(info.meta.req.method)) {
|
|
88
103
|
return info;
|
|
89
104
|
}
|
|
@@ -96,41 +111,41 @@ class LoggerService {
|
|
|
96
111
|
});
|
|
97
112
|
// Custom format to handle CustomError serialization
|
|
98
113
|
this.handleCustomErrorFormat = (0, winston_1.format)((info) => {
|
|
114
|
+
var _a, _b, _c;
|
|
99
115
|
let error;
|
|
100
116
|
// express-winston errorLogger often puts the error in info.message or info.meta.err
|
|
101
117
|
if (info.message instanceof Error) {
|
|
102
118
|
error = info.message;
|
|
103
119
|
}
|
|
104
|
-
else if (info.meta
|
|
120
|
+
else if (((_a = info.meta) === null || _a === void 0 ? void 0 : _a.err) instanceof Error) {
|
|
121
|
+
// Use optional chaining
|
|
105
122
|
error = info.meta.err;
|
|
106
123
|
}
|
|
107
124
|
else if (info.level === "error" &&
|
|
108
125
|
info.message &&
|
|
109
126
|
typeof info.message === "object" &&
|
|
110
|
-
info.message.stack
|
|
111
|
-
|
|
127
|
+
info.message.stack // Type assertion to check for stack
|
|
128
|
+
) {
|
|
112
129
|
error = info.message;
|
|
113
130
|
}
|
|
114
131
|
if (error instanceof common_1.CustomError) {
|
|
115
|
-
// Replace the error object/message with serialized details
|
|
116
|
-
// Log serialized errors under 'errorDetails' and keep original message if it's just a string
|
|
117
132
|
info.errorDetails = error.serializeErrors();
|
|
118
133
|
if (typeof info.message === "object") {
|
|
119
|
-
info.message = error.message;
|
|
134
|
+
info.message = error.message;
|
|
120
135
|
}
|
|
121
|
-
|
|
122
|
-
|
|
136
|
+
if ((_b = info.meta) === null || _b === void 0 ? void 0 : _b.err) {
|
|
137
|
+
// Use optional chaining
|
|
123
138
|
delete info.meta.err;
|
|
124
139
|
}
|
|
125
140
|
}
|
|
126
141
|
else if (error) {
|
|
127
|
-
// For generic errors, ensure the stack is included if available
|
|
128
142
|
info.errorMessage = error.message;
|
|
129
143
|
info.errorStack = error.stack;
|
|
130
144
|
if (typeof info.message === "object") {
|
|
131
145
|
info.message = error.message;
|
|
132
146
|
}
|
|
133
|
-
if (info.meta
|
|
147
|
+
if ((_c = info.meta) === null || _c === void 0 ? void 0 : _c.err) {
|
|
148
|
+
// Use optional chaining
|
|
134
149
|
delete info.meta.err;
|
|
135
150
|
}
|
|
136
151
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@riocrypto/common-server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2278",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./build/index.js",
|
|
6
6
|
"types": "./build/index.d.ts",
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"crypto-js": "^4.2.0",
|
|
35
35
|
"csurf": "^1.11.0",
|
|
36
36
|
"express": "^4.21.1",
|
|
37
|
+
"express-rate-limit": "^7.5.0",
|
|
37
38
|
"express-validator": "^6.14.2",
|
|
38
39
|
"fireblocks-sdk": "^5.13.0",
|
|
39
40
|
"googleapis": "^135.1.0",
|