Tools: Building a Production-Ready AWS Security Vulnerability Scanner: A Technical Deep Dive

Tools: Building a Production-Ready AWS Security Vulnerability Scanner: A Technical Deep Dive

Source: Dev.to

The Problem: Security Visibility at Scale ## Our Solution: An Intelligent, Unified Security Dashboard ## Architecture Overview ## Technical Implementation ## 1. Multi-Service Data Collection ## 2. Operational Issue Detection ## 3. Intelligent Prioritization ## 4. User Experience Innovation ## Performance Optimizations ## 1. Efficient Data Loading ## 2. Client-Side Filtering ## 3. Smart Sorting ## Deployment Architecture ## Infrastructure as Code ## Cost Optimization ## Results & Impact ## Lessons Learned ## Future Enhancements ## Conclusion ## Reach Out to Us In modern cloud environments, security vulnerabilities don't announce themselves. They hide in: The Challenge: Organizations using AWS face a fragmented security landscape: The Result: Security teams spend hours: We built a comprehensive AWS Security Vulnerability Scanner that: Challenge: Each AWS security service returns data in different formats. Solution: Unified scanner with normalized output: Beyond CVEs, we detect operational security issues: Impact: Found $70/month in cost savings in our sandbox account alone. Challenge: Not all vulnerabilities are equal. A Critical CVE in a non-production Lambda is less urgent than a High CVE in a public-facing EC2 instance. Solution: Multi-factor priority scoring: Problem: Traditional security dashboards are overwhelming. Users see hundreds of findings with no clear action path. All filtering happens client-side for instant response: Multiple sort options with O(n log n) performance: Monthly Costs (Small Environment): ROI: Found $70/month in cost savings (idle resources) in first scan. Metrics from Sandbox Deployment: UX Matters in Security Tools Prioritization is Critical Automation Reduces Toil Automated Remediation ML-Based Prioritization Multi-Account Support Building effective security tools requires more than just collecting data. It requires: Our AWS Security Vulnerability Scanner demonstrates that with thoughtful design and implementation, security tools can be both powerful and delightful to use. Interested in modernizing your cloud infrastructure and building enterprise-grade solutions? Storm Reply is driven by continuous learning and practical innovation. We specialize in designing and delivering scalable AWS architectures that support customers throughout their cloud journey, from early assessment to production-ready deployment. With deep experience in AWS architecture, data engineering, and security best practices, we help enterprises migrate with confidence and move faster on their cloud transformation goals. Let’s connect and explore how we can support your modernization initiatives. 🌐 Website: https://www.stormreply.cloud/ 💼 LinkedIn: https://www.linkedin.com/company/storm-reply/posts/?feedView=all Date: January 2026 Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse CODE_BLOCK: class AWSSecurityScanner: def __init__(self, region='us-east-1'): self.securityhub = boto3.client('securityhub', region_name=region) self.inspector = boto3.client('inspector2', region_name=region) self.config = boto3.client('config', region_name=region) self.support = boto3.client('support', region_name='us-east-1') def scan_all(self): findings = { 'security_hub': self.scan_security_hub_findings(), 'inspector': self.scan_inspector_vulnerabilities(), 'config': self.scan_config_compliance(), 'trusted_advisor': self.scan_trusted_advisor() } return self.generate_report(findings) Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: class AWSSecurityScanner: def __init__(self, region='us-east-1'): self.securityhub = boto3.client('securityhub', region_name=region) self.inspector = boto3.client('inspector2', region_name=region) self.config = boto3.client('config', region_name=region) self.support = boto3.client('support', region_name='us-east-1') def scan_all(self): findings = { 'security_hub': self.scan_security_hub_findings(), 'inspector': self.scan_inspector_vulnerabilities(), 'config': self.scan_config_compliance(), 'trusted_advisor': self.scan_trusted_advisor() } return self.generate_report(findings) CODE_BLOCK: class AWSSecurityScanner: def __init__(self, region='us-east-1'): self.securityhub = boto3.client('securityhub', region_name=region) self.inspector = boto3.client('inspector2', region_name=region) self.config = boto3.client('config', region_name=region) self.support = boto3.client('support', region_name='us-east-1') def scan_all(self): findings = { 'security_hub': self.scan_security_hub_findings(), 'inspector': self.scan_inspector_vulnerabilities(), 'config': self.scan_config_compliance(), 'trusted_advisor': self.scan_trusted_advisor() } return self.generate_report(findings) COMMAND_BLOCK: class OperationalScanner: def scan_unused_s3_buckets(self, days_threshold=90): """Find S3 buckets with no activity""" # Check last modified date # Calculate storage costs # Generate deletion recommendations def scan_expiring_certificates(self, days_threshold=30): """Find ACM certificates expiring soon""" # Check NotAfter date # Prioritize by usage (InUseBy) # Alert on critical expirations def scan_idle_load_balancers(self): """Find load balancers with no traffic""" # Query CloudWatch metrics # Calculate monthly cost waste # Recommend deletion Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: class OperationalScanner: def scan_unused_s3_buckets(self, days_threshold=90): """Find S3 buckets with no activity""" # Check last modified date # Calculate storage costs # Generate deletion recommendations def scan_expiring_certificates(self, days_threshold=30): """Find ACM certificates expiring soon""" # Check NotAfter date # Prioritize by usage (InUseBy) # Alert on critical expirations def scan_idle_load_balancers(self): """Find load balancers with no traffic""" # Query CloudWatch metrics # Calculate monthly cost waste # Recommend deletion COMMAND_BLOCK: class OperationalScanner: def scan_unused_s3_buckets(self, days_threshold=90): """Find S3 buckets with no activity""" # Check last modified date # Calculate storage costs # Generate deletion recommendations def scan_expiring_certificates(self, days_threshold=30): """Find ACM certificates expiring soon""" # Check NotAfter date # Prioritize by usage (InUseBy) # Alert on critical expirations def scan_idle_load_balancers(self): """Find load balancers with no traffic""" # Query CloudWatch metrics # Calculate monthly cost waste # Recommend deletion COMMAND_BLOCK: function isTopPriority(finding) { const severity = finding.Severity?.Label; const fixAvailable = finding.Vulnerabilities?.[0]?.FixAvailable; const ageInDays = calculateAge(finding.CreatedAt); const isExposed = isInternetExposed(finding); return (severity === 'CRITICAL' || severity === 'HIGH') && fixAvailable === 'YES' && ageInDays > 7 && isExposed; } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: function isTopPriority(finding) { const severity = finding.Severity?.Label; const fixAvailable = finding.Vulnerabilities?.[0]?.FixAvailable; const ageInDays = calculateAge(finding.CreatedAt); const isExposed = isInternetExposed(finding); return (severity === 'CRITICAL' || severity === 'HIGH') && fixAvailable === 'YES' && ageInDays > 7 && isExposed; } COMMAND_BLOCK: function isTopPriority(finding) { const severity = finding.Severity?.Label; const fixAvailable = finding.Vulnerabilities?.[0]?.FixAvailable; const ageInDays = calculateAge(finding.CreatedAt); const isExposed = isInternetExposed(finding); return (severity === 'CRITICAL' || severity === 'HIGH') && fixAvailable === 'YES' && ageInDays > 7 && isExposed; } CODE_BLOCK: // Cache busting for fresh data const cacheBuster = new Date().getTime(); const response = await fetch(`findings.json?v=${cacheBuster}`); // Skeleton loading states function showLoadingState() { const skeletonHTML = ` <div class="skeleton skeleton-card"></div> <div class="skeleton skeleton-card"></div> `; container.innerHTML = skeletonHTML; } Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: // Cache busting for fresh data const cacheBuster = new Date().getTime(); const response = await fetch(`findings.json?v=${cacheBuster}`); // Skeleton loading states function showLoadingState() { const skeletonHTML = ` <div class="skeleton skeleton-card"></div> <div class="skeleton skeleton-card"></div> `; container.innerHTML = skeletonHTML; } CODE_BLOCK: // Cache busting for fresh data const cacheBuster = new Date().getTime(); const response = await fetch(`findings.json?v=${cacheBuster}`); // Skeleton loading states function showLoadingState() { const skeletonHTML = ` <div class="skeleton skeleton-card"></div> <div class="skeleton skeleton-card"></div> `; container.innerHTML = skeletonHTML; } CODE_BLOCK: function matchesGlobalFilters(finding) { // Region filter if (globalFilters.region && finding.Region !== globalFilters.region) { return false; } // Service filter if (globalFilters.service) { const resourceType = finding.Resources?.[0]?.Type || ''; if (!resourceType.includes(globalFilters.service)) { return false; } } // Search filter (fuzzy match) if (globalFilters.search) { const searchText = globalFilters.search.toLowerCase(); return title.includes(searchText) || cve.includes(searchText) || resourceId.includes(searchText); } return true; } Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: function matchesGlobalFilters(finding) { // Region filter if (globalFilters.region && finding.Region !== globalFilters.region) { return false; } // Service filter if (globalFilters.service) { const resourceType = finding.Resources?.[0]?.Type || ''; if (!resourceType.includes(globalFilters.service)) { return false; } } // Search filter (fuzzy match) if (globalFilters.search) { const searchText = globalFilters.search.toLowerCase(); return title.includes(searchText) || cve.includes(searchText) || resourceId.includes(searchText); } return true; } CODE_BLOCK: function matchesGlobalFilters(finding) { // Region filter if (globalFilters.region && finding.Region !== globalFilters.region) { return false; } // Service filter if (globalFilters.service) { const resourceType = finding.Resources?.[0]?.Type || ''; if (!resourceType.includes(globalFilters.service)) { return false; } } // Search filter (fuzzy match) if (globalFilters.search) { const searchText = globalFilters.search.toLowerCase(); return title.includes(searchText) || cve.includes(searchText) || resourceId.includes(searchText); } return true; } COMMAND_BLOCK: function sortFindings(findings, sortBy) { switch(sortBy) { case 'severity': return findings.sort((a, b) => severityOrder[a.Severity.Label] - severityOrder[b.Severity.Label] ); case 'cvss': return findings.sort((a, b) => getCVSS(b) - getCVSS(a) ); case 'age': return findings.sort((a, b) => new Date(a.CreatedAt) - new Date(b.CreatedAt) ); } } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: function sortFindings(findings, sortBy) { switch(sortBy) { case 'severity': return findings.sort((a, b) => severityOrder[a.Severity.Label] - severityOrder[b.Severity.Label] ); case 'cvss': return findings.sort((a, b) => getCVSS(b) - getCVSS(a) ); case 'age': return findings.sort((a, b) => new Date(a.CreatedAt) - new Date(b.CreatedAt) ); } } COMMAND_BLOCK: function sortFindings(findings, sortBy) { switch(sortBy) { case 'severity': return findings.sort((a, b) => severityOrder[a.Severity.Label] - severityOrder[b.Severity.Label] ); case 'cvss': return findings.sort((a, b) => getCVSS(b) - getCVSS(a) ); case 'age': return findings.sort((a, b) => new Date(a.CreatedAt) - new Date(b.CreatedAt) ); } } COMMAND_BLOCK: # CloudFormation Template Resources: SecurityScannerFunction: Type: AWS::Lambda::Function Properties: Runtime: python3.11 Handler: index.lambda_handler Timeout: 300 Environment: Variables: REPORTS_BUCKET: !Ref SecurityReportsBucket SNS_TOPIC_ARN: !Ref SecurityAlertsTopic DailyScanRule: Type: AWS::Events::Rule Properties: ScheduleExpression: 'cron(0 9 * * ? *)' Targets: - Arn: !GetAtt SecurityScannerFunction.Arn Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: # CloudFormation Template Resources: SecurityScannerFunction: Type: AWS::Lambda::Function Properties: Runtime: python3.11 Handler: index.lambda_handler Timeout: 300 Environment: Variables: REPORTS_BUCKET: !Ref SecurityReportsBucket SNS_TOPIC_ARN: !Ref SecurityAlertsTopic DailyScanRule: Type: AWS::Events::Rule Properties: ScheduleExpression: 'cron(0 9 * * ? *)' Targets: - Arn: !GetAtt SecurityScannerFunction.Arn COMMAND_BLOCK: # CloudFormation Template Resources: SecurityScannerFunction: Type: AWS::Lambda::Function Properties: Runtime: python3.11 Handler: index.lambda_handler Timeout: 300 Environment: Variables: REPORTS_BUCKET: !Ref SecurityReportsBucket SNS_TOPIC_ARN: !Ref SecurityAlertsTopic DailyScanRule: Type: AWS::Events::Rule Properties: ScheduleExpression: 'cron(0 9 * * ? *)' Targets: - Arn: !GetAtt SecurityScannerFunction.Arn - Outdated packages in Lambda functions - Unpatched EC2 instances running critical workloads - Container images with known CVEs in ECR - Misconfigured security groups exposing services to the internet - Security Hub aggregates findings but lacks actionable remediation - Inspector scans for CVEs but doesn't prioritize by business impact - AWS Config checks compliance but doesn't show cost implications - Trusted Advisor provides recommendations but requires manual correlation - Manually correlating findings across multiple AWS services - Determining which vulnerabilities to fix first - Finding the exact commands to remediate issues - Tracking unused resources that increase attack surface - Aggregates findings from Security Hub, Inspector, AWS Config, and Trusted Advisor - Prioritizes vulnerabilities using intelligent scoring - Provides exact remediation commands - Identifies cost optimization opportunities - Delivers an intuitive, scannable interface - Parallel API calls for performance - Error handling for partial failures - Pagination for large result sets - Caching to reduce API costs - Severity (CVSS score) - Fix availability - Age (older = higher priority) - Internet exposure - Environment (production > non-production) - Compact Row View - Scan 10+ findings without scrolling - Global Filters - Filter by region, service, environment, time - Smart Search - Search CVE IDs, instance IDs, package names - Linked Remediation - Click vulnerability → See exact fix - Copy-Paste Commands - One-click copy of remediation commands - Auto-Refresh - Optional 5-minute auto-refresh with toast notifications - Security Hub: $30 - Inspector: $40 - AWS Config: $15 - S3 + CloudFront: $2 - Total: ~$92/month - 73 vulnerabilities identified across 5 services - 1 Critical (CVE-2025-69264 - pnpm RCE) - 72 High severity findings - 7 unused S3 buckets (inactive 100+ days) - 1 idle load balancer ($20/month waste) - 1 idle RDS instance ($50/month waste) - Before: 2-3 hours to manually correlate findings - After: 5 minutes to identify and prioritize top issues - "Finally, a security dashboard that tells me what to do" - "The copy-paste commands save so much time" - "Love the Top Priority filter - shows exactly what needs fixing" - UX Matters in Security Tools Security teams are overwhelmed with data Actionable guidance > Raw findings Scannable interfaces > Detailed cards - Security teams are overwhelmed with data - Actionable guidance > Raw findings - Scannable interfaces > Detailed cards - Integration is Key No single AWS service provides complete visibility Correlation across services reveals true risk Operational issues (cost, unused resources) matter - No single AWS service provides complete visibility - Correlation across services reveals true risk - Operational issues (cost, unused resources) matter - Prioritization is Critical Not all vulnerabilities are equal Context matters (environment, exposure, age) Fix availability should drive priority - Not all vulnerabilities are equal - Context matters (environment, exposure, age) - Fix availability should drive priority - Automation Reduces Toil Daily scans catch new issues early Auto-generated remediation commands reduce errors Toast notifications build trust - Daily scans catch new issues early - Auto-generated remediation commands reduce errors - Toast notifications build trust - Security teams are overwhelmed with data - Actionable guidance > Raw findings - Scannable interfaces > Detailed cards - No single AWS service provides complete visibility - Correlation across services reveals true risk - Operational issues (cost, unused resources) matter - Not all vulnerabilities are equal - Context matters (environment, exposure, age) - Fix availability should drive priority - Daily scans catch new issues early - Auto-generated remediation commands reduce errors - Toast notifications build trust - Automated Remediation Auto-patch non-production resources Create Jira tickets for manual review Track remediation progress - Auto-patch non-production resources - Create Jira tickets for manual review - Track remediation progress - ML-Based Prioritization Learn from user actions Predict likelihood of exploitation Recommend based on similar environments - Learn from user actions - Predict likelihood of exploitation - Recommend based on similar environments - Compliance Mapping Map findings to compliance frameworks (PCI-DSS, HIPAA, SOC 2) Generate compliance reports Track remediation for audits - Map findings to compliance frameworks (PCI-DSS, HIPAA, SOC 2) - Generate compliance reports - Track remediation for audits - Multi-Account Support Aggregate findings across AWS accounts Organization-wide dashboards Role-based access control - Aggregate findings across AWS accounts - Organization-wide dashboards - Role-based access control - Auto-patch non-production resources - Create Jira tickets for manual review - Track remediation progress - Learn from user actions - Predict likelihood of exploitation - Recommend based on similar environments - Map findings to compliance frameworks (PCI-DSS, HIPAA, SOC 2) - Generate compliance reports - Track remediation for audits - Aggregate findings across AWS accounts - Organization-wide dashboards - Role-based access control - Intelligent aggregation across multiple sources - Smart prioritization based on real risk - Actionable guidance that reduces time-to-fix - Intuitive UX that security teams actually want to use - Backend: Python 3.11, Boto3 - Frontend: Vanilla JavaScript, HTML5, CSS3 - Infrastructure: AWS Lambda, CloudFormation, S3, SNS - APIs: Security Hub, Inspector, Config, Trusted Advisor