UNPKG

@vptech/aws-security-baseline

Version:

Auto-generate AWS security baselines, IAM policies, and security groups from TypeScript interfaces

399 lines 16.2 kB
"use strict"; /** * TypeScript Security Analyzer * Analyzes TypeScript interfaces to determine security requirements */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.TypeScriptSecurityAnalyzer = void 0; const ts = __importStar(require("typescript")); const path = __importStar(require("path")); class TypeScriptSecurityAnalyzer { /** * Analyze a TypeScript interface for security requirements */ analyzeInterface(filePath, interfaceName) { const program = this.createProgram(filePath); const sourceFile = program.getSourceFile(filePath); if (!sourceFile) { throw new Error(`Source file not found: ${filePath}`); } const interfaceDeclaration = this.findInterface(sourceFile, interfaceName); if (!interfaceDeclaration) { throw new Error(`Interface '${interfaceName}' not found in ${filePath}`); } const resourceAccess = this.analyzeResourceAccess(interfaceDeclaration); const networkRequirements = this.analyzeNetworkRequirements(interfaceDeclaration); const dataClassification = this.analyzeDataClassification(interfaceDeclaration); const recommendedPolicies = this.generateRecommendedPolicies(resourceAccess, dataClassification); return { interfaceName, filePath, resourceAccess, networkRequirements, dataClassification, recommendedPolicies }; } createProgram(filePath) { const configPath = ts.findConfigFile(path.dirname(filePath), ts.sys.fileExists, 'tsconfig.json'); let compilerOptions = { target: ts.ScriptTarget.ES2020, module: ts.ModuleKind.CommonJS, strict: true }; if (configPath) { const configFile = ts.readConfigFile(configPath, ts.sys.readFile); const parsedConfig = ts.parseJsonConfigFileContent(configFile.config, ts.sys, path.dirname(configPath)); compilerOptions = parsedConfig.options; } return ts.createProgram([filePath], compilerOptions); } findInterface(sourceFile, interfaceName) { let interfaceDecl; const visit = (node) => { if (ts.isInterfaceDeclaration(node) && node.name.text === interfaceName) { interfaceDecl = node; return; } ts.forEachChild(node, visit); }; visit(sourceFile); return interfaceDecl; } analyzeResourceAccess(interfaceDecl) { const resourceAccess = []; const checker = this.createProgram(interfaceDecl.getSourceFile().fileName).getTypeChecker(); interfaceDecl.members.forEach(member => { if (ts.isPropertySignature(member) && member.name) { const propertyName = member.name.getText(); const jsDocTags = this.extractJSDocTags(member); // Analyze property for resource access patterns const access = this.determineResourceAccess(propertyName, member, jsDocTags); if (access) { resourceAccess.push(access); } } }); return resourceAccess; } analyzeNetworkRequirements(interfaceDecl) { const networkAccess = []; interfaceDecl.members.forEach(member => { if (ts.isPropertySignature(member) && member.name) { const propertyName = member.name.getText(); const jsDocTags = this.extractJSDocTags(member); // Check for network-related properties if (this.isNetworkProperty(propertyName, jsDocTags)) { const access = this.extractNetworkRequirements(propertyName, jsDocTags); if (access) { networkAccess.push(access); } } } }); return networkAccess; } analyzeDataClassification(interfaceDecl) { const categories = []; let highestLevel = 'public'; let encryptionRequired = false; interfaceDecl.members.forEach(member => { if (ts.isPropertySignature(member) && member.name) { const propertyName = member.name.getText(); const jsDocTags = this.extractJSDocTags(member); // Classify data based on property names and annotations const classification = this.classifyProperty(propertyName, jsDocTags); if (classification) { categories.push(classification); // Update highest security level if (this.getSecurityLevel(classification.type) > this.getSecurityLevel(highestLevel)) { highestLevel = this.mapDataTypeToSecurityLevel(classification.type); } // Check if encryption is required if (['pii', 'phi', 'financial'].includes(classification.type)) { encryptionRequired = true; } } } }); const retentionRequirements = this.generateRetentionRequirements(categories); return { level: highestLevel, categories, retentionRequirements, encryptionRequired }; } extractJSDocTags(node) { const tags = new Map(); const jsDocTags = ts.getJSDocTags(node); jsDocTags.forEach(tag => { if (tag.tagName && tag.comment) { tags.set(tag.tagName.text, typeof tag.comment === 'string' ? tag.comment : tag.comment.map(c => c.text).join('')); } }); return tags; } determineResourceAccess(propertyName, member, jsDocTags) { // Check JSDoc annotations for explicit resource access patterns if (jsDocTags.has('resource')) { const resourceType = jsDocTags.get('resource'); const accessLevel = this.determineAccessLevel(jsDocTags); return { resourceType, accessLevel, encryption: { enabled: jsDocTags.has('encrypted') || this.requiresEncryption(propertyName), keyRotation: jsDocTags.has('keyRotation'), algorithm: jsDocTags.get('encryption') || 'aws:kms' } }; } // Infer resource access from property patterns if (propertyName.includes('s3') || propertyName.includes('bucket')) { return { resourceType: 's3', accessLevel: this.inferAccessLevel(propertyName), encryption: { enabled: true, algorithm: 'aws:kms' } }; } if (propertyName.includes('db') || propertyName.includes('database')) { return { resourceType: 'rds', accessLevel: 'private', encryption: { enabled: true, algorithm: 'aws:kms', keyRotation: true } }; } return null; } isNetworkProperty(propertyName, jsDocTags) { return jsDocTags.has('port') || jsDocTags.has('protocol') || propertyName.includes('port') || propertyName.includes('endpoint') || propertyName.includes('url'); } extractNetworkRequirements(propertyName, jsDocTags) { if (jsDocTags.has('port')) { const port = jsDocTags.get('port'); const protocol = jsDocTags.get('protocol') || 'tcp'; const source = jsDocTags.get('allowFrom') || '0.0.0.0/0'; return { protocol: protocol, port: isNaN(Number(port)) ? port : Number(port), sourceType: source.includes('/') ? 'cidr' : 'security-group', source, description: `Access to ${propertyName} on port ${port}` }; } return null; } classifyProperty(propertyName, jsDocTags) { // Check explicit JSDoc classification if (jsDocTags.has('classification')) { const classification = jsDocTags.get('classification'); return { name: propertyName, type: classification, fields: [propertyName], regulations: this.getRegulationsForType(classification) }; } // Infer classification from property names const piiPatterns = ['email', 'phone', 'address', 'ssn', 'name', 'dob', 'birthdate']; const phiPatterns = ['medical', 'health', 'diagnosis', 'treatment', 'patient', 'medication']; const financialPatterns = ['payment', 'card', 'account', 'balance', 'transaction', 'billing']; const propertyLower = propertyName.toLowerCase(); if (piiPatterns.some(pattern => propertyLower.includes(pattern))) { return { name: propertyName, type: 'pii', fields: [propertyName], regulations: ['GDPR', 'CCPA', 'PIPEDA'] }; } if (phiPatterns.some(pattern => propertyLower.includes(pattern))) { return { name: propertyName, type: 'phi', fields: [propertyName], regulations: ['HIPAA', 'Privacy Act 1988 (Australia)'] }; } if (financialPatterns.some(pattern => propertyLower.includes(pattern))) { return { name: propertyName, type: 'financial', fields: [propertyName], regulations: ['PCI-DSS', 'SOX', 'GDPR'] }; } return null; } determineAccessLevel(jsDocTags) { if (jsDocTags.has('access')) { return jsDocTags.get('access'); } return 'private'; } inferAccessLevel(propertyName) { if (propertyName.includes('public')) return 'public'; if (propertyName.includes('internal')) return 'internal'; if (propertyName.includes('restricted')) return 'restricted'; return 'private'; } requiresEncryption(propertyName) { const sensitivePatterns = ['password', 'secret', 'key', 'token', 'credential', 'private']; return sensitivePatterns.some(pattern => propertyName.toLowerCase().includes(pattern)); } getSecurityLevel(level) { const levels = { public: 0, internal: 1, confidential: 2, restricted: 3 }; return levels[level] || 0; } mapDataTypeToSecurityLevel(type) { switch (type) { case 'phi': case 'financial': return 'restricted'; case 'pii': return 'confidential'; case 'proprietary': return 'internal'; default: return 'public'; } } getRegulationsForType(type) { switch (type) { case 'pii': return ['GDPR', 'CCPA', 'PIPEDA']; case 'phi': return ['HIPAA', 'Privacy Act 1988 (Australia)']; case 'financial': return ['PCI-DSS', 'SOX', 'GDPR']; case 'proprietary': return ['Trade Secrets Act']; default: return []; } } generateRetentionRequirements(categories) { const requirements = []; categories.forEach(category => { switch (category.type) { case 'pii': requirements.push({ category: category.name, retentionPeriod: '7 years', disposalMethod: 'Secure deletion with cryptographic erasure', legalBasis: 'GDPR Article 17 (Right to erasure)' }); break; case 'phi': requirements.push({ category: category.name, retentionPeriod: '6 years', disposalMethod: 'HIPAA-compliant secure deletion', legalBasis: 'HIPAA 45 CFR 164.316(b)(2)(i)' }); break; case 'financial': requirements.push({ category: category.name, retentionPeriod: '7 years', disposalMethod: 'Secure deletion with audit trail', legalBasis: 'SOX Section 802' }); break; } }); return requirements; } generateRecommendedPolicies(resourceAccess, dataClassification) { const policies = []; // Add policies based on data classification if (dataClassification.level === 'restricted') { policies.push('RestrictedDataAccessPolicy'); policies.push('EncryptionAtRestRequiredPolicy'); policies.push('EncryptionInTransitRequiredPolicy'); } if (dataClassification.level === 'confidential') { policies.push('ConfidentialDataAccessPolicy'); policies.push('EncryptionAtRestRequiredPolicy'); } // Add policies based on resource access resourceAccess.forEach(access => { switch (access.resourceType) { case 's3': policies.push('S3SecureAccessPolicy'); if (access.encryption?.enabled) { policies.push('S3EncryptionRequiredPolicy'); } break; case 'rds': policies.push('RDSSecureAccessPolicy'); policies.push('DatabaseEncryptionPolicy'); break; } }); // Add compliance-specific policies const hasPhiData = dataClassification.categories.some(c => c.type === 'phi'); const hasPiiData = dataClassification.categories.some(c => c.type === 'pii'); const hasFinancialData = dataClassification.categories.some(c => c.type === 'financial'); if (hasPhiData) { policies.push('HIPAACompliancePolicy'); } if (hasPiiData) { policies.push('GDPRCompliancePolicy'); } if (hasFinancialData) { policies.push('PCIDSSCompliancePolicy'); } return [...new Set(policies)]; // Remove duplicates } } exports.TypeScriptSecurityAnalyzer = TypeScriptSecurityAnalyzer; //# sourceMappingURL=typescript-analyzer.js.map