@iota-big3/sdk-security
Version:
Advanced security features including zero trust, quantum-safe crypto, and ML threat detection
610 lines • 21.8 kB
JavaScript
"use strict";
/**
* AWS Secrets Manager Provider
* Native AWS integration for secrets management
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.AWSSecretsProvider = void 0;
const events_1 = require("events");
const types_1 = require("../types");
class AWSSecretsProvider extends events_1.EventEmitter {
constructor(config) {
super();
this.provider = types_1.SecretsProvider.AWS_SECRETS_MANAGER;
this.isInitialized = false;
this.cache = new Map();
this.config = config;
this.validateConfig();
}
/**
* Initialize AWS Secrets Manager client
*/
async initialize() {
if (this.isInitialized)
return;
try {
// In production, would initialize AWS SDK client
// const client = new SecretsManagerClient({
// region: this.config.region,
// credentials: this.getAWSCredentials()
// });
this.isInitialized = true;
this.emit('initialized', { provider: this.provider });
}
catch (error) {
this.emit('error', { error, operation: 'initialize' });
throw error;
}
}
/**
* Get secret from AWS Secrets Manager
*/
async getSecret(name, version) {
await this.ensureInitialized();
// Check cache
if (this.config.caching?.enabled && !version) {
const cached = this.getFromCache(name);
if (cached)
return cached;
}
try {
// Simulate AWS API call
const response = await this.awsGetSecret(name, version);
const secret = {
name: response.Name,
value: this.parseSecretValue(response.SecretString, response.SecretBinary),
type: this.inferSecretType(response.Name, response.Tags),
createdAt: response.CreatedDate,
updatedAt: response.LastChangedDate || response.CreatedDate,
version: response.VersionId,
tags: this.convertTagsToRecord(response.Tags),
description: response.Description
};
// Check rotation
if (response.RotationEnabled) {
secret.rotationPolicy = {
enabled: true,
interval: 30, // AWS default
lastRotated: response.LastRotatedDate,
nextRotation: response.NextRotationDate,
autoRotate: true,
rotationFunction: response.RotationLambdaARN
};
}
// Cache if enabled
if (this.config.caching?.enabled && !version) {
this.addToCache(name, secret);
}
// Audit
this.auditLog({
timestamp: new Date(),
action: types_1.SecretAction.READ,
secretName: name,
actor: { id: 'system', type: 'SYSTEM' },
result: 'SUCCESS'
});
return secret;
}
catch (error) {
this.auditLog({
timestamp: new Date(),
action: types_1.SecretAction.READ,
secretName: name,
actor: { id: 'system', type: 'SYSTEM' },
result: 'FAILURE',
error: error.message
});
throw error;
}
}
/**
* Create or update secret
*/
async setSecret(name, value, metadata) {
await this.ensureInitialized();
try {
const secretString = typeof value === 'string' ? value : JSON.stringify(value);
const tags = metadata?.tags ? this.convertRecordToTags(metadata.tags) : undefined;
// Check if secret exists
const exists = await this.secretExists(name);
if (exists) {
// Update existing secret
await this.awsUpdateSecret(name, secretString, metadata?.description);
// Update tags if provided
if (tags) {
await this.awsTagResource(name, tags);
}
}
else {
// Create new secret
await this.awsCreateSecret(name, secretString, {
description: metadata?.description,
tags,
kmsKeyId: this.config.encryption?.keyId
});
}
// Configure rotation if specified
if (metadata?.rotationPolicy?.enabled) {
await this.configureRotation(name, metadata.rotationPolicy);
}
// Clear cache
this.cache.delete(name);
// Audit
this.auditLog({
timestamp: new Date(),
action: exists ? types_1.SecretAction.UPDATE : types_1.SecretAction.CREATE,
secretName: name,
actor: { id: 'system', type: 'SYSTEM' },
result: 'SUCCESS',
metadata: { type: metadata?.type }
});
this.emit('secret:' + (exists ? 'updated' : 'created'), { name, metadata });
}
catch (error) {
this.auditLog({
timestamp: new Date(),
action: types_1.SecretAction.CREATE,
secretName: name,
actor: { id: 'system', type: 'SYSTEM' },
result: 'FAILURE',
error: error.message
});
throw error;
}
}
/**
* Delete secret
*/
async deleteSecret(name, permanent) {
await this.ensureInitialized();
try {
if (permanent) {
// Force delete without recovery window
await this.awsDeleteSecret(name, true);
}
else {
// Schedule deletion with 30-day recovery window
await this.awsDeleteSecret(name, false);
}
// Clear cache
this.cache.delete(name);
// Audit
this.auditLog({
timestamp: new Date(),
action: types_1.SecretAction.DELETE,
secretName: name,
actor: { id: 'system', type: 'SYSTEM' },
result: 'SUCCESS',
metadata: { permanent }
});
this.emit('secret:deleted', { name, permanent });
}
catch (error) {
this.auditLog({
timestamp: new Date(),
action: types_1.SecretAction.DELETE,
secretName: name,
actor: { id: 'system', type: 'SYSTEM' },
result: 'FAILURE',
error: error.message
});
throw error;
}
}
/**
* List secrets
*/
async listSecrets(filter) {
await this.ensureInitialized();
try {
const allSecrets = await this.awsListSecrets();
const secrets = [];
for (const secretSummary of allSecrets) {
// Apply name filter
if (filter?.namePattern &&
!secretSummary.Name.match(new RegExp(filter.namePattern))) {
continue;
}
// Get full metadata for filtering
const tags = this.convertTagsToRecord(secretSummary.Tags);
// Apply tag filter
if (filter?.tags && !this.matchTags(tags, filter.tags)) {
continue;
}
// Apply date filter
if (filter?.modifiedSince &&
secretSummary.LastChangedDate < filter.modifiedSince) {
continue;
}
const metadata = {
name: secretSummary.Name,
type: this.inferSecretType(secretSummary.Name, secretSummary.Tags),
description: secretSummary.Description,
createdAt: secretSummary.CreatedDate,
updatedAt: secretSummary.LastChangedDate || secretSummary.CreatedDate,
tags
};
// Add rotation info if available
if (secretSummary.RotationEnabled) {
metadata.rotationPolicy = {
enabled: true,
interval: 30,
lastRotated: secretSummary.LastRotatedDate,
autoRotate: true
};
}
secrets.push(metadata);
}
return secrets;
}
catch (error) {
throw new Error(`Failed to list secrets: ${error.message}`);
}
}
/**
* Rotate secret
*/
async rotateSecret(name) {
await this.ensureInitialized();
try {
// Trigger rotation
await this.awsRotateSecret(name);
// Clear cache
this.cache.delete(name);
// Audit
this.auditLog({
timestamp: new Date(),
action: types_1.SecretAction.ROTATE,
secretName: name,
actor: { id: 'system', type: 'SYSTEM' },
result: 'SUCCESS'
});
this.emit('secret:rotated', { name });
}
catch (error) {
this.auditLog({
timestamp: new Date(),
action: types_1.SecretAction.ROTATE,
secretName: name,
actor: { id: 'system', type: 'SYSTEM' },
result: 'FAILURE',
error: error.message
});
throw error;
}
}
/**
* Schedule rotation
*/
async scheduleRotation(name, policy) {
await this.ensureInitialized();
if (!policy.rotationFunction) {
throw new Error('Rotation Lambda ARN is required for AWS Secrets Manager');
}
await this.awsConfigureRotation(name, {
AutomaticallyAfterDays: policy.interval,
LambdaFunctionArn: policy.rotationFunction
});
this.emit('rotation:scheduled', {
name,
interval: policy.interval
});
}
/**
* Get secret versions
*/
async getSecretVersions(name) {
await this.ensureInitialized();
const versions = await this.awsListSecretVersions(name);
return versions
.filter(v => !v.VersionStages?.includes('AWSPENDING'))
.map(v => v.VersionId)
.filter(Boolean);
}
/**
* Rollback to specific version
*/
async rollbackSecret(name, version) {
await this.ensureInitialized();
try {
// Get the specified version
const oldValue = await this.awsGetSecret(name, version);
// Update current version with old value
await this.awsUpdateSecret(name, oldValue.SecretString || Buffer.from(oldValue.SecretBinary).toString('base64'));
// Clear cache
this.cache.delete(name);
// Audit
this.auditLog({
timestamp: new Date(),
action: types_1.SecretAction.ROLLBACK,
secretName: name,
actor: { id: 'system', type: 'SYSTEM' },
result: 'SUCCESS',
metadata: { targetVersion: version }
});
this.emit('secret:rollback', { name, version });
}
catch (error) {
this.auditLog({
timestamp: new Date(),
action: types_1.SecretAction.ROLLBACK,
secretName: name,
actor: { id: 'system', type: 'SYSTEM' },
result: 'FAILURE',
error: error.message
});
throw error;
}
}
/**
* Get multiple secrets
*/
async getBulkSecrets(names) {
// AWS doesn't have bulk get, so we parallelize individual requests
const secrets = await Promise.all(names.map(name => this.getSecret(name).catch(() => null)));
return secrets.filter(Boolean);
}
/**
* Import secrets
*/
async importSecrets(secrets) {
// Process in batches to avoid rate limits
const batchSize = 10;
for (let i = 0; i < secrets.length; i += batchSize) {
const batch = secrets.slice(i, i + batchSize);
await Promise.all(batch.map(secret => this.setSecret(secret.name, secret.value, secret)));
}
this.emit('secrets:imported', { count: secrets.length });
}
/**
* Export secrets
*/
async exportSecrets(filter) {
const metadata = await this.listSecrets(filter);
const secrets = [];
// Export in batches
const batchSize = 10;
for (let i = 0; i < metadata.length; i += batchSize) {
const batch = metadata.slice(i, i + batchSize);
const batchSecrets = await Promise.all(batch.map(meta => this.getSecret(meta.name).catch(() => null)));
secrets.push(...batchSecrets.filter(Boolean));
}
this.auditLog({
timestamp: new Date(),
action: types_1.SecretAction.EXPORT,
secretName: '*',
actor: { id: 'system', type: 'SYSTEM' },
result: 'SUCCESS',
metadata: { count: secrets.length }
});
return secrets;
}
/**
* Health check
*/
async healthCheck() {
const startTime = Date.now();
try {
// Try to list secrets with limit 1
await this.awsListSecrets(1);
return {
healthy: true,
provider: this.provider,
connectivity: true,
authentication: true,
lastCheck: new Date(),
latency: Date.now() - startTime
};
}
catch (error) {
return {
healthy: false,
provider: this.provider,
connectivity: false,
authentication: false,
lastCheck: new Date(),
latency: Date.now() - startTime,
errors: [error.message]
};
}
}
/**
* Helper methods
*/
validateConfig() {
if (!this.config.region) {
throw new Error('AWS region is required');
}
}
async ensureInitialized() {
if (!this.isInitialized) {
await this.initialize();
}
}
getFromCache(name) {
if (!this.config.caching?.enabled)
return null;
const cached = this.cache.get(name);
if (!cached)
return null;
const age = Date.now() - cached.cachedAt.getTime();
if (age > (this.config.caching.ttl * 1000)) {
this.cache.delete(name);
return null;
}
return cached.secret;
}
addToCache(name, secret) {
if (!this.config.caching?.enabled)
return;
// Check cache size
const maxSize = this.config.caching.maxSize || 100;
if (this.cache.size >= maxSize) {
// Remove oldest entry
const oldest = Array.from(this.cache.entries())
.sort((a, b) => a[1].cachedAt.getTime() - b[1].cachedAt.getTime())[0];
this.cache.delete(oldest[0]);
}
this.cache.set(name, { secret, cachedAt: new Date() });
}
parseSecretValue(secretString, secretBinary) {
if (secretString) {
try {
return JSON.parse(secretString);
}
catch {
return secretString;
}
}
if (secretBinary) {
return Buffer.from(secretBinary);
}
return null;
}
inferSecretType(name, tags) {
// Check tags first
const tagMap = this.convertTagsToRecord(tags);
if (tagMap.type) {
return tagMap.type;
}
// Infer from name
if (name.includes('api') || name.includes('key'))
return types_1.SecretType.API_KEY;
if (name.includes('db') || name.includes('database') || name.includes('rds')) {
return types_1.SecretType.DATABASE_CREDENTIAL;
}
if (name.includes('cert'))
return types_1.SecretType.CERTIFICATE;
if (name.includes('ssh'))
return types_1.SecretType.SSH_KEY;
if (name.includes('token'))
return types_1.SecretType.TOKEN;
return types_1.SecretType.CUSTOM;
}
convertTagsToRecord(tags) {
if (!tags)
return {};
const record = {};
for (const tag of tags) {
record[tag.Key] = tag.Value;
}
return record;
}
convertRecordToTags(record) {
return Object.entries(record).map(([Key, Value]) => ({ Key, Value }));
}
matchTags(secretTags, filterTags) {
return Object.entries(filterTags).every(([key, value]) => secretTags[key] === value);
}
async secretExists(name) {
try {
await this.awsGetSecret(name);
return true;
}
catch (error) {
if (error.name === 'ResourceNotFoundException') {
return false;
}
throw error;
}
}
async configureRotation(name, policy) {
if (!policy.rotationFunction) {
// Auto-create rotation Lambda for RDS/DocumentDB
if (name.includes('rds') || name.includes('docdb')) {
policy.rotationFunction = await this.createRotationLambda(name);
}
else {
throw new Error('Rotation function ARN required');
}
}
await this.awsConfigureRotation(name, {
AutomaticallyAfterDays: policy.interval,
LambdaFunctionArn: policy.rotationFunction
});
}
async createRotationLambda(secretName) {
// In production, would create appropriate rotation Lambda
// based on secret type (RDS, DocumentDB, etc.)
return `arn:aws:lambda:${this.config.region}:123456789012:function:SecretsManagerRotation`;
}
auditLog(event) {
this.emit('audit', event);
// Could also send to CloudTrail or CloudWatch
}
/**
* AWS API Mock Methods
* In production, these would use actual AWS SDK
*/
async awsGetSecret(name, version) {
await new Promise(resolve => setTimeout(resolve, 50));
return {
Name: name,
VersionId: version || 'AWSCURRENT',
SecretString: JSON.stringify({ username: 'admin', password: 'secret123' }),
CreatedDate: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000),
LastChangedDate: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
Description: 'Mock secret',
Tags: [{ Key: 'Environment', Value: 'production' }],
RotationEnabled: name.includes('rds'),
LastRotatedDate: name.includes('rds') ? new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) : undefined,
NextRotationDate: name.includes('rds') ? new Date(Date.now() + 23 * 24 * 60 * 60 * 1000) : undefined,
RotationLambdaARN: name.includes('rds') ? 'arn:aws:lambda:us-east-1:123456789012:function:rotation' : undefined
};
}
async awsCreateSecret(name, value, options) {
await new Promise(resolve => setTimeout(resolve, 100));
// Mock create
}
async awsUpdateSecret(name, value, description) {
await new Promise(resolve => setTimeout(resolve, 100));
// Mock update
}
async awsDeleteSecret(name, forceDelete) {
await new Promise(resolve => setTimeout(resolve, 100));
// Mock delete
}
async awsListSecrets(maxResults) {
await new Promise(resolve => setTimeout(resolve, 100));
return [
{
Name: 'prod/api/key',
Description: 'Production API key',
CreatedDate: new Date(Date.now() - 60 * 24 * 60 * 60 * 1000),
LastChangedDate: new Date(Date.now() - 5 * 24 * 60 * 60 * 1000),
Tags: [{ Key: 'Environment', Value: 'production' }]
},
{
Name: 'prod/rds/master',
Description: 'RDS master credentials',
CreatedDate: new Date(Date.now() - 90 * 24 * 60 * 60 * 1000),
LastChangedDate: new Date(Date.now() - 1 * 24 * 60 * 60 * 1000),
Tags: [{ Key: 'Type', Value: 'database' }],
RotationEnabled: true,
LastRotatedDate: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000)
}
].slice(0, maxResults);
}
async awsRotateSecret(name) {
await new Promise(resolve => setTimeout(resolve, 200));
// Mock rotation
}
async awsConfigureRotation(name, config) {
await new Promise(resolve => setTimeout(resolve, 100));
// Mock rotation configuration
}
async awsListSecretVersions(name) {
await new Promise(resolve => setTimeout(resolve, 100));
return [
{ VersionId: 'AWSCURRENT', VersionStages: ['AWSCURRENT'] },
{ VersionId: 'v2', VersionStages: [] },
{ VersionId: 'v1', VersionStages: [] }
];
}
async awsTagResource(arn, tags) {
await new Promise(resolve => setTimeout(resolve, 50));
// Mock tagging
}
}
exports.AWSSecretsProvider = AWSSecretsProvider;
//# sourceMappingURL=aws-secrets-provider.js.map