UNPKG

@aikidosec/firewall

Version:

Zen by Aikido is an embedded Application Firewall that autonomously protects Node.js apps against common and critical attacks, provides rate limiting, detects malicious traffic (including bots), and more.

100 lines (99 loc) 3.72 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.mergeDataSchemas = mergeDataSchemas; const isPrimitiveType_1 = require("./isPrimitiveType"); /** * Merge two data schemas into one, getting all properties from both schemas to capture optional properties. * If the types are different, only primitive types are merged. * Merging of arrays with objects or objects / arrays with primitive types is not supported. * In this case the first schema is preferred over the second schema because it can already contain multiple merged schemas. * If the types are the same, the properties of the second schema are merged into the first schema. */ function mergeDataSchemas(first, second) { const result = { ...first }; // Can not merge different types if (!isSameType(first.type, second.type)) { return mergeTypes(first, second); } if (first.properties && second.properties) { result.properties = { ...first.properties }; for (const key in second.properties) { if (result.properties[key]) { result.properties[key] = mergeDataSchemas(result.properties[key], second.properties[key]); } else { result.properties[key] = second.properties[key]; // If a property is not in the first schema, we can assume it is optional // because we only store schemas for requests with status 2xx result.properties[key].optional = true; } } for (const key in first.properties) { // Check if removed in second schema if (!second.properties[key]) { result.properties[key].optional = true; } } } if (first.items && second.items) { result.items = mergeDataSchemas(first.items, second.items); } if (first.format && second.format && first.format !== second.format) { // If the formats are different, we can not merge them // So we set format to undefined because we are not sure anymore result.format = undefined; } if (!first.format && second.format) { result.format = second.format; } return result; } /** * Check if both types are the same. */ function isSameType(first, second) { if (Array.isArray(first) && Array.isArray(second)) { return doTypeArraysMatch(first, second); } if (Array.isArray(first) && !Array.isArray(second)) { return doTypeArraysMatch(first, [second]); } if (!Array.isArray(first) && Array.isArray(second)) { return doTypeArraysMatch([first], second); } return first === second; } /** * Compare two arrays of types and ignore the order. */ function doTypeArraysMatch(first, second) { if (first.length !== second.length) { return false; } return first.every((type) => second.includes(type)); } /** * Merge types into one schema if they are different. */ function mergeTypes(first, second) { // Currently we do not support merging arrays and other objects and arrays / objects with primitive types if (!(0, isPrimitiveType_1.onlyContainsPrimitiveTypes)(first.type) || !(0, isPrimitiveType_1.onlyContainsPrimitiveTypes)(second.type)) { // Prefer non-null type if (first.type === "null") { return second; } return first; } first.type = mergeTypeArrays(first.type, second.type); return first; } function mergeTypeArrays(first, second) { if (!Array.isArray(first)) { first = [first]; } if (!Array.isArray(second)) { second = [second]; } return Array.from(new Set([...first, ...second])); }