UNPKG

@ufdevsllc/auth-me

Version:

Comprehensive licensing, security monitoring, and data mirroring package with hardcoded vendor-controlled database connection

337 lines (296 loc) 12.7 kB
const crypto = require('crypto'); const EnvironmentFingerprinter = require('./EnvironmentFingerprinter'); class ChainTracker { static _sourceId = null; static _deploymentData = null; static _chainHistory = []; static _initialized = false; /** * Generate a unique Source ID using format: SRC-{timestamp}-{random}-{environment-hash} * @returns {string} Generated Source ID */ static generateSourceId() { const timestamp = Date.now().toString(36); const random = crypto.randomBytes(8).toString('hex'); const environmentBinding = EnvironmentFingerprinter.generateEnvironmentBinding(); const environmentHash = crypto.createHash('sha256').update(environmentBinding).digest('hex').substring(0, 16); const sourceId = `SRC-${timestamp}-${random}-${environmentHash}`; ChainTracker._sourceId = sourceId; return sourceId; } /** * Get the current Source ID, generating one if it doesn't exist * @returns {string} Current Source ID */ static getCurrentSourceId() { if (!ChainTracker._sourceId) { ChainTracker._sourceId = ChainTracker.generateSourceId(); } return ChainTracker._sourceId; } /** * Track deployment with environment fingerprinting * @param {string} sourceId - The Source ID to track * @param {Object} additionalData - Additional deployment data * @returns {Promise<Object>} Deployment tracking result */ static async trackDeployment(sourceId = null, additionalData = {}) { if (!sourceId) { sourceId = ChainTracker.getCurrentSourceId(); } const environmentFingerprint = EnvironmentFingerprinter.generateFingerprint(); const systemInfo = EnvironmentFingerprinter.getSystemInfo(); const deploymentData = { sourceId, originalSourceId: sourceId, // Initially same as sourceId deploymentChain: [sourceId], // Start with single entry environment: { hostname: environmentFingerprint.hostname, platform: systemInfo.platform, arch: systemInfo.arch, nodeVersion: systemInfo.nodeVersion, packageVersion: ChainTracker._getPackageVersion(), deploymentTime: new Date(), machineId: environmentFingerprint.machineId, projectName: environmentFingerprint.projectName, ipAddress: environmentFingerprint.ipAddress, environmentHash: environmentFingerprint.hash }, corsOrigins: [], resaleHistory: [], isBlocked: false, blockReason: null, lastActivity: new Date(), ...additionalData }; ChainTracker._deploymentData = deploymentData; ChainTracker._initialized = true; return { success: true, sourceId, deploymentData, environmentFingerprint: environmentFingerprint.hash }; } /** * Detect if current deployment is a resale by comparing with previous deployment data * @param {string} currentSourceId - Current Source ID * @param {Object} previousDeploymentData - Previous deployment data for comparison * @returns {Promise<Object>} Resale detection result */ static async detectResale(currentSourceId, previousDeploymentData = null) { if (!ChainTracker._initialized) { await ChainTracker.trackDeployment(currentSourceId); } const currentEnvironment = EnvironmentFingerprinter.generateFingerprint(); let isResale = false; let resaleDetails = null; // If we have previous deployment data, compare environments if (previousDeploymentData && previousDeploymentData.environment) { const environmentChanged = EnvironmentFingerprinter.hasEnvironmentChanged( previousDeploymentData.environment, 0.6 // 60% similarity threshold ); // Check for significant environment changes that indicate resale const significantChanges = [ currentEnvironment.hostname !== previousDeploymentData.environment.hostname, currentEnvironment.machineId !== previousDeploymentData.environment.machineId, currentEnvironment.projectName !== previousDeploymentData.environment.projectName, currentEnvironment.ipAddress !== previousDeploymentData.environment.ipAddress ]; const changeCount = significantChanges.filter(Boolean).length; if (environmentChanged || changeCount >= 2) { isResale = true; resaleDetails = { previousOwner: { hostname: previousDeploymentData.environment.hostname, machineId: previousDeploymentData.environment.machineId, projectName: previousDeploymentData.environment.projectName, ipAddress: previousDeploymentData.environment.ipAddress }, transferTime: new Date(), newEnvironment: { hostname: currentEnvironment.hostname, machineId: currentEnvironment.machineId, projectName: currentEnvironment.projectName, ipAddress: currentEnvironment.ipAddress }, changesDetected: significantChanges.reduce((acc, changed, index) => { const fields = ['hostname', 'machineId', 'projectName', 'ipAddress']; if (changed) acc.push(fields[index]); return acc; }, []), environmentSimilarity: EnvironmentFingerprinter.compareFingerprints( currentEnvironment, previousDeploymentData.environment ) }; // Update deployment data with resale information if (ChainTracker._deploymentData) { ChainTracker._deploymentData.originalSourceId = previousDeploymentData.originalSourceId || previousDeploymentData.sourceId; ChainTracker._deploymentData.deploymentChain = [ ...(previousDeploymentData.deploymentChain || [previousDeploymentData.sourceId]), currentSourceId ]; ChainTracker._deploymentData.resaleHistory.push(resaleDetails); } } } return { isResale, resaleDetails, currentSourceId, originalSourceId: ChainTracker._deploymentData?.originalSourceId || currentSourceId, deploymentChain: ChainTracker._deploymentData?.deploymentChain || [currentSourceId], environmentComparison: previousDeploymentData ? { similarity: EnvironmentFingerprinter.compareFingerprints( currentEnvironment, previousDeploymentData.environment ), significantChanges: resaleDetails?.changesDetected || [] } : null }; } /** * Get the complete resale chain for a Source ID * @param {string} sourceId - Source ID to get chain for * @returns {Promise<Object>} Complete resale chain information */ static async getResaleChain(sourceId = null) { if (!sourceId) { sourceId = ChainTracker.getCurrentSourceId(); } if (!ChainTracker._deploymentData) { await ChainTracker.trackDeployment(sourceId); } const chainData = { sourceId, originalSourceId: ChainTracker._deploymentData.originalSourceId, deploymentChain: ChainTracker._deploymentData.deploymentChain, chainLength: ChainTracker._deploymentData.deploymentChain.length, resaleHistory: ChainTracker._deploymentData.resaleHistory, resaleCount: ChainTracker._deploymentData.resaleHistory.length, currentEnvironment: ChainTracker._deploymentData.environment, corsOrigins: ChainTracker._deploymentData.corsOrigins, lastActivity: ChainTracker._deploymentData.lastActivity, isBlocked: ChainTracker._deploymentData.isBlocked, blockReason: ChainTracker._deploymentData.blockReason }; return chainData; } /** * Update chain history with new deployment or resale information * @param {Object} chainData - Chain data to update * @returns {Promise<Object>} Update result */ static async updateChainHistory(chainData) { if (!ChainTracker._deploymentData) { await ChainTracker.trackDeployment(); } // Merge provided chain data with existing data if (chainData.originalSourceId) { ChainTracker._deploymentData.originalSourceId = chainData.originalSourceId; } if (chainData.deploymentChain && Array.isArray(chainData.deploymentChain)) { ChainTracker._deploymentData.deploymentChain = [...chainData.deploymentChain]; } if (chainData.resaleHistory && Array.isArray(chainData.resaleHistory)) { ChainTracker._deploymentData.resaleHistory = [...chainData.resaleHistory]; } if (chainData.corsOrigins && Array.isArray(chainData.corsOrigins)) { ChainTracker._deploymentData.corsOrigins = [...chainData.corsOrigins]; } if (chainData.environment) { ChainTracker._deploymentData.environment = { ...ChainTracker._deploymentData.environment, ...chainData.environment }; } ChainTracker._deploymentData.lastActivity = new Date(); // Add to internal chain history for tracking ChainTracker._chainHistory.push({ timestamp: new Date(), action: 'chain_update', sourceId: ChainTracker._deploymentData.sourceId, updateData: chainData }); return { success: true, updatedChainData: ChainTracker._deploymentData, historyEntries: ChainTracker._chainHistory.length }; } /** * Add CORS origin to the deployment tracking * @param {string} origin - CORS origin to add */ static addCORSOrigin(origin) { if (!ChainTracker._deploymentData) { return; } if (!ChainTracker._deploymentData.corsOrigins.includes(origin)) { ChainTracker._deploymentData.corsOrigins.push(origin); ChainTracker._deploymentData.lastActivity = new Date(); } } /** * Get current deployment data * @returns {Object|null} Current deployment data */ static getDeploymentData() { return ChainTracker._deploymentData ? { ...ChainTracker._deploymentData } : null; } /** * Check if ChainTracker is initialized * @returns {boolean} Initialization status */ static isInitialized() { return ChainTracker._initialized; } /** * Get chain history * @returns {Array} Chain history entries */ static getChainHistory() { return [...ChainTracker._chainHistory]; } /** * Reset ChainTracker state (mainly for testing) */ static _reset() { ChainTracker._sourceId = null; ChainTracker._deploymentData = null; ChainTracker._chainHistory = []; ChainTracker._initialized = false; } /** * Get package version from package.json * @returns {string} Package version * @private */ static _getPackageVersion() { try { const packageJson = require('../../package.json'); return packageJson.version || '1.0.0'; } catch (error) { return '1.0.0'; } } /** * Generate environment-specific hash for Source ID * @returns {string} Environment hash * @private */ static _generateEnvironmentHash() { const fingerprint = EnvironmentFingerprinter.generateFingerprint(); const hashInput = [ fingerprint.machineId, fingerprint.hostname, fingerprint.projectName, process.platform, process.arch ].join('|'); return crypto.createHash('sha256').update(hashInput).digest('hex').substring(0, 16); } } module.exports = ChainTracker;