@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
JavaScript
;
/**
* 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