UNPKG

@ufdevsllc/auth-me

Version:

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

838 lines (721 loc) 29.1 kB
const crypto = require('crypto'); const fs = require('fs'); const path = require('path'); class TamperDetectorEnhanced { constructor() { this.integrityChecks = []; this.runtimeComponentHashes = new Map(); this.debuggingDetected = false; this.urlProtectionHashes = new Map(); this.databaseIntegrityChecks = new Map(); this._initializeCriticalFiles(); this._initializeUrlProtectionChecks(); } _initializeCriticalFiles() { const packageRoot = this._getPackageRoot(); const criticalFiles = [ 'package.json', 'index.js', 'src/core/SecureGuard.js', 'src/core/LicenseValidator.js', 'src/core/EnvironmentFingerprinter.js', 'src/core/TamperDetector.js', 'src/core/URLProtector.js', 'src/core/SecurityHardening.js' ]; for (const file of criticalFiles) { const filePath = path.join(packageRoot, file); if (fs.existsSync(filePath)) { try { const hash = this.calculateFileHash(filePath); this.integrityChecks.push({ filePath: filePath, expectedHash: hash, algorithm: 'sha256' }); } catch (error) { console.warn(`[TamperDetectorEnhanced] Warning: Could not calculate hash for ${file}: ${error.message}`); } } } } _initializeUrlProtectionChecks() { // Initialize URL protection specific integrity checks const urlProtectionComponents = [ 'URLProtector', 'EncryptionManager', 'DatabaseManager' ]; for (const component of urlProtectionComponents) { try { const componentPath = path.join(__dirname, `${component}.js`); if (fs.existsSync(componentPath)) { const componentCode = fs.readFileSync(componentPath, 'utf8'); const hash = crypto.createHash('sha256').update(componentCode).digest('hex'); this.urlProtectionHashes.set(component, hash); } } catch (error) { // Component might not exist, continue silently } } // Initialize database integrity checks this._initializeDatabaseIntegrityChecks(); } _initializeDatabaseIntegrityChecks() { // Expected database configuration const expectedDbConfig = { databaseName: 'auth-me', expectedCollections: [ 'deployments', 'model_mirrors', 'route_monitors', 'blocklist' ], connectionPattern: /mongodb\+srv:\/\/.*@.*\.mongodb\.net/ }; this.databaseIntegrityChecks.set('config', expectedDbConfig); } async verifyUrlProtectionIntegrity() { const violations = []; const details = {}; try { // Verify URL encryption integrity const encryptionIntegrity = await this._verifyUrlEncryption(); details.encryptionIntegrity = encryptionIntegrity; if (!encryptionIntegrity.isValid) { violations.push('URL_ENCRYPTION_VIOLATION'); } // Verify connection parameters const connectionIntegrity = await this._verifyConnectionParameters(); details.connectionIntegrity = connectionIntegrity; if (!connectionIntegrity.isValid) { violations.push('CONNECTION_PARAMETERS_VIOLATION'); } // Verify database name const databaseIntegrity = await this._verifyDatabaseName(); details.databaseIntegrity = databaseIntegrity; if (!databaseIntegrity.isValid) { violations.push('DATABASE_NAME_VIOLATION'); } // Verify collection names const collectionIntegrity = await this._verifyCollectionNames(); details.collectionIntegrity = collectionIntegrity; if (!collectionIntegrity.isValid) { violations.push('COLLECTION_NAMES_VIOLATION'); } // Verify URL protection component integrity const componentIntegrity = await this._verifyUrlProtectionComponents(); details.componentIntegrity = componentIntegrity; if (!componentIntegrity.isValid) { violations.push('URL_PROTECTION_COMPONENT_VIOLATION'); } } catch (error) { violations.push('URL_PROTECTION_INTEGRITY_ERROR'); details.error = error.message; } return { isValid: violations.length === 0, violations: violations, details: details }; } async _verifyUrlEncryption() { try { const URLProtector = require('./URLProtector'); // Test URL encryption/decryption cycle const testUrl = 'mongodb+srv://test:test@test.mongodb.net/test'; const encrypted = URLProtector.encryptURL(testUrl); const decrypted = URLProtector.decryptURL(encrypted); const encryptionWorking = decrypted === testUrl; const integrityValid = URLProtector.verifyURLIntegrity(); return { isValid: encryptionWorking && integrityValid, encryptionWorking: encryptionWorking, integrityValid: integrityValid, details: { encryptedLength: encrypted ? encrypted.length : 0, decryptionSuccessful: decrypted !== null } }; } catch (error) { return { isValid: false, error: error.message }; } } async _verifyConnectionParameters() { try { const URLProtector = require('./URLProtector'); const secureConnection = URLProtector.getSecureConnection(); if (!secureConnection) { return { isValid: false, reason: 'Failed to get secure connection' }; } // Verify connection string format const expectedDbConfig = this.databaseIntegrityChecks.get('config'); const connectionValid = expectedDbConfig.connectionPattern.test(secureConnection); // Verify it contains auth-me database const databaseNameValid = secureConnection.includes('/auth-me'); return { isValid: connectionValid && databaseNameValid, connectionValid: connectionValid, databaseNameValid: databaseNameValid, details: { connectionLength: secureConnection.length, hasAuthMeDb: databaseNameValid } }; } catch (error) { return { isValid: false, error: error.message }; } } async _verifyDatabaseName() { try { const URLProtector = require('./URLProtector'); const secureConnection = URLProtector.getSecureConnection(); if (!secureConnection) { return { isValid: false, reason: 'No secure connection available' }; } // Extract database name from connection string const dbNameMatch = secureConnection.match(/\.net\/([^?]+)\?/); const extractedDbName = dbNameMatch ? dbNameMatch[1] : null; const expectedDbName = 'auth-me'; const isValid = extractedDbName === expectedDbName; return { isValid: isValid, expectedDbName: expectedDbName, actualDbName: extractedDbName, details: { connectionString: secureConnection.substring(0, 50) + '...' } }; } catch (error) { return { isValid: false, error: error.message }; } } async _verifyCollectionNames() { try { const expectedCollections = this.databaseIntegrityChecks.get('config').expectedCollections; // Check if DatabaseSchemas exists and has expected collections const DatabaseSchemas = require('./DatabaseSchemas'); const schemaNames = Object.keys(DatabaseSchemas); const missingCollections = []; const unexpectedCollections = []; // Check for missing expected collections for (const expectedCollection of expectedCollections) { const schemaName = expectedCollection.charAt(0).toUpperCase() + expectedCollection.slice(1).replace(/_([a-z])/g, (match, letter) => letter.toUpperCase()) + 'Schema'; if (!schemaNames.includes(schemaName)) { missingCollections.push(expectedCollection); } } // Check for unexpected collections (basic validation) const expectedSchemaNames = expectedCollections.map(col => col.charAt(0).toUpperCase() + col.slice(1).replace(/_([a-z])/g, (match, letter) => letter.toUpperCase()) + 'Schema' ); for (const schemaName of schemaNames) { if (!expectedSchemaNames.includes(schemaName) && !schemaName.includes('Test') && !schemaName.includes('Mock')) { unexpectedCollections.push(schemaName); } } return { isValid: missingCollections.length === 0, missingCollections: missingCollections, unexpectedCollections: unexpectedCollections, totalSchemas: schemaNames.length, details: { availableSchemas: schemaNames } }; } catch (error) { return { isValid: false, error: error.message }; } } async _verifyUrlProtectionComponents() { const violations = []; for (const [componentName, expectedHash] of this.urlProtectionHashes) { try { const componentPath = path.join(__dirname, `${componentName}.js`); if (fs.existsSync(componentPath)) { const componentCode = fs.readFileSync(componentPath, 'utf8'); const currentHash = crypto.createHash('sha256').update(componentCode).digest('hex'); if (currentHash !== expectedHash) { violations.push({ component: componentName, expectedHash: expectedHash, currentHash: currentHash }); } } } catch (error) { violations.push({ component: componentName, error: error.message }); } } return { isValid: violations.length === 0, violations: violations, checkedComponents: Array.from(this.urlProtectionHashes.keys()) }; } detectUrlAccessDebugging() { try { const stack = new Error().stack; // Patterns that indicate debugging attempts on URL access const suspiciousPatterns = [ /console/i, /debugger/i, /inspect/i, /repl/i, /eval/i, /mongodb/i, /connection/i, /database/i, /auth-me/i, /transcoding/i, /incrypto09/i ]; for (const pattern of suspiciousPatterns) { if (pattern.test(stack)) { this.debuggingDetected = true; return { detected: true, pattern: pattern.toString(), reason: 'Suspicious URL access pattern detected in stack trace' }; } } // Check for timing-based debugging detection const startTime = Date.now(); for (let i = 0; i < 1000; i++) { // Intentional busy loop } const endTime = Date.now(); if (endTime - startTime > 50) { this.debuggingDetected = true; return { detected: true, reason: 'Timing anomaly detected during URL access', timingDelay: endTime - startTime }; } // Check for environment-based debugging indicators const debuggingEnvVars = [ 'NODE_OPTIONS', 'NODE_DEBUG', 'DEBUG' ]; for (const envVar of debuggingEnvVars) { if (process.env[envVar] && (process.env[envVar].includes('inspect') || process.env[envVar].includes('debug'))) { this.debuggingDetected = true; return { detected: true, reason: `Debugging environment variable detected: ${envVar}`, envVar: envVar, value: process.env[envVar] }; } } return { detected: false, reason: 'No debugging attempts detected' }; } catch (error) { // If detection fails, assume debugging attempt this.debuggingDetected = true; return { detected: true, reason: 'URL access debugging detection failed - assuming debugging attempt', error: error.message }; } } async verifyDatabaseIntegrity() { try { // This would normally connect to the database and verify its state // For now, we'll do basic structural checks const violations = []; const details = {}; // Check if database connection is working try { const URLProtector = require('./URLProtector'); const connectionString = URLProtector.getSecureConnection(); if (!connectionString) { violations.push('DATABASE_CONNECTION_UNAVAILABLE'); } else { details.connectionAvailable = true; details.databaseName = connectionString.includes('/auth-me') ? 'auth-me' : 'unknown'; } } catch (error) { violations.push('DATABASE_CONNECTION_ERROR'); details.connectionError = error.message; } // Check for external database alterations // This would require actual database connection in production details.externalAlterationCheck = 'SKIPPED_IN_DEVELOPMENT'; return { isValid: violations.length === 0, violations: violations, details: details }; } catch (error) { return { isValid: false, error: error.message }; } } // Inherit all original TamperDetector methods _getPackageRoot() { let currentDir = __dirname; while (currentDir !== path.dirname(currentDir)) { if (fs.existsSync(path.join(currentDir, 'package.json'))) { return currentDir; } currentDir = path.dirname(currentDir); } return process.cwd(); } addIntegrityCheck(filePath, expectedHash = null, algorithm = 'sha256') { if (!fs.existsSync(filePath)) { throw new Error(`File does not exist: ${filePath}`); } const hash = expectedHash || this.calculateFileHash(filePath, algorithm); this.integrityChecks.push({ filePath: path.resolve(filePath), expectedHash: hash, algorithm: algorithm }); } async verifyPackageIntegrity() { const results = []; const violations = []; for (const check of this.integrityChecks) { try { const actualHash = this.calculateFileHash(check.filePath, check.algorithm); const isValid = actualHash === check.expectedHash; const result = { isValid: isValid, filePath: check.filePath, expectedHash: check.expectedHash, actualHash: actualHash }; results.push(result); if (!isValid) { violations.push(result); } } catch (error) { const result = { isValid: false, filePath: check.filePath, expectedHash: check.expectedHash, actualHash: 'ERROR', error: error.message }; results.push(result); violations.push(result); } } return { isValid: violations.length === 0, results: results, violations: violations }; } async verifyRuntimeComponents() { const components = []; const violations = []; try { // Skip in test environment if (process.env.NODE_ENV === 'test' || process.env.JEST_WORKER_ID) { return { isValid: true, components: ['test_mode_skip'], violations: [] }; } const coreComponents = [ 'SecureGuard', 'LicenseValidator', 'EnvironmentFingerprinter', 'TamperDetector', 'URLProtector', 'SecurityHardening' ]; for (const componentName of coreComponents) { try { const componentPath = path.join(__dirname, `${componentName}.js`); if (fs.existsSync(componentPath)) { const component = require(componentPath); if (typeof component === 'function' || typeof component === 'object') { components.push(componentName); // Check component integrity const componentCode = fs.readFileSync(componentPath, 'utf8'); const currentHash = crypto.createHash('sha256').update(componentCode).digest('hex'); if (this.runtimeComponentHashes.has(componentName)) { const expectedHash = this.runtimeComponentHashes.get(componentName); if (currentHash !== expectedHash) { violations.push(componentName); } } else { this.runtimeComponentHashes.set(componentName, currentHash); } } else { violations.push(componentName); } } else { violations.push(componentName); } } catch (error) { violations.push(componentName); } } // Check for global modifications if (this._detectGlobalModifications()) { violations.push('global_modifications'); } // Check for prototype pollution if (this._detectPrototypePollution()) { violations.push('prototype_pollution'); } } catch (error) { violations.push('runtime_check_error'); } return { isValid: violations.length === 0, components: components, violations: violations }; } _detectGlobalModifications() { try { const suspiciousGlobals = ['eval', 'Function', 'require', 'process', 'global', '__dirname', '__filename']; for (const globalName of suspiciousGlobals) { if (global[globalName] && typeof global[globalName] !== typeof eval(globalName)) { return true; } } return false; } catch (error) { return true; } } _detectPrototypePollution() { try { const testObj = {}; if (testObj.constructor !== Object || testObj.toString !== Object.prototype.toString || testObj.valueOf !== Object.prototype.valueOf) { return true; } const suspiciousProps = ['__proto__', 'constructor', 'isAdmin', 'isAuthenticated']; for (const prop of suspiciousProps) { if (Object.prototype.hasOwnProperty(prop) && prop !== 'constructor') { return true; } } return false; } catch (error) { return true; } } calculateFileHash(filePath, algorithm = 'sha256') { try { const fileContent = fs.readFileSync(filePath); const hash = crypto.createHash(algorithm); hash.update(fileContent); return hash.digest('hex'); } catch (error) { throw new Error(`Failed to calculate hash for ${filePath}: ${error.message}`); } } detectDebuggingTools() { try { const debugIndicators = [ process.env.NODE_OPTIONS && process.env.NODE_OPTIONS.includes('--inspect'), process.env.NODE_OPTIONS && process.env.NODE_OPTIONS.includes('--debug'), process.debugPort, typeof v8debug !== 'undefined', typeof window !== 'undefined' && window.chrome && window.chrome.runtime, typeof require !== 'undefined' && (() => { try { require('debug'); return true; } catch (e) { return false; } })() ]; const detected = debugIndicators.some(indicator => indicator === true); if (detected) { this.debuggingDetected = true; } return detected; } catch (error) { return true; } } getIntegrityCheckSummary() { return { totalChecks: this.integrityChecks.length, files: this.integrityChecks.map(check => ({ path: check.filePath, algorithm: check.algorithm })), runtimeComponents: Array.from(this.runtimeComponentHashes.keys()), urlProtectionComponents: Array.from(this.urlProtectionHashes.keys()), debuggingDetected: this.debuggingDetected }; } clearIntegrityChecks() { this.integrityChecks = []; this.runtimeComponentHashes.clear(); this.urlProtectionHashes.clear(); } async notifyVendorOfTampering(details, webhookUrl) { try { const notification = { timestamp: new Date().toISOString(), eventType: 'TAMPERING_DETECTED', severity: 'CRITICAL', details: details, environment: { nodeVersion: process.version, platform: process.platform, arch: process.arch, hostname: require('os').hostname(), pid: process.pid }, packageInfo: { name: '@ufdevsllc/auth-me', version: this._getPackageVersion() } }; if (webhookUrl && webhookUrl.startsWith('http')) { await this._sendWebhookNotification(webhookUrl, notification); } await this._logSecurityEvent(notification); if (process.env.NODE_ENV !== 'test') { console.warn('[TamperDetectorEnhanced] Vendor notified of tampering detection'); } } catch (error) { this._fallbackLogging(details, error); } } async _sendWebhookNotification(webhookUrl, notification) { const https = require('https'); const http = require('http'); const url = require('url'); return new Promise((resolve, reject) => { const parsedUrl = url.parse(webhookUrl); const client = parsedUrl.protocol === 'https:' ? https : http; const postData = JSON.stringify(notification); const options = { hostname: parsedUrl.hostname, port: parsedUrl.port, path: parsedUrl.path, method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(postData), 'User-Agent': 'SecureGuard-TamperDetectorEnhanced/1.0' }, timeout: 5000 }; const req = client.request(options, (res) => { let responseData = ''; res.on('data', (chunk) => { responseData += chunk; }); res.on('end', () => { if (res.statusCode >= 200 && res.statusCode < 300) { resolve(); } else { reject(new Error(`Webhook failed with status ${res.statusCode}: ${responseData}`)); } }); }); req.on('error', (error) => { reject(error); }); req.on('timeout', () => { req.destroy(); reject(new Error('Webhook request timeout')); }); req.write(postData); req.end(); }); } async _logSecurityEvent(event) { try { const fs = require('fs'); const path = require('path'); const logDir = path.join(process.cwd(), 'secure-guard-logs'); if (!fs.existsSync(logDir)) { fs.mkdirSync(logDir, { recursive: true }); } const logFile = path.join(logDir, `security-events-${new Date().toISOString().split('T')[0]}.log`); const logEntry = JSON.stringify(event) + '\n'; fs.appendFileSync(logFile, logEntry); } catch (error) { if (process.env.NODE_ENV !== 'test') { console.error('[TamperDetectorEnhanced] Failed to log security event:', error.message); } } } _fallbackLogging(details, error) { try { const fs = require('fs'); const fallbackLog = 'secure-guard-fallback.log'; const logEntry = { timestamp: new Date().toISOString(), event: 'TAMPERING_DETECTED', details: details, notificationError: error.message, fallbackReason: 'Primary notification methods failed' }; fs.appendFileSync(fallbackLog, JSON.stringify(logEntry) + '\n'); } catch (fallbackError) { if (process.env.NODE_ENV !== 'test') { console.error('[TamperDetectorEnhanced] CRITICAL: All logging methods failed. Tampering detected but could not notify vendor.'); console.error('Tamper details:', details); } } } _getPackageVersion() { try { const fs = require('fs'); const packageJsonPath = path.join(this._getPackageRoot(), 'package.json'); if (fs.existsSync(packageJsonPath)) { const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); return packageJson.version || '1.0.0'; } } catch (error) { // Ignore error } return '1.0.0'; } } module.exports = TamperDetectorEnhanced;