UNPKG

@syntropysoft/praetorian

Version:

Praetorian CLI – A universal multi-environment configuration validator for DevSecOps teams. Validate, compare, and secure YAML/ENV files with ease.

242 lines 7.9 kB
"use strict"; /** * Permission Validator - Functional Programming * * Single Responsibility: Validate file permissions only * Pure functions, no state, no side effects */ Object.defineProperty(exports, "__esModule", { value: true }); exports.getRecommendedPermissions = exports.isSensitiveFile = exports.getPermissionDescription = exports.isTooRestrictive = exports.isTooPermissive = exports.parsePermissions = exports.formatPermissions = exports.validatePermissions = void 0; /** * Pure function to validate file permissions */ const validatePermissions = (filePath, permissions, rules, context) => { // Guard clause: no file path if (!filePath || filePath.trim().length === 0) { return []; } // Guard clause: no rules if (!rules || rules.length === 0) { return []; } // Guard clause: no permissions provided if (permissions === undefined || permissions === null) { return rules.map(rule => createPermissionError(filePath, rule, 'Permissions not available')); } return rules .filter(rule => rule.enabled && matchesFilePattern(filePath, rule.filePattern)) .map(rule => validateSinglePermission(filePath, permissions, rule, context)); }; exports.validatePermissions = validatePermissions; /** * Pure function to validate a single permission rule */ const validateSinglePermission = (filePath, permissions, rule, context) => { // Guard clause: invalid rule if (!rule || !rule.filePattern) { return createPermissionError(filePath, rule, 'Invalid permission rule'); } const isValid = isPermissionValid(permissions, rule); if (isValid) { return createPermissionSuccess(filePath, permissions, rule); } return createPermissionError(filePath, rule, 'Permissions too permissive', permissions); }; /** * Pure function to check if permissions are valid */ const isPermissionValid = (permissions, rule) => { // Guard clause: invalid permissions if (permissions < 0 || permissions > 7777) { return false; } // Check maximum permissions if (permissions > rule.maxPermissions) { return false; } // Check minimum permissions if specified if (rule.minPermissions !== undefined && permissions < rule.minPermissions) { return false; } return true; }; /** * Pure function to check if file matches pattern */ const matchesFilePattern = (filePath, pattern) => { // Guard clause: empty pattern if (!pattern || pattern.trim().length === 0) { return false; } // Convert glob pattern to regex const regexPattern = convertGlobToRegex(pattern); return regexPattern.test(filePath); }; /** * Pure function to convert glob pattern to regex */ const convertGlobToRegex = (globPattern) => { // Guard clause: empty pattern if (!globPattern) { return /^$/; } // Escape special regex characters except glob ones let regex = globPattern .replace(/[.+^${}()|[\]\\]/g, '\\$&') // Escape regex special chars .replace(/\*/g, '.*') // * matches anything .replace(/\?/g, '.'); // ? matches single char // Handle ** for recursive matching regex = regex.replace(/\*\*/g, '.*'); return new RegExp(`^${regex}$`); }; /** * Pure function to create permission success result */ const createPermissionSuccess = (filePath, permissions, rule) => ({ filePath, currentPermissions: permissions, requiredPermissions: rule.maxPermissions, valid: true }); /** * Pure function to create permission error result */ const createPermissionError = (filePath, rule, message, permissions) => ({ filePath, currentPermissions: permissions || 0, requiredPermissions: rule.maxPermissions, valid: false }); /** * Pure function to format permissions as octal string */ const formatPermissions = (permissions) => { // Guard clause: invalid permissions if (permissions < 0 || permissions > 7777) { return '0000'; } return permissions.toString(8).padStart(4, '0'); }; exports.formatPermissions = formatPermissions; /** * Pure function to parse permissions from string */ const parsePermissions = (permissionString) => { // Guard clause: empty string if (!permissionString || permissionString.trim().length === 0) { return 0; } // Remove leading zeros and convert to number const cleanString = permissionString.replace(/^0+/, '') || '0'; const permissions = parseInt(cleanString, 8); // Guard clause: invalid number if (isNaN(permissions) || permissions < 0 || permissions > 7777) { return 0; } return permissions; }; exports.parsePermissions = parsePermissions; /** * Pure function to check if permissions are too permissive */ const isTooPermissive = (permissions, maxPermissions) => { // Guard clause: invalid permissions if (permissions < 0 || permissions > 7777) { return true; } return permissions > maxPermissions; }; exports.isTooPermissive = isTooPermissive; /** * Pure function to check if permissions are too restrictive */ const isTooRestrictive = (permissions, minPermissions) => { // Guard clause: invalid permissions if (permissions < 0 || permissions > 7777) { return true; } return permissions < minPermissions; }; exports.isTooRestrictive = isTooRestrictive; /** * Pure function to get permission description */ const getPermissionDescription = (permissions) => { // Guard clause: invalid permissions if (permissions < 0 || permissions > 7777) { return 'Invalid permissions'; } const owner = (permissions & 0o700) >> 6; const group = (permissions & 0o70) >> 3; const other = permissions & 0o7; const formatBits = (bits) => { const read = (bits & 4) ? 'r' : '-'; const write = (bits & 2) ? 'w' : '-'; const execute = (bits & 1) ? 'x' : '-'; return `${read}${write}${execute}`; }; return `${formatBits(owner)}${formatBits(group)}${formatBits(other)}`; }; exports.getPermissionDescription = getPermissionDescription; /** * Pure function to check if file is sensitive */ const isSensitiveFile = (filePath) => { // Guard clause: empty path if (!filePath || filePath.trim().length === 0) { return false; } const sensitivePatterns = [ /\.env$/i, /\.env\..*$/i, /config\.json$/i, /secrets\.ya?ml$/i, /\.pem$/i, /\.key$/i, /\.p12$/i, /\.pfx$/i, /id_rsa$/i, /id_dsa$/i, /id_ecdsa$/i, /id_ed25519$/i ]; return sensitivePatterns.some(pattern => pattern.test(filePath)); }; exports.isSensitiveFile = isSensitiveFile; /** * Pure function to get recommended permissions for file */ const getRecommendedPermissions = (filePath) => { // Guard clause: empty path if (!filePath || filePath.trim().length === 0) { return 644; } // Sensitive files should be more restrictive if ((0, exports.isSensitiveFile)(filePath)) { return 600; // Owner read/write only } // Executable files if (isExecutableFile(filePath)) { return 755; // Owner read/write/execute, others read/execute } // Regular files return 644; // Owner read/write, others read }; exports.getRecommendedPermissions = getRecommendedPermissions; /** * Pure function to check if file is executable */ const isExecutableFile = (filePath) => { // Guard clause: empty path if (!filePath || filePath.trim().length === 0) { return false; } const executablePatterns = [ /\.(sh|bash|zsh|fish)$/i, /\.(exe|bat|cmd)$/i, /\.(py|pl|rb|js|ts)$/i, /^[^.]*$/ // No extension (common for executables) ]; return executablePatterns.some(pattern => pattern.test(filePath)); }; //# sourceMappingURL=PermissionValidator.js.map