272 lines
9.6 KiB
JavaScript
272 lines
9.6 KiB
JavaScript
/**
|
|
* 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
|