UNPKG

@flamesshield/rules-engine

Version:

Independent rules engine for security analysis of Firebase

240 lines 10.4 kB
/** * Computed Properties Utility Functions * * This module provides utility functions for calculating computed properties * from ProjectInformation objects, replacing the old fact-transformer approach. */ /** * Calculates the enforcement ratio for App Check services * @param projectInfo - The project information object * @returns A number between 0 and 1 representing the ratio of enforced services */ export function calculateEnforcementRatio(projectInfo) { if (!projectInfo.app_check || !projectInfo.app_check.services) { return 0; } const totalServices = projectInfo.app_check.services.length; if (totalServices === 0) { return 0; } const enforcedServices = projectInfo.app_check.services.filter(service => service.is_enforced).length; return enforcedServices / totalServices; } /** * Counts the number of apps without attestation providers * @param projectInfo - The project information object * @returns The count of apps without attestation providers */ export function countAppsWithoutAttestationProviders(projectInfo) { if (!projectInfo.app_check || !projectInfo.app_check.apps) { return 0; } return projectInfo.app_check.apps.filter(app => !app.attestation_providers || app.attestation_providers.length === 0).length; } /** * Calculates a simple security score based on various factors * @param projectInfo - The project information object * @returns A number between 0 and 1 representing the security score */ export function calculateSecurityScore(projectInfo) { let score = 0; const weights = { auth: 0.3, app_check: 0.3, enforcement: 0.2, mfa: 0.1, emailVerification: 0.1 }; // Auth configuration score if (projectInfo.auth) { const authScore = projectInfo.auth.enforced ? 1 : 0; score += authScore * weights.auth; } // App Check score if (projectInfo.app_check) { const appCheckScore = projectInfo.app_check.app_check_enabled ? 1 : 0; score += appCheckScore * weights.app_check; } // Enforcement ratio score const enforcementRatio = calculateEnforcementRatio(projectInfo); score += enforcementRatio * weights.enforcement; // MFA score if (projectInfo.auth?.mfa_enabled) { score += weights.mfa; } // Email verification score if (projectInfo.auth?.email_verification_enabled) { score += weights.emailVerification; } return Math.min(score, 1); // Cap at 1 } /** * Determines if the project has secrets using the function_secrets scan results * @param projectInfo - The project information object * @returns True if secrets are detected, false otherwise */ export function hasSecrets(projectInfo) { // Primary check: Use function_secrets scan results (authoritative source) if (projectInfo.function_secrets && projectInfo.function_secrets.length > 0) { return projectInfo.function_secrets.some(scanResult => scanResult.secretsCount > 0); } return false; } /** * Determines if the project has HTTP triggered functions * @param projectInfo - The project information object * @returns True if HTTP triggered functions are found, false otherwise */ export function hasHttpTriggeredFunctions(projectInfo) { // Check functionsv1 for HTTP triggers if (projectInfo.functionsv1 && projectInfo.functionsv1.length > 0) { const hasHttpV1 = projectInfo.functionsv1.some((func) => func.trigger === 'HTTP' || func.trigger === 'CALLABLE'); if (hasHttpV1) return true; } // Check functionsv2 for HTTP triggers (CALLABLE type) if (projectInfo.functionsv2 && projectInfo.functionsv2.length > 0) { const hasCallableV2 = projectInfo.functionsv2.some((func) => func.trigger === 'CALLABLE' || func.trigger === 'HTTP'); if (hasCallableV2) return true; } return false; } /** * Determines if this is a multi-region deployment * @param projectInfo - The project information object * @returns True if deployed across multiple regions, false otherwise */ export function isMultiRegionDeployment(projectInfo) { const regions = new Set(); // Check functionsv1 regions if (projectInfo.functionsv1) { projectInfo.functionsv1.forEach((func) => { if (func.location) { regions.add(func.location); } }); } // Check functionsv2 regions if (projectInfo.functionsv2) { projectInfo.functionsv2.forEach((func) => { if (func.location) { regions.add(func.location); } }); } // Check database regions if (projectInfo.databases) { projectInfo.databases.forEach(db => { if (db.location) { regions.add(db.location); } }); } // Check storage bucket regions if (projectInfo.storage_buckets) { projectInfo.storage_buckets.forEach(bucket => { if (bucket.location) { regions.add(bucket.location); } }); } return regions.size > 1; } import { SecurityAnalysisClient } from '../api/SecurityAnalysisClient.js'; /** * Creates a vulnerability lookup object that can be used to check if specific vulnerability IDs exist * @param vulnerabilities - The array of vulnerability findings * @returns An object with vulnerability IDs as keys and boolean values */ export function createVulnerabilityLookup(vulnerabilities) { if (!vulnerabilities || vulnerabilities.length === 0) { return {}; } const lookup = {}; vulnerabilities.forEach(vulnerability => { if (vulnerability.vulnerabilityId) { lookup[vulnerability.vulnerabilityId] = true; } }); return lookup; } /** * Creates an enriched ProjectInformation object with all computed properties * @param projectInfo - The base project information object * @returns An enriched project information object with computed properties */ export async function enrichProjectInformation(projectInfo) { const totalServices = projectInfo.app_check?.services?.length || 0; const enforcedServices = projectInfo.app_check?.services?.filter(s => s.is_enforced).length || 0; const unenforced = totalServices - enforcedServices; const totalApps = projectInfo.app_check?.apps?.length || 0; const appsWithoutProviders = countAppsWithoutAttestationProviders(projectInfo); const appsWithProviders = totalApps - appsWithoutProviders; // Helper function to check if a specific service is unenforced const isServiceUnenforced = (serviceName) => { const service = projectInfo.app_check?.services?.find(s => s.name === serviceName); return service ? !service.is_enforced : false; }; let firestore_vulnerabilities = []; if (projectInfo.firestore_rules) { try { const client = new SecurityAnalysisClient(); firestore_vulnerabilities = await client.analyzeFirestoreRules(projectInfo.firestore_rules); } catch (e) { firestore_vulnerabilities = []; } } const computed = { enforcement_ratio: calculateEnforcementRatio(projectInfo), apps_without_attestation_providers: appsWithoutProviders, security_score: calculateSecurityScore(projectInfo), has_secrets: hasSecrets(projectInfo), multi_region_deployment: isMultiRegionDeployment(projectInfo), has_http_triggered_functions: hasHttpTriggeredFunctions(projectInfo), total_services: totalServices, enforced_services: enforcedServices, unenforced_services: unenforced, total_apps: totalApps, apps_with_attestation_providers: appsWithProviders, firestore_vulnerabilities: firestore_vulnerabilities || [], fails_rule: createVulnerabilityLookup(firestore_vulnerabilities || []), // Service-specific enforcement flags auth_service_unenforced: isServiceUnenforced('identitytoolkit.googleapis.com'), functions_service_unenforced: isServiceUnenforced('cloudfunctions.googleapis.com'), dataconnect_service_unenforced: isServiceUnenforced('firebasedataconnect.googleapis.com'), ailogic_service_unenforced: isServiceUnenforced('firebaseailogic.googleapis.com'), google_identity_ios_service_unenforced: isServiceUnenforced('googleidentity.googleapis.com'), maps_js_service_unenforced: isServiceUnenforced('maps-js'), places_api_service_unenforced: isServiceUnenforced('places-backend.googleapis.com'), rtdb_service_unenforced: isServiceUnenforced('firebasedatabase.googleapis.com'), storage_service_unenforced: isServiceUnenforced('storage.googleapis.com'), }; return { ...projectInfo, has_secrets: hasSecrets(projectInfo), computed, // Add direct access properties for test compatibility total_apps: totalApps, total_services: totalServices, unenforced_services: unenforced, enforcement_coverage_ratio: totalServices > 0 ? enforcedServices / totalServices : 0, security_summary: projectInfo.app_check?.security_summary, apps_without_attestation_providers: appsWithoutProviders, has_http_triggered_functions: hasHttpTriggeredFunctions(projectInfo), firestore_vulnerabilities: firestore_vulnerabilities || [], fails_rule: createVulnerabilityLookup(firestore_vulnerabilities || []), // Service-specific enforcement flags auth_service_unenforced: isServiceUnenforced('identitytoolkit.googleapis.com'), functions_service_unenforced: isServiceUnenforced('cloudfunctions.googleapis.com'), dataconnect_service_unenforced: isServiceUnenforced('firebasedataconnect.googleapis.com'), ailogic_service_unenforced: isServiceUnenforced('firebaseailogic.googleapis.com'), google_identity_ios_service_unenforced: isServiceUnenforced('googleidentity.googleapis.com'), maps_js_service_unenforced: isServiceUnenforced('maps-js'), places_api_service_unenforced: isServiceUnenforced('places-backend.googleapis.com'), rtdb_service_unenforced: isServiceUnenforced('firebasedatabase.googleapis.com'), storage_service_unenforced: isServiceUnenforced('storage.googleapis.com'), }; } //# sourceMappingURL=computed-properties.js.map