@syntropysoft/praetorian
Version:
Praetorian CLI – A universal multi-environment configuration validator for DevSecOps teams. Validate, compare, and secure YAML/ENV files with ease.
275 lines • 8.38 kB
JavaScript
;
/**
* @file src/application/services/rule-loading/RuleComposer.ts
* @description Pure functions for composing and manipulating validation rules
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.getUniqueTags = exports.getUniqueCategories = exports.getDisabledRules = exports.getEnabledRules = exports.getRulesBySeverity = exports.getRulesByTags = exports.getRulesByCategory = exports.validateRuleSpecificProperties = exports.validateLoadedRules = exports.applyRuleOverrides = exports.addCustomRules = exports.composeRules = void 0;
/**
* Composes rules from configuration
* @param baseRules - Base rules to start with
* @param config - Rule configuration
* @param options - Composition options
* @returns Composed rules
*/
const composeRules = (baseRules, config, options = {}) => {
// Guard clause: no config
if (!config) {
return baseRules;
}
// Guard clause: no base rules
if (!baseRules || baseRules.length === 0) {
return [];
}
let rules = [...baseRules];
// Add custom rules
if (config.customRules && config.customRules.length > 0) {
rules = (0, exports.addCustomRules)(rules, config.customRules);
}
// Apply overrides
if (config.overrideRules && config.overrideRules.length > 0) {
rules = (0, exports.applyRuleOverrides)(rules, config.overrideRules);
}
return rules;
};
exports.composeRules = composeRules;
/**
* Adds custom rules to existing rules
* @param existingRules - Existing rules
* @param customRules - Custom rules to add
* @returns Combined rules
*/
const addCustomRules = (existingRules, customRules) => {
// Guard clause: no existing rules
if (!existingRules || existingRules.length === 0) {
return customRules || [];
}
// Guard clause: no custom rules
if (!customRules || customRules.length === 0) {
return existingRules;
}
return [...existingRules, ...customRules];
};
exports.addCustomRules = addCustomRules;
/**
* Applies rule overrides to existing rules
* @param rules - Existing rules
* @param overrides - Rule overrides
* @returns Updated rules
*/
const applyRuleOverrides = (rules, overrides) => {
// Guard clause: no rules
if (!rules || rules.length === 0) {
return [];
}
// Guard clause: no overrides
if (!overrides || overrides.length === 0) {
return rules;
}
const rulesMap = new Map(rules.map(rule => [rule.id, rule]));
for (const override of overrides) {
// Guard clause: no ID in override
if (!override.id) {
continue;
}
const existingRule = rulesMap.get(override.id);
if (existingRule) {
// Update existing rule
const updatedRule = {
...existingRule,
...override,
};
rulesMap.set(override.id, updatedRule);
}
else {
// Add new rule (treat as custom rule)
rulesMap.set(override.id, override);
}
}
return Array.from(rulesMap.values());
};
exports.applyRuleOverrides = applyRuleOverrides;
/**
* Validates loaded rules for consistency
* @param rules - Rules to validate
* @returns Validation warnings
*/
const validateLoadedRules = (rules) => {
// Guard clause: no rules
if (!rules || rules.length === 0) {
return [];
}
const warnings = [];
const ruleIds = new Set();
for (const rule of rules) {
// Check for duplicate IDs
if (ruleIds.has(rule.id)) {
warnings.push(`Duplicate rule ID found: ${rule.id}`);
}
ruleIds.add(rule.id);
// Check for required fields
if (!rule.name || !rule.description) {
warnings.push(`Rule ${rule.id} missing required fields`);
}
// Validate rule-specific properties
const ruleValidationWarnings = (0, exports.validateRuleSpecificProperties)(rule);
warnings.push(...ruleValidationWarnings);
}
return warnings;
};
exports.validateLoadedRules = validateLoadedRules;
/**
* Validates rule-specific properties
* @param rule - Rule to validate
* @returns Validation warnings for this rule
*/
const validateRuleSpecificProperties = (rule) => {
// Guard clause: no rule
if (!rule) {
return ['Rule is null or undefined'];
}
const warnings = [];
switch (rule.type) {
case 'format':
if (!rule.format && !rule.pattern) {
warnings.push(`Format rule ${rule.id} missing format or pattern`);
}
break;
case 'structure':
// Structure rules don't have specific requirements
break;
case 'security':
if (!rule.securityType) {
warnings.push(`Security rule ${rule.id} missing securityType`);
}
break;
case 'schema':
if (!rule.validateSchema) {
warnings.push(`Schema rule ${rule.id} has validateSchema set to false`);
}
break;
}
return warnings;
};
exports.validateRuleSpecificProperties = validateRuleSpecificProperties;
/**
* Gets rules by category
* @param rules - Rules to filter
* @param category - Category to filter by
* @returns Filtered rules
*/
const getRulesByCategory = (rules, category) => {
// Guard clause: no rules
if (!rules || rules.length === 0) {
return [];
}
// Guard clause: no category
if (!category || category.trim().length === 0) {
return rules;
}
return rules.filter(rule => rule.category === category);
};
exports.getRulesByCategory = getRulesByCategory;
/**
* Gets rules by tags
* @param rules - Rules to filter
* @param tags - Tags to filter by
* @returns Filtered rules
*/
const getRulesByTags = (rules, tags) => {
// Guard clause: no rules
if (!rules || rules.length === 0) {
return [];
}
// Guard clause: no tags
if (!tags || tags.length === 0) {
return rules;
}
return rules.filter(rule => rule.tags && tags.every(tag => rule.tags.includes(tag)));
};
exports.getRulesByTags = getRulesByTags;
/**
* Gets rules by severity
* @param rules - Rules to filter
* @param severity - Severity to filter by
* @returns Filtered rules
*/
const getRulesBySeverity = (rules, severity) => {
// Guard clause: no rules
if (!rules || rules.length === 0) {
return [];
}
// Guard clause: no severity
if (!severity || severity.trim().length === 0) {
return rules;
}
return rules.filter(rule => rule.severity === severity);
};
exports.getRulesBySeverity = getRulesBySeverity;
/**
* Gets enabled rules only
* @param rules - Rules to filter
* @returns Enabled rules
*/
const getEnabledRules = (rules) => {
// Guard clause: no rules
if (!rules || rules.length === 0) {
return [];
}
return rules.filter(rule => rule.enabled);
};
exports.getEnabledRules = getEnabledRules;
/**
* Gets disabled rules only
* @param rules - Rules to filter
* @returns Disabled rules
*/
const getDisabledRules = (rules) => {
// Guard clause: no rules
if (!rules || rules.length === 0) {
return [];
}
return rules.filter(rule => !rule.enabled);
};
exports.getDisabledRules = getDisabledRules;
/**
* Gets unique categories from rules
* @param rules - Rules to analyze
* @returns Array of unique categories
*/
const getUniqueCategories = (rules) => {
// Guard clause: no rules
if (!rules || rules.length === 0) {
return [];
}
const categories = new Set();
for (const rule of rules) {
if (rule.category) {
categories.add(rule.category);
}
}
return Array.from(categories);
};
exports.getUniqueCategories = getUniqueCategories;
/**
* Gets unique tags from rules
* @param rules - Rules to analyze
* @returns Array of unique tags
*/
const getUniqueTags = (rules) => {
// Guard clause: no rules
if (!rules || rules.length === 0) {
return [];
}
const tags = new Set();
for (const rule of rules) {
if (rule.tags) {
for (const tag of rule.tags) {
tags.add(tag);
}
}
}
return Array.from(tags);
};
exports.getUniqueTags = getUniqueTags;
//# sourceMappingURL=RuleComposer.js.map