@flamesshield/rules-engine
Version:
Independent rules engine for security analysis of Firebase
117 lines • 4.55 kB
JavaScript
/**
* PathDiscovery - Utility class for discovering and managing paths from project information
*
* Extracted from RuleValidator for better code organization and reusability.
* Handles discovery of available paths, type mapping, and deprecated path identification.
*/
export class PathDiscovery {
/**
* Discovers all available paths, their types, and deprecated paths from project information
*/
static discoverAll(projectInfo) {
const availablePaths = new Set();
const typeMap = new Map();
const deprecatedPaths = PathDiscovery.getDeprecatedPaths(projectInfo);
// Add paths from project structure
PathDiscovery.addPathsFromObject('', projectInfo, availablePaths, typeMap);
return {
availablePaths,
typeMap,
deprecatedPaths
};
}
/**
* Discovers only available paths from project structure
*/
static discoverPaths(projectInfo) {
const availablePaths = new Set();
const typeMap = new Map();
PathDiscovery.addPathsFromObject('', projectInfo, availablePaths, typeMap);
return availablePaths;
}
/**
* Gets type information for all discovered paths
*/
static getPathTypeMap(projectInfo) {
const availablePaths = new Set();
const typeMap = new Map();
PathDiscovery.addPathsFromObject('', projectInfo, availablePaths, typeMap);
return typeMap;
}
/**
* Identifies deprecated direct access paths
*/
static getDeprecatedPaths(projectInfo) {
const deprecatedPaths = new Set();
// These are direct access properties that should use computed paths instead
const deprecatedDirectPaths = [
'total_apps',
'total_services',
'unenforced_services',
'enforcement_coverage_ratio',
'apps_without_attestation_providers',
'auth_service_unenforced',
'functions_service_unenforced',
'dataconnect_service_unenforced',
'ailogic_service_unenforced',
'google_identity_ios_service_unenforced',
'maps_js_service_unenforced',
'places_api_service_unenforced'
];
for (const path of deprecatedDirectPaths) {
if (projectInfo.hasOwnProperty(path)) {
deprecatedPaths.add(path);
}
}
return deprecatedPaths;
}
/**
* Recursively adds paths from an object to the available paths set
*/
static addPathsFromObject(prefix, obj, paths, typeMap, depth = 0) {
// Prevent infinite recursion
if (depth > 10 || !obj || typeof obj !== 'object') {
return;
}
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
const fullPath = prefix ? `${prefix}.${key}` : key;
const value = obj[key];
paths.add(fullPath);
typeMap.set(fullPath, typeof value);
// Recursively add nested paths for objects
if (value && typeof value === 'object' && !Array.isArray(value)) {
PathDiscovery.addPathsFromObject(fullPath, value, paths, typeMap, depth + 1);
}
}
}
}
/**
* Extracts path references from a condition string
* Excludes string literals, numbers, and common operators/keywords
*/
static extractPathsFromCondition(condition) {
// Enhanced regex to extract paths, excluding string literals and numbers
const pathRegex = /\b([a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)*)\b/g;
// Remove string literals first to avoid false positives
const conditionWithoutStrings = condition.replace(/"[^"]*"|'[^']*'/g, '');
const matches = conditionWithoutStrings.match(pathRegex) || [];
// Filter out operators, literals, and common comparison keywords
const skipKeywords = [
'and', 'or', 'not', 'true', 'false', 'null',
'eq', 'ne', 'gt', 'lt', 'gte', 'lte'
];
return matches.filter(match => {
// Skip operators, literals, and common comparison keywords
if (skipKeywords.includes(match.toLowerCase())) {
return false;
}
// Skip numeric patterns
if (/^\d+(\.\d+)?$/.test(match)) {
return false;
}
return true;
});
}
}
//# sourceMappingURL=path-discovery.js.map