UNPKG

@codecot/pw-checker

Version:

A comprehensive CLI tool to audit passwords locally using HIBP and import from Chrome/Bitwarden

265 lines • 10.1 kB
#!/usr/bin/env node import { exec } from 'child_process'; import { promisify } from 'util'; import chalk from 'chalk'; import { getDatabase } from './database.js'; const execAsync = promisify(exec); // Critical account categories for enhanced risk scoring const CRITICAL_CATEGORIES = new Set([ 'banking', 'finance', 'investment', 'cryptocurrency', 'work', 'email', 'government', 'healthcare', 'insurance', 'social', 'shopping', 'cloud' ]); /** * Categorize accounts based on domain or name */ function categorizeAccount(name, uri) { const lowerName = name.toLowerCase(); const lowerUri = uri?.toLowerCase() || ''; // Banking and Finance if (lowerName.includes('bank') || lowerUri.includes('bank') || lowerName.includes('paypal') || lowerUri.includes('paypal') || lowerName.includes('stripe') || lowerUri.includes('stripe') || lowerName.includes('credit') || lowerName.includes('loan')) { return 'banking'; } // Work/Professional if (lowerName.includes('work') || lowerName.includes('office') || lowerName.includes('company') || lowerName.includes('enterprise') || lowerUri.includes('slack') || lowerUri.includes('teams') || lowerUri.includes('zoom') || lowerUri.includes('jira')) { return 'work'; } // Email if (lowerName.includes('email') || lowerName.includes('mail') || lowerUri.includes('gmail') || lowerUri.includes('outlook') || lowerUri.includes('yahoo') || lowerUri.includes('@')) { return 'email'; } // Social Media if (lowerUri.includes('facebook') || lowerUri.includes('twitter') || lowerUri.includes('instagram') || lowerUri.includes('linkedin') || lowerUri.includes('tiktok') || lowerUri.includes('snapchat')) { return 'social'; } // Cloud Services if (lowerUri.includes('google') || lowerUri.includes('microsoft') || lowerUri.includes('apple') || lowerUri.includes('amazon') || lowerUri.includes('dropbox') || lowerUri.includes('icloud')) { return 'cloud'; } // Healthcare if (lowerName.includes('health') || lowerName.includes('medical') || lowerName.includes('doctor') || lowerName.includes('hospital')) { return 'healthcare'; } // Government if (lowerName.includes('gov') || lowerUri.includes('.gov') || lowerName.includes('tax') || lowerName.includes('irs')) { return 'government'; } // Shopping if (lowerUri.includes('amazon') || lowerUri.includes('ebay') || lowerUri.includes('shop') || lowerName.includes('store')) { return 'shopping'; } return 'other'; } /** * Check if Bitwarden CLI is authenticated */ async function checkBitwardenStatus() { try { const { stdout } = await execAsync('bw status'); const status = JSON.parse(stdout); return status.status === 'unlocked'; } catch (error) { console.error(chalk.red('āŒ Error checking Bitwarden status:'), error); return false; } } /** * Get session token for Bitwarden CLI */ async function getBitwardenSession() { try { // Check if already unlocked const isUnlocked = await checkBitwardenStatus(); if (isUnlocked) { return 'already_unlocked'; } console.log(chalk.yellow('šŸ” Bitwarden vault is locked. Please unlock it first:')); console.log(chalk.cyan(' bw unlock')); console.log(chalk.cyan(' export BW_SESSION="<session_key>"')); console.log(chalk.yellow('Then run this import command again.')); return null; } catch (error) { console.error(chalk.red('āŒ Error getting Bitwarden session:'), error); return null; } } /** * Export items from Bitwarden */ async function exportBitwardenItems() { try { const { stdout } = await execAsync('bw list items'); return JSON.parse(stdout); } catch (error) { console.error(chalk.red('āŒ Error exporting items from Bitwarden:'), error); throw error; } } /** * Export folders from Bitwarden for categorization */ async function exportBitwardenFolders() { try { const { stdout } = await execAsync('bw list folders'); return JSON.parse(stdout); } catch (error) { console.log(chalk.yellow('āš ļø Could not fetch folders (this is optional)')); return []; } } /** * Import passwords from Bitwarden vault */ export async function importFromBitwarden() { console.log(chalk.blue('šŸ”„ Starting Bitwarden import...')); // Check if Bitwarden CLI is available try { await execAsync('bw --version'); } catch (error) { console.error(chalk.red('āŒ Bitwarden CLI not found. Please install it first:')); console.log(chalk.cyan(' npm install -g @bitwarden/cli')); return; } // Check authentication status const session = await getBitwardenSession(); if (!session) { return; } try { // Export items and folders console.log(chalk.blue('šŸ“¤ Exporting items from Bitwarden...')); const [items, folders] = await Promise.all([ exportBitwardenItems(), exportBitwardenFolders() ]); // Create folder lookup const folderMap = new Map(); folders.forEach(folder => { folderMap.set(folder.id, folder.name); }); // Filter login items only const loginItems = items.filter(item => item.type === 1 && item.login?.password && item.login?.username); console.log(chalk.green(`āœ… Found ${loginItems.length} login items`)); if (loginItems.length === 0) { console.log(chalk.yellow('āš ļø No login items found in Bitwarden vault')); return; } // Open database const db = await getDatabase(); let imported = 0; let skipped = 0; for (const item of loginItems) { try { const uri = item.login?.uris?.[0]?.uri || `bitwarden-item-${item.id}`; const username = item.login?.username || ''; const password = item.login?.password || ''; const folderName = item.folderId ? folderMap.get(item.folderId) : undefined; const category = categorizeAccount(item.name, uri); // Check if entry already exists const existing = await db.get('SELECT id FROM pw_entries WHERE url = ? AND username = ?', [uri, username]); if (existing) { skipped++; continue; } // Insert new entry await db.run(` INSERT INTO pw_entries ( url, username, password, password_hash, source, date_created, is_compromised, category, folder_name ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) `, [ uri, username, password, '', // Will be populated during breach check 'bitwarden', new Date().toISOString(), 0, // Will be checked later category, folderName || null ]); imported++; // Progress indicator if (imported % 50 === 0) { console.log(chalk.blue(`šŸ“„ Imported ${imported} entries...`)); } } catch (error) { console.error(chalk.red(`āŒ Error importing item "${item.name}":`, error)); skipped++; } } await db.close(); console.log(chalk.green(`\nāœ… Bitwarden import completed!`)); console.log(chalk.cyan(`šŸ“Š Summary:`)); console.log(chalk.cyan(` • Imported: ${imported} entries`)); console.log(chalk.cyan(` • Skipped (duplicates): ${skipped} entries`)); console.log(chalk.cyan(` • Total processed: ${imported + skipped} entries`)); if (imported > 0) { console.log(chalk.yellow('\nšŸ’” Next steps:')); console.log(chalk.yellow(' • Run breach check: npm run check:breaches')); console.log(chalk.yellow(' • Calculate risk scores: npm run risk:calculate')); console.log(chalk.yellow(' • Generate security report: npm run risk:report')); } } catch (error) { console.error(chalk.red('āŒ Error during Bitwarden import:'), error); throw error; } } /** * CLI interface */ async function main() { const args = process.argv.slice(2); if (args.includes('--help') || args.includes('-h')) { console.log(chalk.blue('šŸ” Bitwarden Import Tool')); console.log(''); console.log('Usage:'); console.log(' npm run import:bitwarden Import passwords from Bitwarden'); console.log(''); console.log('Prerequisites:'); console.log(' 1. Install Bitwarden CLI: npm install -g @bitwarden/cli'); console.log(' 2. Login to Bitwarden: bw login'); console.log(' 3. Unlock vault: bw unlock'); console.log(' 4. Export session: export BW_SESSION="<session_key>"'); console.log(''); console.log('Features:'); console.log(' • Imports login items from Bitwarden vault'); console.log(' • Automatically categorizes accounts for risk scoring'); console.log(' • Preserves folder structure from Bitwarden'); console.log(' • Skips duplicate entries'); console.log(' • Integrates with existing risk scoring system'); return; } await importFromBitwarden(); } // Run if called directly if (import.meta.url === `file://${process.argv[1]}`) { main().catch(error => { console.error(chalk.red('āŒ Fatal error:'), error); process.exit(1); }); } //# sourceMappingURL=importFromBitwarden.js.map