UNPKG

fortify-schema

Version:

A modern TypeScript validation library designed around familiar interface syntax and powerful conditional validation. Experience schema validation that feels natural to TypeScript developers while unlocking advanced runtime validation capabilities.

216 lines (212 loc) 9.61 kB
'use strict'; var ErrorHandler = require('../schema/mode/interfaces/errors/ErrorHandler.js'); var errors_type = require('../schema/mode/interfaces/errors/types/errors.type.js'); // Helper export function for deep JSON validation function validateJsonDeep(data, options) { const result = { success: true, errors: [], warnings: [], }; if (options.currentDepth > options.maxDepth) { result.success = false; result.errors.push(ErrorHandler.ErrorHandler.createError([], `Maximum depth of ${options.maxDepth} exceeded`, errors_type.ErrorCode.SECURITY_VIOLATION, "valid depth", options.currentDepth)); return result; } const dataType = Array.isArray(data) ? "array" : data === null ? "null" : typeof data; if (!options.allowedTypes.includes(dataType)) { result.success = false; result.errors.push(ErrorHandler.ErrorHandler.createSimpleError(`Type '${dataType}' is not allowed`, errors_type.ErrorCode.SECURITY_VIOLATION)); return result; } if (typeof data === "string" && data.length > options.maxStringLength) { result.success = false; result.errors.push(ErrorHandler.ErrorHandler.createError([], `String length exceeds maximum of ${options.maxStringLength}`, errors_type.ErrorCode.SECURITY_VIOLATION, `string with length <= ${options.maxStringLength}`, data.length)); } if (Array.isArray(data)) { if (data.length > options.maxArrayLength) { result.success = false; result.errors.push(ErrorHandler.ErrorHandler.createError([], `Array length exceeds maximum of ${options.maxArrayLength}`, errors_type.ErrorCode.SECURITY_VIOLATION, `array with length <= ${options.maxArrayLength}`, data.length)); } for (const item of data) { const itemResult = validateJsonDeep(item, { ...options, currentDepth: options.currentDepth + 1, }); result.success = result.success && itemResult.success; result.errors.push(...itemResult.errors); result.warnings.push(...itemResult.warnings); } } if (typeof data === "object" && data !== null && !Array.isArray(data)) { const keys = Object.keys(data); options.keyCount += keys.length; if (options.keyCount > options.maxKeys) { result.success = false; result.errors.push(ErrorHandler.ErrorHandler.createError([], `Maximum key count of ${options.maxKeys} exceeded`, errors_type.ErrorCode.SECURITY_VIOLATION, `object with <= ${options.maxKeys} keys`, options.keyCount)); } for (const key of keys) { if (options.forbiddenKeys.includes(key)) { result.success = false; result.errors.push(ErrorHandler.ErrorHandler.createError([key], `Forbidden key '${key}' found`, errors_type.ErrorCode.SECURITY_VIOLATION, "allowed key", key)); } const valueResult = validateJsonDeep(data[key], { ...options, currentDepth: options.currentDepth + 1, }); result.success = result.success && valueResult.success; result.errors.push(...valueResult.errors); result.warnings.push(...valueResult.warnings); } } return result; } // Helper export function for JSON schema validation function validateJsonSchema(data, schema) { // Basic schema validation - you might want to use a proper JSON schema library like Ajv const result = { success: true, errors: [], warnings: [], }; if (schema.type) { const dataType = Array.isArray(data) ? "array" : data === null ? "null" : typeof data; if (dataType !== schema.type) { result.success = false; result.errors.push(ErrorHandler.ErrorHandler.createError([], `Expected type '${schema.type}', got '${dataType}'`, errors_type.ErrorCode.TYPE_MISMATCH, schema.type, data)); } } if (schema.properties && typeof data === "object" && data !== null) { for (const [key, propSchema] of Object.entries(schema.properties)) { if (key in data) { const propResult = validateJsonSchema(data[key], propSchema); result.success = result.success && propResult.success; result.errors.push(...propResult.errors); result.warnings.push(...propResult.warnings); } } } return result; } // Helper export function for IPv4 validation function validateIPv4(ip, strict) { const ipv4Regex = strict ? /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/ : /^(?:\d{1,3}\.){3}\d{1,3}$/; if (!ipv4Regex.test(ip)) { return { valid: false }; } if (strict) { const octets = ip.split("."); // Check for leading zeros (except for "0" itself) for (const octet of octets) { if (octet.length > 1 && octet.startsWith("0")) { return { valid: false }; // Leading zeros not allowed } const num = Number(octet); if (num < 0 || num > 255) { return { valid: false }; } } } return { valid: true, normalized: ip }; } // Helper export function for IPv6 validation function validateIPv6(ip, strict) { // Improved IPv6 regex that properly handles compressed notation const ipv6Regex = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/; if (!ipv6Regex.test(ip)) { return { valid: false }; } return { valid: true, normalized: normalizeIPv6(ip) }; } // Helper export function for IPv6 normalization function normalizeIPv6(ip) { // Expand compressed notation and normalize to full form if (ip === "::") { return "0000:0000:0000:0000:0000:0000:0000:0000"; } if (ip === "::1") { return "0000:0000:0000:0000:0000:0000:0000:0001"; } // Handle compressed notation if (ip.includes("::")) { const parts = ip.split("::"); const leftParts = parts[0] ? parts[0].split(":") : []; const rightParts = parts[1] ? parts[1].split(":") : []; const missingParts = 8 - leftParts.length - rightParts.length; const fullParts = [ ...leftParts, ...Array(missingParts).fill("0000"), ...rightParts, ]; return fullParts.map((part) => part.padStart(4, "0")).join(":"); } // Already full form, just normalize padding return ip .split(":") .map((part) => part.padStart(4, "0")) .join(":"); } // Helper export function for deep object validation function validateObjectDeep(obj, maxDepth, currentDepth) { const result = { success: true, errors: [], warnings: [], }; if (currentDepth > maxDepth) { result.success = false; result.errors.push(ErrorHandler.ErrorHandler.createError([], `Maximum object depth of ${maxDepth} exceeded`, errors_type.ErrorCode.SECURITY_VIOLATION, `object with depth <= ${maxDepth}`, currentDepth)); return result; } for (const [key, value] of Object.entries(obj)) { if (typeof value === "object" && value !== null && !Array.isArray(value)) { const deepResult = validateObjectDeep(value, maxDepth, currentDepth + 1); result.success = result.success && deepResult.success; result.errors.push(...deepResult.errors); result.warnings.push(...deepResult.warnings); } } return result; } // Helper export function for object schema validation function validateObjectSchema(obj, schema) { const result = { success: true, errors: [], warnings: [], }; if (schema.type && schema.type !== "object") { result.success = false; result.errors.push(ErrorHandler.ErrorHandler.createError([], `Expected object type in schema`, errors_type.ErrorCode.TYPE_MISMATCH, "object", schema.type)); return result; } if (schema.properties) { for (const [key, propSchema] of Object.entries(schema.properties)) { if (key in obj) { const propResult = validateJsonSchema(obj[key], propSchema); result.success = result.success && propResult.success; result.errors.push(...propResult.errors); result.warnings.push(...propResult.warnings); } } } return result; } exports.normalizeIPv6 = normalizeIPv6; exports.validateIPv4 = validateIPv4; exports.validateIPv6 = validateIPv6; exports.validateJsonDeep = validateJsonDeep; exports.validateJsonSchema = validateJsonSchema; exports.validateObjectDeep = validateObjectDeep; exports.validateObjectSchema = validateObjectSchema; //# sourceMappingURL=securityHelpers.js.map