@syntropysoft/praetorian
Version:
Praetorian CLI – A universal multi-environment configuration validator for DevSecOps teams. Validate, compare, and secure YAML/ENV files with ease.
221 lines • 8.06 kB
JavaScript
;
/**
* @file src/application/services/rule-loading/RuleSetLoader.ts
* @description Pure functions for loading rule sets from various sources
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.combineRuleLoadResults = exports.createEmptyResult = exports.isUrl = exports.extractRulesFromContent = exports.parseContentByExtension = exports.parseRuleFile = exports.loadRuleSetFromUrl = exports.loadRuleSetFromFile = exports.loadCoreRuleSet = exports.loadRuleSet = void 0;
const promises_1 = __importDefault(require("fs/promises"));
const path_1 = __importDefault(require("path"));
const js_yaml_1 = __importDefault(require("js-yaml"));
const core_rules_1 = require("../../../shared/rules/core-rules");
/**
* Loads a rule set from a path or URL
* @param ruleSetPath - Path to rule set (file path or URL)
* @param options - Loading options
* @returns Promise with loaded rules
*/
const loadRuleSet = async (ruleSetPath, options) => {
// Guard clause: empty path
if (!ruleSetPath || ruleSetPath.trim().length === 0) {
return (0, exports.createEmptyResult)(['Rule set path cannot be empty']);
}
try {
// Check if it's a core rule set reference
if (ruleSetPath.startsWith('@praetorian/core/')) {
return (0, exports.loadCoreRuleSet)(ruleSetPath);
}
// Check if it's a URL
if ((0, exports.isUrl)(ruleSetPath)) {
return await (0, exports.loadRuleSetFromUrl)(ruleSetPath);
}
// Load from local file
return await (0, exports.loadRuleSetFromFile)(ruleSetPath, options);
}
catch (error) {
return (0, exports.createEmptyResult)([`Failed to load rule set ${ruleSetPath}: ${error.message}`]);
}
};
exports.loadRuleSet = loadRuleSet;
/**
* Loads a core rule set by name
* @param corePath - Core rule set path (e.g., '@praetorian/core/all')
* @returns Rule load result
*/
const loadCoreRuleSet = (corePath) => {
// Guard clause: invalid core path
if (!corePath || !corePath.startsWith('@praetorian/core/')) {
return (0, exports.createEmptyResult)(['Invalid core rule set path']);
}
const coreSetName = corePath.replace('@praetorian/core/', '');
// Guard clause: unknown core set
if (!(coreSetName in core_rules_1.CORE_RULE_SETS)) {
return (0, exports.createEmptyResult)([`Unknown core rule set: ${coreSetName}`]);
}
const rules = core_rules_1.CORE_RULE_SETS[coreSetName];
return {
rules: Array.isArray(rules) ? rules : [],
errors: [],
warnings: [],
};
};
exports.loadCoreRuleSet = loadCoreRuleSet;
/**
* Loads rule set from a local file
* @param filePath - Path to the rule file
* @param options - Loading options
* @returns Promise with loaded rules
*/
const loadRuleSetFromFile = async (filePath, options) => {
// Guard clause: empty file path
if (!filePath || filePath.trim().length === 0) {
return (0, exports.createEmptyResult)(['File path cannot be empty']);
}
try {
const fullPath = path_1.default.resolve(options.workingDirectory, filePath);
const content = await promises_1.default.readFile(fullPath, 'utf-8');
const parseResult = (0, exports.parseRuleFile)(content, fullPath);
return parseResult;
}
catch (error) {
return (0, exports.createEmptyResult)([`Failed to read rule file ${filePath}: ${error.message}`]);
}
};
exports.loadRuleSetFromFile = loadRuleSetFromFile;
/**
* Loads rule set from a URL (placeholder for future implementation)
* @param url - URL to the rule set
* @returns Promise with loaded rules
*/
const loadRuleSetFromUrl = async (url) => {
// Guard clause: empty URL
if (!url || url.trim().length === 0) {
return (0, exports.createEmptyResult)(['URL cannot be empty']);
}
// Guard clause: invalid URL
if (!(0, exports.isUrl)(url)) {
return (0, exports.createEmptyResult)(['Invalid URL format']);
}
// TODO: Implement URL loading
return (0, exports.createEmptyResult)([`URL loading not yet implemented: ${url}`]);
};
exports.loadRuleSetFromUrl = loadRuleSetFromUrl;
/**
* Parses rule file content based on file extension
* @param content - File content
* @param filePath - Full file path for extension detection
* @returns Parsed rules result
*/
const parseRuleFile = (content, filePath) => {
// Guard clause: empty content
if (!content || content.trim().length === 0) {
return (0, exports.createEmptyResult)(['File content is empty']);
}
try {
const ext = path_1.default.extname(filePath).toLowerCase();
const parsedContent = (0, exports.parseContentByExtension)(content, ext);
// Guard clause: invalid parsed content
if (!parsedContent) {
return (0, exports.createEmptyResult)([`Failed to parse ${ext} file`]);
}
const rules = (0, exports.extractRulesFromContent)(parsedContent);
return {
rules,
errors: [],
warnings: [],
};
}
catch (error) {
return (0, exports.createEmptyResult)([`Failed to parse rule file: ${error.message}`]);
}
};
exports.parseRuleFile = parseRuleFile;
/**
* Parses content based on file extension
* @param content - File content
* @param extension - File extension
* @returns Parsed content
*/
const parseContentByExtension = (content, extension) => {
// Guard clause: empty content
if (!content || content.trim().length === 0) {
return null;
}
switch (extension) {
case '.yaml':
case '.yml':
return js_yaml_1.default.load(content);
case '.json':
return JSON.parse(content);
default:
throw new Error(`Unsupported file format: ${extension}`);
}
};
exports.parseContentByExtension = parseContentByExtension;
/**
* Extracts rules from parsed content
* @param parsedContent - Parsed file content
* @returns Array of rules
*/
const extractRulesFromContent = (parsedContent) => {
// Guard clause: null or undefined content
if (!parsedContent) {
return [];
}
// If it's already an array, return it
if (Array.isArray(parsedContent)) {
return parsedContent;
}
// If it has a rules property, extract it
if (parsedContent.rules && Array.isArray(parsedContent.rules)) {
return parsedContent.rules;
}
// Invalid format
throw new Error('Invalid rule file format: expected array of rules or object with rules array');
};
exports.extractRulesFromContent = extractRulesFromContent;
/**
* Checks if a string is a valid URL
* @param str - String to check
* @returns True if URL, false otherwise
*/
const isUrl = (str) => {
// Guard clause: empty string
if (!str || str.trim().length === 0) {
return false;
}
return str.startsWith('http://') || str.startsWith('https://');
};
exports.isUrl = isUrl;
/**
* Creates an empty rule load result with errors
* @param errors - Array of error messages
* @returns Empty result with errors
*/
const createEmptyResult = (errors = []) => ({
rules: [],
errors,
warnings: [],
});
exports.createEmptyResult = createEmptyResult;
/**
* Combines multiple rule load results
* @param results - Array of rule load results
* @returns Combined result
*/
const combineRuleLoadResults = (results) => {
// Guard clause: empty results array
if (!results || results.length === 0) {
return (0, exports.createEmptyResult)();
}
return results.reduce((combined, result) => ({
rules: [...combined.rules, ...result.rules],
errors: [...combined.errors, ...result.errors],
warnings: [...combined.warnings, ...result.warnings],
}), (0, exports.createEmptyResult)());
};
exports.combineRuleLoadResults = combineRuleLoadResults;
//# sourceMappingURL=RuleSetLoader.js.map