@aikidosec/firewall
Version:
Zen by Aikido is an embedded Web Application Firewall that autonomously protects Node.js apps against common and critical attacks
100 lines (99 loc) • 3.72 kB
JavaScript
;
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]));
}