claude-flow-novice
Version:
Claude Flow Novice - Advanced orchestration platform for multi-agent AI workflows with CFN Loop architecture Includes Local RuVector Accelerator and all CFN skills for complete functionality.
410 lines (409 loc) • 15.3 kB
JavaScript
/**
* RuVector Security Configuration Manager
*
* Centralized security settings and validation for:
* - Encryption configuration (AES-256-GCM)
* - Authentication methods (API key, JWT)
* - RBAC enforcement settings
* - Audit logging configuration
* - Rate limiting parameters
* - Session timeout settings
* - Key rotation intervals
*
* Security Features:
* - Environment variable validation on startup
* - Minimum strength requirements for secrets
* - Configuration validation and type safety
* - Security header configuration
* - Cryptographic algorithm strength validation
* - No default/hardcoded secrets
* - Comprehensive logging of configuration status
*
* CVSS Mitigation: Addresses security misconfiguration (OWASP A05)
* Compliance: Supports NIST SC-2 (Access Enforcement)
*/ import * as crypto from 'crypto';
import { createLogger } from './logging.js';
const logger = createLogger('security-config');
/**
* Security configuration constants
*/ export const SECURITY_DEFAULTS = {
ENCRYPTION_ALGORITHM: 'aes-256-gcm',
ENCRYPTION_KEY_LENGTH: 32,
JWT_ALGORITHM: 'HS256',
MIN_SECRET_LENGTH: 32,
SESSION_TIMEOUT_HOURS: 24,
KEY_ROTATION_DAYS: 90,
RATE_LIMIT_PER_MINUTE: 1000,
RATE_LIMIT_PER_HOUR: 50000,
RATE_LIMIT_PER_DAY: 500000,
AUDIT_RETENTION_DAYS: 90,
AUDIT_ARCHIVE_DAYS: 30,
PASSWORD_MIN_LENGTH: 12,
PASSWORD_REQUIRE_UPPERCASE: true,
PASSWORD_REQUIRE_NUMBERS: true,
PASSWORD_REQUIRE_SYMBOLS: true
};
/**
* RuVector Security Configuration Manager
*/ export class SecurityConfig {
config = null;
validationResult = null;
/**
* Initialize and validate security configuration from environment
*/ async initialize() {
try {
this.config = this.buildConfigFromEnvironment();
this.validationResult = this.validateConfiguration();
if (!this.validationResult.valid) {
logger.error('Security configuration validation failed', {
errors: this.validationResult.errors,
warnings: this.validationResult.warnings
});
throw new Error(`Security configuration invalid: ${this.validationResult.errors.join(', ')}`);
}
logger.info('Security configuration initialized successfully', {
warnings: this.validationResult.warnings
});
return this.validationResult;
} catch (error) {
logger.error('Failed to initialize security configuration', {
error
});
throw error;
}
}
/**
* Build configuration from environment variables
*/ buildConfigFromEnvironment() {
const env = process.env;
return {
encryption: {
algorithm: 'aes-256-gcm',
key_length_bytes: SECURITY_DEFAULTS.ENCRYPTION_KEY_LENGTH,
key_present: !!env.RUVECTOR_BACKUP_KEY,
key_valid: this.validateEncryptionKey(env.RUVECTOR_BACKUP_KEY),
key_strength: this.checkKeyStrength(env.RUVECTOR_BACKUP_KEY) ? 'strong' : 'weak'
},
authentication: {
method: env.RUVECTOR_AUTH_METHOD ?? 'api-key',
api_key_required: env.RUVECTOR_REQUIRE_API_KEY !== 'false',
jwt_secret_present: !!env.RUVECTOR_JWT_SECRET,
jwt_algorithm: env.RUVECTOR_JWT_ALGORITHM ?? 'HS256',
session_timeout_hours: parseInt(env.RUVECTOR_SESSION_TIMEOUT_HOURS ?? '24', 10)
},
rbac: {
enforcement_enabled: env.RUVECTOR_RBAC_ENABLED !== 'false',
default_deny: env.RUVECTOR_RBAC_DEFAULT_DENY !== 'false',
role_hierarchy: this.parseRoleHierarchy(env.RUVECTOR_ROLE_HIERARCHY),
collection_level_control: env.RUVECTOR_COLLECTION_ACL_ENABLED !== 'false'
},
rate_limiting: {
enabled: env.RUVECTOR_RATE_LIMITING_ENABLED !== 'false',
per_minute: parseInt(env.RUVECTOR_RATE_LIMIT_PER_MINUTE ?? '1000', 10),
per_hour: parseInt(env.RUVECTOR_RATE_LIMIT_PER_HOUR ?? '50000', 10),
per_day: parseInt(env.RUVECTOR_RATE_LIMIT_PER_DAY ?? '500000', 10),
burst_capacity: parseInt(env.RUVECTOR_BURST_CAPACITY ?? '100', 10)
},
audit: {
enabled: env.RUVECTOR_AUDIT_ENABLED !== 'false',
backend: env.RUVECTOR_AUDIT_BACKEND ?? 'postgres',
retention_days: parseInt(env.RUVECTOR_AUDIT_RETENTION_DAYS ?? '90', 10),
archive_enabled: env.RUVECTOR_AUDIT_ARCHIVE_ENABLED !== 'false',
archive_days: parseInt(env.RUVECTOR_AUDIT_ARCHIVE_DAYS ?? '30', 10),
tamper_detection_enabled: env.RUVECTOR_AUDIT_TAMPER_DETECTION !== 'false'
},
security_headers: {
'X-Content-Type-Options': 'nosniff',
'X-Frame-Options': 'DENY',
'X-XSS-Protection': '1; mode=block',
'Content-Security-Policy': env.RUVECTOR_CSP_HEADER ?? "default-src 'self'",
'Strict-Transport-Security': env.RUVECTOR_HSTS_HEADER ?? 'max-age=31536000; includeSubDomains'
},
timestamp: new Date()
};
}
/**
* Validate encryption key strength and format
*/ validateEncryptionKey(key) {
if (!key) {
return false;
}
try {
// Key should be hex-encoded 32-byte value (64 characters)
if (!/^[0-9a-fA-F]{64}$/.test(key)) {
logger.warn('Encryption key format invalid', {
length: key.length
});
return false;
}
const keyBuffer = Buffer.from(key, 'hex');
return keyBuffer.length === SECURITY_DEFAULTS.ENCRYPTION_KEY_LENGTH;
} catch (error) {
logger.error('Encryption key validation failed', {
error
});
return false;
}
}
/**
* Check if key meets strength requirements
*/ checkKeyStrength(key) {
if (!key) {
return false;
}
// Key should be at least 32 bytes (256 bits)
return key.length >= SECURITY_DEFAULTS.MIN_SECRET_LENGTH;
}
/**
* Parse role hierarchy from environment variable
*/ parseRoleHierarchy(hierarchyStr) {
if (!hierarchyStr) {
return [
'viewer',
'editor',
'admin',
'super_admin'
];
}
try {
return hierarchyStr.split(',').map((r)=>r.trim());
} catch (error) {
logger.warn('Failed to parse role hierarchy, using defaults', {
error
});
return [
'viewer',
'editor',
'admin',
'super_admin'
];
}
}
/**
* Validate complete security configuration
*/ validateConfiguration() {
const errors = [];
const warnings = [];
if (!this.config) {
errors.push('Configuration not initialized');
return {
valid: false,
errors,
warnings,
timestamp: new Date()
};
}
// Validate encryption
if (!this.config.encryption.key_present) {
errors.push('RUVECTOR_BACKUP_KEY environment variable not set (required for encryption)');
}
if (this.config.encryption.key_present && !this.config.encryption.key_valid) {
errors.push('RUVECTOR_BACKUP_KEY format invalid (must be 64-char hex string)');
}
if (this.config.encryption.key_present && this.config.encryption.key_strength === 'weak') {
warnings.push('RUVECTOR_BACKUP_KEY appears weak (less than 32 bytes)');
}
// Validate authentication
if (this.config.authentication.method === 'jwt' && !this.config.authentication.jwt_secret_present) {
errors.push('JWT authentication configured but RUVECTOR_JWT_SECRET not set');
}
if (this.config.authentication.session_timeout_hours < 1) {
errors.push('Session timeout must be at least 1 hour');
}
// Validate RBAC
if (this.config.rbac.enforcement_enabled && !this.config.rbac.default_deny) {
warnings.push('RBAC enabled but default-deny policy not enforced');
}
// Validate rate limiting
if (this.config.rate_limiting.enabled) {
if (this.config.rate_limiting.per_minute < 1) {
errors.push('Rate limit per_minute must be >= 1');
}
if (this.config.rate_limiting.burst_capacity > this.config.rate_limiting.per_minute) {
errors.push('Burst capacity cannot exceed per_minute limit');
}
}
// Validate audit
if (this.config.audit.enabled && this.config.audit.retention_days < 1) {
errors.push('Audit retention days must be >= 1');
}
if (this.config.audit.archive_enabled && this.config.audit.archive_days > this.config.audit.retention_days) {
errors.push('Archive days cannot exceed retention days');
}
return {
valid: errors.length === 0,
errors,
warnings,
timestamp: new Date()
};
}
/**
* Get current configuration
*/ getConfiguration() {
if (!this.config) {
throw new Error('Security configuration not initialized');
}
return {
...this.config
};
}
/**
* Get validation result
*/ getValidationResult() {
if (!this.validationResult) {
throw new Error('Configuration not validated');
}
return {
...this.validationResult
};
}
/**
* Check if encryption is properly configured
*/ isEncryptionEnabled() {
if (!this.config) return false;
return this.config.encryption.key_present && this.config.encryption.key_valid;
}
/**
* Check if RBAC is enforced
*/ isRBACEnabled() {
if (!this.config) return false;
return this.config.rbac.enforcement_enabled && this.config.rbac.default_deny;
}
/**
* Check if audit logging is enabled
*/ isAuditEnabled() {
if (!this.config) return false;
return this.config.audit.enabled;
}
/**
* Check if rate limiting is enabled
*/ isRateLimitingEnabled() {
if (!this.config) return false;
return this.config.rate_limiting.enabled;
}
/**
* Get security headers for HTTP responses
*/ getSecurityHeaders() {
if (!this.config) {
return {};
}
return {
...this.config.security_headers
};
}
/**
* Validate session timeout (in hours)
*/ validateSessionTimeout(hours) {
return hours > 0 && hours <= 720; // Max 30 days
}
/**
* Validate password complexity
*/ validatePasswordComplexity(password) {
const requirements = {
length: password.length >= SECURITY_DEFAULTS.PASSWORD_MIN_LENGTH,
uppercase: SECURITY_DEFAULTS.PASSWORD_REQUIRE_UPPERCASE ? /[A-Z]/.test(password) : true,
numbers: SECURITY_DEFAULTS.PASSWORD_REQUIRE_NUMBERS ? /[0-9]/.test(password) : true,
symbols: SECURITY_DEFAULTS.PASSWORD_REQUIRE_SYMBOLS ? /[!@#$%^&*]/.test(password) : true
};
const valid = Object.values(requirements).every((r)=>r);
return {
valid,
requirements
};
}
/**
* Generate a cryptographically secure random key
*/ generateRandomKey(lengthBytes = 32) {
const buffer = crypto.randomBytes(lengthBytes);
return buffer.toString('hex');
}
/**
* Validate API key format and strength
*/ validateAPIKey(apiKey) {
// API key should be at least 32 characters
if (apiKey.length < 32) {
return false;
}
// Should contain mix of characters
const hasAlpha = /[a-zA-Z]/.test(apiKey);
const hasNumbers = /[0-9]/.test(apiKey);
return hasAlpha && hasNumbers;
}
/**
* Get a summary of security posture
*/ getSecuritySummary() {
if (!this.config) {
return {
encryption_status: 'disabled',
authentication_status: 'weak',
rbac_status: 'disabled',
audit_status: 'disabled',
rate_limiting_status: 'disabled',
overall_score: 0.0
};
}
let score = 0.0;
// Encryption (25%)
const encryptionStatus = this.config.encryption.key_valid ? 'enabled' : 'disabled';
if (this.config.encryption.key_valid && this.config.encryption.key_strength === 'strong') {
score += 0.25;
}
// Authentication (25%)
const authStatus = this.config.authentication.jwt_secret_present ? 'strong' : 'weak';
if (this.config.authentication.jwt_secret_present) {
score += 0.25;
}
// RBAC (20%)
const rbacStatus = this.config.rbac.enforcement_enabled && this.config.rbac.default_deny ? 'enforced' : 'permissive';
if (rbacStatus === 'enforced') {
score += 0.2;
}
// Audit (15%)
const auditStatus = this.config.audit.enabled ? 'enabled' : 'disabled';
if (this.config.audit.enabled) {
score += 0.15;
}
// Rate limiting (15%)
const rateLimitStatus = this.config.rate_limiting.enabled ? 'enabled' : 'disabled';
if (this.config.rate_limiting.enabled) {
score += 0.15;
}
return {
encryption_status: encryptionStatus,
authentication_status: authStatus,
rbac_status: rbacStatus,
audit_status: auditStatus,
rate_limiting_status: rateLimitStatus,
overall_score: parseFloat(score.toFixed(2))
};
}
}
/**
* Create a singleton security configuration instance
*/ let securityConfigInstance = null;
export async function getSecurityConfig() {
if (!securityConfigInstance) {
securityConfigInstance = new SecurityConfig();
await securityConfigInstance.initialize();
}
return securityConfigInstance;
}
/**
* Initialize security configuration at startup
*/ export async function initializeSecurityConfig() {
const config = new SecurityConfig();
const result = await config.initialize();
if (!result.valid) {
logger.error('CRITICAL: Security configuration validation failed on startup', {
errors: result.errors
});
process.exit(1);
}
logger.info('Security configuration validated at startup', {
status: 'VALID',
warnings: result.warnings.length
});
securityConfigInstance = config;
return result;
}
//# sourceMappingURL=security-config.js.map