/** * Credential Generator - CVE-3 Remediation * * Fixes hardcoded default credentials by providing secure random * credential generation for installation and runtime. * * Security Properties: * - Uses crypto.randomBytes for cryptographically secure randomness * - Configurable entropy levels * - No hardcoded defaults stored in code * - Secure credential storage recommendations * * @module v3/security/credential-generator */ import { randomBytes, randomUUID } from 'crypto'; export class CredentialGeneratorError extends Error { code; constructor(message, code) { super(message); this.code = code; this.name = 'CredentialGeneratorError'; } } /** * Character sets for credential generation */ const CHARSETS = { UPPERCASE: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', LOWERCASE: 'abcdefghijklmnopqrstuvwxyz', DIGITS: '0123456789', SPECIAL: '!@#$%^&*()_+-=[]{}|;:,.<>?', // URL-safe characters for API keys URL_SAFE: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_', // Hex characters for secrets HEX: '0123456789abcdef', }; /** * Secure credential generator. * * This class provides cryptographically secure credential generation * to replace hardcoded default credentials. * * @example * ```typescript * const generator = new CredentialGenerator(); * const credentials = generator.generateInstallationCredentials(); * // Store credentials securely (environment variables, secrets manager) * ``` */ export class CredentialGenerator { config; constructor(config = {}) { this.config = { passwordLength: config.passwordLength ?? 32, apiKeyLength: config.apiKeyLength ?? 48, secretLength: config.secretLength ?? 64, passwordCharset: config.passwordCharset ?? CHARSETS.UPPERCASE + CHARSETS.LOWERCASE + CHARSETS.DIGITS + CHARSETS.SPECIAL, apiKeyCharset: config.apiKeyCharset ?? CHARSETS.URL_SAFE, }; this.validateConfig(); } /** * Validates configuration parameters. */ validateConfig() { if (this.config.passwordLength < 16) { throw new CredentialGeneratorError('Password length must be at least 16 characters', 'INVALID_PASSWORD_LENGTH'); } if (this.config.apiKeyLength < 32) { throw new CredentialGeneratorError('API key length must be at least 32 characters', 'INVALID_API_KEY_LENGTH'); } if (this.config.secretLength < 32) { throw new CredentialGeneratorError('Secret length must be at least 32 characters', 'INVALID_SECRET_LENGTH'); } } /** * Generates a cryptographically secure random string using rejection sampling * to eliminate modulo bias. * * @param length - Length of the string to generate * @param charset - Character set to use * @returns Random string */ generateSecureString(length, charset) { const charsetLength = charset.length; const result = new Array(length); // Calculate rejection threshold to eliminate modulo bias // For a byte (0-255), we reject values >= (256 - (256 % charsetLength)) // This ensures uniform distribution over charset indices const maxValidValue = 256 - (256 % charsetLength); let i = 0; while (i < length) { // Generate more random bytes than needed to reduce iterations const randomBuffer = randomBytes(Math.max(length - i, 16)); for (let j = 0; j < randomBuffer.length && i < length; j++) { const randomValue = randomBuffer[j]; // Rejection sampling: only accept values below threshold if (randomValue < maxValidValue) { result[i] = charset[randomValue % charsetLength]; i++; } // Values >= maxValidValue are rejected to avoid bias } } return result.join(''); } /** * Generates a secure random password. * * @param length - Optional custom length (default from config) * @returns Secure random password */ generatePassword(length) { const len = length ?? this.config.passwordLength; // Ensure password contains at least one of each required character type const password = this.generateSecureString(len, this.config.passwordCharset); // Validate the generated password meets requirements if (!this.hasRequiredCharacterTypes(password)) { // Regenerate if requirements not met (rare case) return this.generatePassword(length); } return password; } /** * Checks if password has required character types. */ hasRequiredCharacterTypes(password) { const hasUppercase = /[A-Z]/.test(password); const hasLowercase = /[a-z]/.test(password); const hasDigit = /\d/.test(password); const hasSpecial = /[!@#$%^&*()_+\-=\[\]{}|;:,.<>?]/.test(password); return hasUppercase && hasLowercase && hasDigit && hasSpecial; } /** * Generates a secure API key. * * @param prefix - Optional prefix for the key (e.g., 'cf_') * @returns API key credential with metadata */ generateApiKey(prefix = 'cf_') { const keyBody = this.generateSecureString(this.config.apiKeyLength - prefix.length, this.config.apiKeyCharset); const key = `${prefix}${keyBody}`; const keyId = randomUUID(); return { key, prefix, keyId, createdAt: new Date(), }; } /** * Generates a secure secret for JWT, sessions, etc. * * @param length - Optional custom length (default from config) * @returns Hex-encoded secret */ generateSecret(length) { const len = length ?? this.config.secretLength; // Generate raw bytes and encode as hex for consistent storage return randomBytes(Math.ceil(len / 2)).toString('hex').slice(0, len); } /** * Generates an encryption key suitable for AES-256. * * @returns 32-byte key encoded as hex (64 characters) */ generateEncryptionKey() { return randomBytes(32).toString('hex'); } /** * Generates a complete set of installation credentials. * * These should be stored securely (environment variables, * secrets manager, etc.) and NEVER committed to version control. * * @param expirationDays - Optional expiration period in days * @returns Complete credential set */ generateInstallationCredentials(expirationDays) { const now = new Date(); const expiresAt = expirationDays ? new Date(now.getTime() + expirationDays * 24 * 60 * 60 * 1000) : undefined; return { adminPassword: this.generatePassword(), servicePassword: this.generatePassword(), jwtSecret: this.generateSecret(64), sessionSecret: this.generateSecret(64), encryptionKey: this.generateEncryptionKey(), generatedAt: now, expiresAt, }; } /** * Generates a secure session token. * * @returns URL-safe session token */ generateSessionToken() { return this.generateSecureString(64, CHARSETS.URL_SAFE); } /** * Generates a secure CSRF token. * * @returns CSRF token */ generateCsrfToken() { return randomBytes(32).toString('base64url'); } /** * Generates a secure nonce for one-time use. * * @returns Unique nonce value */ generateNonce() { return randomBytes(16).toString('hex'); } /** * Creates a setup script output for secure credential deployment. * * @param credentials - Generated credentials * @returns Environment variable export script */ createEnvScript(credentials) { return `# Claude Flow V3 - Generated Credentials # Generated: ${credentials.generatedAt.toISOString()} # IMPORTANT: Store these securely and delete this file after use export CLAUDE_FLOW_ADMIN_PASSWORD="${credentials.adminPassword}" export CLAUDE_FLOW_SERVICE_PASSWORD="${credentials.servicePassword}" export CLAUDE_FLOW_JWT_SECRET="${credentials.jwtSecret}" export CLAUDE_FLOW_SESSION_SECRET="${credentials.sessionSecret}" export CLAUDE_FLOW_ENCRYPTION_KEY="${credentials.encryptionKey}" `; } /** * Creates a JSON configuration output for secure credential deployment. * * @param credentials - Generated credentials * @returns JSON configuration (for secrets manager import) */ createJsonConfig(credentials) { return JSON.stringify({ 'claude-flow/admin-password': credentials.adminPassword, 'claude-flow/service-password': credentials.servicePassword, 'claude-flow/jwt-secret': credentials.jwtSecret, 'claude-flow/session-secret': credentials.sessionSecret, 'claude-flow/encryption-key': credentials.encryptionKey, 'claude-flow/generated-at': credentials.generatedAt.toISOString(), 'claude-flow/expires-at': credentials.expiresAt?.toISOString() ?? null, }, null, 2); } } /** * Factory function to create a production credential generator. * * @returns Configured CredentialGenerator instance */ export function createCredentialGenerator() { return new CredentialGenerator(); } /** * Quick credential generation for CLI usage. * * @returns Generated installation credentials */ export function generateCredentials() { const generator = new CredentialGenerator(); return generator.generateInstallationCredentials(); } //# sourceMappingURL=credential-generator.js.map