@iota-big3/sdk-security
Version:
Advanced security features including zero trust, quantum-safe crypto, and ML threat detection
352 lines • 12.7 kB
JavaScript
"use strict";
/**
* @iota-big3/sdk-security - Chaos Engineering Framework
* Production readiness through controlled failure injection
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.CHAOS_SCENARIOS = exports.ChaosEngine = void 0;
const events_1 = require("events");
class ChaosEngine extends events_1.EventEmitter {
constructor(config) {
super();
this.isActive = false;
this.failures = [];
this.activeFailures = new Map();
this.originalMethods = new Map();
this.config = config;
this.metrics = this.initializeMetrics();
}
initializeMetrics() {
return {
totalFailures: 0,
failuresByType: {},
failuresByComponent: {},
averageRecoveryTime: 0,
systemAvailability: 100,
lastFailureTime: 0
};
}
async start() {
if (!this.config.enabled || this.isActive) {
return;
}
this.isActive = true;
this.emit('chaos:started');
// Check safeguards before starting
const safeToStart = await this.checkSafeguards();
if (!safeToStart) {
this.isActive = false;
this.emit('chaos:aborted', 'Safeguard conditions not met');
return;
}
// Start chaos injection
this.startChaosInjection();
}
async checkSafeguards() {
for (const safeguard of this.config.safeguards) {
const safe = await safeguard.condition();
if (!safe) {
this.emit('chaos:safeguard:triggered', safeguard.name);
switch (safeguard.action) {
case 'abort':
return false;
case 'pause':
await this.pause();
break;
case 'notify':
this.emit('chaos:safeguard:notification', safeguard.name);
break;
}
}
}
return true;
}
startChaosInjection() {
// Inject failures based on probability
const interval = setInterval(async () => {
if (!this.isActive) {
clearInterval(interval);
return;
}
if (Math.random() < this.config.probability) {
await this.injectRandomFailure();
}
}, 1000); // Check every second
}
async injectRandomFailure() {
// Select random target
const target = this.selectRandomTarget();
if (!target)
return;
// Select random failure type
const failureType = this.selectRandomFailureType(target);
// Create chaos event
const event = {
id: this.generateId(),
timestamp: Date.now(),
target: `${target.component}${target.method ? '.' + target.method : ''}`,
failureType,
recovered: false
};
this.activeFailures.set(event.id, event);
this.failures.push(event);
this.updateMetrics(event);
// Apply failure
await this.applyFailure(target, failureType, event);
// Schedule recovery if max duration is set
if (this.config.maxDuration) {
setTimeout(() => {
this.recoverFailure(event.id);
}, Math.random() * this.config.maxDuration);
}
this.emit('chaos:failure:injected', event);
}
selectRandomTarget() {
const totalWeight = this.config.targets.reduce((sum, t) => sum + (t.weight || 1), 0);
let random = Math.random() * totalWeight;
for (const target of this.config.targets) {
random -= (target.weight || 1);
if (random <= 0) {
return target;
}
}
return this.config.targets[0] || null;
}
selectRandomFailureType(target) {
const types = target.failureTypes;
return types[Math.floor(Math.random() * types.length)];
}
async applyFailure(target, type, event) {
switch (type) {
case 'latency':
await this.injectLatency(target, event);
break;
case 'error':
await this.injectError(target, event);
break;
case 'timeout':
await this.injectTimeout(target, event);
break;
case 'resource_exhaustion':
await this.injectResourceExhaustion(target, event);
break;
case 'network_partition':
await this.injectNetworkPartition(target, event);
break;
case 'certificate_expiry':
await this.injectCertificateExpiry(target, event);
break;
case 'permission_denied':
await this.injectPermissionDenied(target, event);
break;
case 'data_corruption':
await this.injectDataCorruption(target, event);
break;
}
}
async injectLatency(target, event) {
const delay = 100 + Math.random() * 4900; // 100-5000ms
event.duration = delay;
// Add artificial delay to target component
this.wrapMethod(target, async (original, ...args) => {
await new Promise(resolve => setTimeout(resolve, delay));
return original.apply(this, args);
});
}
async injectError(target, event) {
const errors = [
new Error('Chaos: Service temporarily unavailable'),
new Error('Chaos: Authentication failed'),
new Error('Chaos: Rate limit exceeded'),
new Error('Chaos: Invalid security token')
];
event.error = errors[Math.floor(Math.random() * errors.length)];
this.wrapMethod(target, async (original, ...args) => {
if (Math.random() > 0.5) {
throw new Error('Chaos: Simulated random error');
}
return original.apply(this, args);
});
}
async injectTimeout(target, event) {
const timeout = 30000; // 30 second timeout
event.duration = timeout;
this.wrapMethod(target, async (original, ...args) => {
await new Promise((_, reject) => {
setTimeout(() => reject(new Error('Chaos: Operation timed out')), timeout);
});
});
}
async injectResourceExhaustion(target, event) {
// Simulate memory pressure
const arrays = [];
const interval = setInterval(() => {
if (!this.activeFailures.has(event.id)) {
clearInterval(interval);
return;
}
// Allocate 10MB
arrays.push(new Array(10 * 1024 * 1024 / 8));
}, 100);
event.error = new Error('Chaos: Resource exhaustion');
}
async injectNetworkPartition(target, event) {
event.error = new Error('Chaos: Network unreachable');
this.wrapMethod(target, async (original, ...args) => {
throw event.error;
});
}
async injectCertificateExpiry(target, event) {
event.error = new Error('Chaos: Certificate has expired');
this.wrapMethod(target, async (original, ...args) => {
throw event.error;
});
}
async injectPermissionDenied(target, event) {
event.error = new Error('Chaos: Permission denied');
this.wrapMethod(target, async (original, ...args) => {
throw event.error;
});
}
async injectDataCorruption(target, event) {
this.wrapMethod(target, async (original, ...args) => {
const result = await original.apply(this, args);
// Corrupt the result
if (typeof result === 'object' && result !== null) {
const keys = Object.keys(result);
if (keys.length > 0) {
const randomKey = keys[Math.floor(Math.random() * keys.length)];
result[randomKey] = ''.repeat(10); // Corrupted data
}
}
return result;
});
}
wrapMethod(target, wrapper) {
const key = `${target.component}.${target.method || 'default'}`;
// Store original method if not already stored
if (!this.originalMethods.has(key)) {
// In real implementation, would use dependency injection or monkey patching
// For now, we'll emit an event that the component can listen to
this.emit('chaos:wrap:method', {
component: target.component,
method: target.method,
wrapper
});
}
}
async recoverFailure(eventId) {
const event = this.activeFailures.get(eventId);
if (!event)
return;
event.recovered = true;
event.duration = Date.now() - event.timestamp;
this.activeFailures.delete(eventId);
// Restore original behavior
const key = event.target;
if (this.originalMethods.has(key)) {
this.emit('chaos:restore:method', {
component: event.target.split('.')[0],
method: event.target.split('.')[1]
});
}
this.updateRecoveryMetrics(event);
this.emit('chaos:failure:recovered', event);
}
async pause() {
this.isActive = false;
this.emit('chaos:paused');
}
async resume() {
if (this.config.enabled && !this.isActive) {
await this.start();
}
}
async stop() {
this.isActive = false;
// Recover all active failures
for (const [eventId] of this.activeFailures) {
await this.recoverFailure(eventId);
}
this.emit('chaos:stopped');
}
updateMetrics(event) {
this.metrics.totalFailures++;
this.metrics.failuresByType[event.failureType] =
(this.metrics.failuresByType[event.failureType] || 0) + 1;
const component = event.target.split('.')[0];
this.metrics.failuresByComponent[component] =
(this.metrics.failuresByComponent[component] || 0) + 1;
this.metrics.lastFailureTime = event.timestamp;
this.calculateAvailability();
}
updateRecoveryMetrics(event) {
if (event.duration) {
const totalRecoveryTime = this.metrics.averageRecoveryTime * (this.metrics.totalFailures - 1);
this.metrics.averageRecoveryTime = (totalRecoveryTime + event.duration) / this.metrics.totalFailures;
}
this.calculateAvailability();
}
calculateAvailability() {
const totalTime = Date.now() - (this.failures[0]?.timestamp || Date.now());
const downtime = this.failures.reduce((sum, f) => sum + (f.duration || 0), 0);
this.metrics.systemAvailability = ((totalTime - downtime) / totalTime) * 100;
}
getMetrics() {
return { ...this.metrics };
}
getFailureHistory(limit = 100) {
return this.failures.slice(-limit);
}
getActiveFailures() {
return Array.from(this.activeFailures.values());
}
generateId() {
return `chaos-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}
}
exports.ChaosEngine = ChaosEngine;
// Chaos test scenarios
exports.CHAOS_SCENARIOS = {
AUTHENTICATION_STORM: {
targets: [{
component: 'AccessControl',
method: 'checkAccess',
failureTypes: ['timeout', 'error'],
weight: 3
}],
probability: 0.8,
maxDuration: 5000
},
CERTIFICATE_ROTATION: {
targets: [{
component: 'ServiceMesh',
method: 'registerService',
failureTypes: ['certificate_expiry'],
weight: 1
}],
probability: 0.5,
maxDuration: 10000
},
COMPLIANCE_DRIFT: {
targets: [{
component: 'ComplianceAutomation',
method: 'runAssessment',
failureTypes: ['data_corruption', 'timeout'],
weight: 1
}],
probability: 0.3,
maxDuration: 15000
},
SECURITY_SCAN_OVERLOAD: {
targets: [{
component: 'SecurityScanner',
method: 'startScan',
failureTypes: ['resource_exhaustion', 'timeout'],
weight: 2
}],
probability: 0.6,
maxDuration: 30000
}
};
//# sourceMappingURL=chaos-engine.js.map