xypriss-security
Version:
XyPriss Security is an advanced JavaScript security library designed for enterprise applications. It provides military-grade encryption, secure data structures, quantum-resistant cryptography, and comprehensive security utilities for modern web applicatio
339 lines (336 loc) • 11.9 kB
JavaScript
;
/**
* Safe Serialization Utility for FortifiedFunction
* Handles cyclic structures, Express objects, and performance optimization
*/
class SafeSerializer {
/**
* **ULTRA-FAST: Primary serialization method with performance optimization**
*/
static stringify(obj, options = {}) {
const opts = { ...this.DEFAULT_OPTIONS, ...options };
// **ULTRA-FAST PATH: Try simple JSON.stringify first**
if (opts.fastMode) {
try {
const result = JSON.stringify(obj);
if (result.length <= opts.maxLength) {
return result;
}
}
catch {
// Fall through to safe serialization
}
}
// **SAFE PATH: Handle complex objects with cyclic references**
return this.safeStringify(obj, opts);
}
/**
* **EXPRESS-SAFE: Enhanced JSON.stringify for Express objects**
*/
static expressStringify(obj, options = {}) {
const opts = { ...this.DEFAULT_OPTIONS, ...options };
try {
return JSON.stringify(obj, this.createExpressReplacer(opts));
}
catch (error) {
// Fallback to safe serialization
return this.safeStringify(obj, opts);
}
}
/**
* **EXPRESS REPLACER: Handles Express req/res objects and circular references**
*/
static createExpressReplacer(options) {
const seen = new WeakSet();
let depth = 0;
return function (key, value) {
// Track depth
if (key === "")
depth = 0;
else
depth++;
if (depth > options.maxDepth) {
return "[Max Depth Exceeded]";
}
// Handle null/undefined
if (value === null || value === undefined) {
return value;
}
// Handle circular references
if (typeof value === "object" && value !== null) {
if (seen.has(value)) {
return "[Circular Reference]";
}
seen.add(value);
}
// Handle Express Request objects
if (value &&
typeof value === "object" &&
value.constructor &&
value.constructor.name === "IncomingMessage") {
return {
method: value.method,
url: value.url,
headers: value.headers,
query: value.query,
params: value.params,
body: value.body,
ip: value.ip,
_type: "[Express Request]",
};
}
// Handle Express Response objects
if (value &&
typeof value === "object" &&
value.constructor &&
value.constructor.name === "ServerResponse") {
return {
statusCode: value.statusCode,
statusMessage: value.statusMessage,
headersSent: value.headersSent,
_type: "[Express Response]",
};
}
// Handle functions
if (typeof value === "function") {
return `[Function: ${value.name || "anonymous"}]`;
}
// Handle large strings
if (typeof value === "string" &&
value.length > options.truncateStrings) {
return (value.substring(0, options.truncateStrings) +
"...[truncated]");
}
// Handle Buffers
if (value instanceof Buffer) {
return `[Buffer: ${value.length} bytes]`;
}
// Handle other special objects
if (value instanceof Date) {
return value.toISOString();
}
if (value instanceof RegExp) {
return value.toString();
}
if (value instanceof Error) {
return {
name: value.name,
message: value.message,
stack: value.stack,
_type: "[Error]",
};
}
return value;
};
}
/**
* **SAFE SERIALIZATION: Handles all edge cases**
*/
static safeStringify(obj, options) {
const seen = new WeakSet();
let depth = 0;
const replacer = (_key, value) => {
// Handle primitive values
if (value === null || typeof value !== "object") {
if (typeof value === "string" &&
value.length > options.truncateStrings) {
return (value.substring(0, options.truncateStrings) +
"...[truncated]");
}
return value;
}
// Check depth limit
depth++;
if (depth > options.maxDepth) {
depth--;
return "[Max Depth Exceeded]";
}
// Handle cyclic references
if (seen.has(value)) {
return `[Circular:${value.constructor?.name || "Object"}]`;
}
seen.add(value);
// Handle special Express objects
if (value.constructor) {
const constructorName = value.constructor.name;
// Express Request object
if (constructorName === "IncomingMessage" ||
constructorName === "Request") {
const result = {
method: value.method,
url: value.url,
headers: this.sanitizeHeaders(value.headers),
params: value.params,
query: value.query,
body: value.body ? "[Request Body]" : undefined,
};
depth--;
return result;
}
// Express Response object
if (constructorName === "ServerResponse" ||
constructorName === "Response") {
const result = {
statusCode: value.statusCode,
statusMessage: value.statusMessage,
headersSent: value.headersSent,
};
depth--;
return result;
}
// Other problematic objects
if (["Socket", "Server", "Agent", "TLSSocket"].includes(constructorName)) {
depth--;
return `[${constructorName}:${value.constructor.name}]`;
}
}
// Handle functions
if (typeof value === "function") {
depth--;
return `[Function:${value.name || "anonymous"}]`;
}
// Handle Buffers
if (Buffer.isBuffer(value)) {
depth--;
return `[Buffer:${value.length}bytes]`;
}
// Handle large arrays
if (Array.isArray(value) && value.length > 100) {
depth--;
return `[Array:${value.length}items]`;
}
// Handle Error objects
if (value instanceof Error) {
depth--;
return {
name: value.name,
message: value.message,
stack: value.stack ? "[Stack Trace]" : undefined,
};
}
depth--;
return value;
};
try {
const result = JSON.stringify(obj, replacer);
// Check length limit
if (result.length > options.maxLength) {
return (result.substring(0, options.maxLength) + "...[truncated]");
}
return result;
}
catch (error) {
// Ultimate fallback
return `[Serialization Error: ${error instanceof Error ? error.message : "Unknown"}]`;
}
}
/**
* **UTILITY: Sanitize HTTP headers for safe logging**
*/
static sanitizeHeaders(headers) {
if (!headers || typeof headers !== "object") {
return headers;
}
const sanitized = {};
const sensitiveHeaders = [
"authorization",
"cookie",
"x-api-key",
"x-auth-token",
];
for (const [key, value] of Object.entries(headers)) {
const lowerKey = key.toLowerCase();
if (sensitiveHeaders.includes(lowerKey)) {
sanitized[key] = "[REDACTED]";
}
else {
sanitized[key] = value;
}
}
return sanitized;
}
/**
* **ULTRA-FAST: Generate cache key with safe serialization**
*/
static generateCacheKey(args, prefix = "cache") {
if (!args || args.length === 0) {
return `${prefix}:empty`;
}
// **EXPRESS DETECTION: Check if arguments contain Express req/res objects**
const hasExpressObjects = args.some((arg) => arg &&
typeof arg === "object" &&
arg.constructor &&
(arg.constructor.name === "IncomingMessage" ||
arg.constructor.name === "ServerResponse" ||
arg.constructor.name === "Request" ||
arg.constructor.name === "Response" ||
(arg.method && arg.url && arg.headers) || // Express Request-like
(arg.statusCode !== undefined &&
arg.headersSent !== undefined)) // Express Response-like
);
if (hasExpressObjects) {
// **EXPRESS-SAFE PATH: Use Express-safe serialization**
const safe = this.expressStringify(args, {
fastMode: false,
maxDepth: 3,
maxLength: 300,
truncateStrings: 50,
});
return `${prefix}:express:${safe}`;
}
try {
// **ULTRA-FAST PATH: Try simple approach first for non-Express objects**
const simple = JSON.stringify(args);
if (simple.length <= 500) {
return `${prefix}:${simple}`;
}
}
catch {
// Fall through to safe approach
}
// **SAFE PATH: Use safe serialization**
const safe = this.stringify(args, {
fastMode: false,
maxDepth: 5,
maxLength: 500,
truncateStrings: 100,
});
return `${prefix}:${safe}`;
}
/**
* **DEBUG: Safe debug logging**
*/
static debugLog(label, obj, maxLength = 200) {
const serialized = this.stringify(obj, {
fastMode: true,
maxLength,
maxDepth: 3,
truncateStrings: 50,
});
console.log(`[DEBUG] ${label}: ${serialized}`);
}
/**
* **AUDIT: Safe audit logging with full details**
*/
static auditLog(obj) {
return this.stringify(obj, {
fastMode: false,
maxDepth: 8,
maxLength: 5000,
truncateStrings: 500,
includeNonEnumerable: false,
});
}
}
SafeSerializer.DEFAULT_OPTIONS = {
maxDepth: 10,
maxLength: 10000,
includeNonEnumerable: false,
truncateStrings: 1000,
fastMode: false,
};
// Cache key generation
const generateSafeCacheKey = (args, prefix) => SafeSerializer.generateCacheKey(args, prefix);
exports.SafeSerializer = SafeSerializer;
exports.generateSafeCacheKey = generateSafeCacheKey;
//# sourceMappingURL=safe-serializer.js.map