aws-security-mcp 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14610,62 +14610,69 @@ var IamScanner = class {
14610
14610
  const summaryMap = summary.SummaryMap ?? {};
14611
14611
  let resourcesScanned = 1;
14612
14612
  const rootArn = `arn:${partition}:iam::${accountId}:root`;
14613
- if (summaryMap["AccountMFAEnabled"] === 0) {
14614
- findings.push(
14615
- makeFinding3({
14616
- riskScore: 10,
14617
- title: "Root account does not have MFA enabled",
14618
- resourceType: "AWS::IAM::Root",
14619
- resourceId: "root",
14620
- resourceArn: rootArn,
14621
- region: "global",
14622
- description: "The AWS root account does not have multi-factor authentication enabled.",
14623
- impact: "Compromised root credentials would grant unrestricted access to all AWS resources with no second factor of authentication.",
14624
- remediationSteps: [
14625
- "Enable MFA on the root account immediately using a hardware or virtual MFA device.",
14626
- "Store the MFA device in a secure location.",
14627
- "Avoid using the root account for daily operations."
14628
- ]
14629
- })
14630
- );
14613
+ const isChinaRegion = region.startsWith("cn-");
14614
+ if (!isChinaRegion) {
14615
+ if (summaryMap["AccountMFAEnabled"] === 0) {
14616
+ findings.push(
14617
+ makeFinding3({
14618
+ riskScore: 10,
14619
+ title: "Root account does not have MFA enabled",
14620
+ resourceType: "AWS::IAM::Root",
14621
+ resourceId: "root",
14622
+ resourceArn: rootArn,
14623
+ region: "global",
14624
+ description: "The AWS root account does not have multi-factor authentication enabled.",
14625
+ impact: "Compromised root credentials would grant unrestricted access to all AWS resources with no second factor of authentication.",
14626
+ remediationSteps: [
14627
+ "Enable MFA on the root account immediately using a hardware or virtual MFA device.",
14628
+ "Store the MFA device in a secure location.",
14629
+ "Avoid using the root account for daily operations."
14630
+ ]
14631
+ })
14632
+ );
14633
+ }
14634
+ } else {
14635
+ warnings.push("Root user checks skipped: AWS China regions use partner-managed accounts without root user.");
14631
14636
  }
14632
- let rootHasAccessKey = false;
14633
- try {
14634
- const content = await waitForCredentialReport(client);
14635
- if (content) {
14636
- const csv = Buffer.from(content).toString("utf-8");
14637
- const lines = csv.split("\n");
14638
- const headers = lines[0]?.split(",") ?? [];
14639
- const ak1Idx = headers.indexOf("access_key_1_active");
14640
- const ak2Idx = headers.indexOf("access_key_2_active");
14641
- if (lines.length > 1) {
14642
- const rootRow = lines[1]?.split(",") ?? [];
14643
- if (ak1Idx >= 0 && rootRow[ak1Idx] === "true" || ak2Idx >= 0 && rootRow[ak2Idx] === "true") {
14644
- rootHasAccessKey = true;
14637
+ if (!isChinaRegion) {
14638
+ let rootHasAccessKey = false;
14639
+ try {
14640
+ const content = await waitForCredentialReport(client);
14641
+ if (content) {
14642
+ const csv = Buffer.from(content).toString("utf-8");
14643
+ const lines = csv.split("\n");
14644
+ const headers = lines[0]?.split(",") ?? [];
14645
+ const ak1Idx = headers.indexOf("access_key_1_active");
14646
+ const ak2Idx = headers.indexOf("access_key_2_active");
14647
+ if (lines.length > 1) {
14648
+ const rootRow = lines[1]?.split(",") ?? [];
14649
+ if (ak1Idx >= 0 && rootRow[ak1Idx] === "true" || ak2Idx >= 0 && rootRow[ak2Idx] === "true") {
14650
+ rootHasAccessKey = true;
14651
+ }
14645
14652
  }
14646
14653
  }
14654
+ } catch (e) {
14655
+ const msg = e instanceof Error ? e.message : String(e);
14656
+ warnings.push(`Credential report check failed: ${msg}`);
14657
+ }
14658
+ if (rootHasAccessKey) {
14659
+ findings.push(
14660
+ makeFinding3({
14661
+ riskScore: 9.5,
14662
+ title: "Root account has active access keys",
14663
+ resourceType: "AWS::IAM::Root",
14664
+ resourceId: "root",
14665
+ resourceArn: rootArn,
14666
+ region: "global",
14667
+ description: "The root account has one or more active access keys.",
14668
+ impact: "Access keys for the root account provide unrestricted API access. If leaked, the entire account is compromised.",
14669
+ remediationSteps: [
14670
+ "Delete all root account access keys.",
14671
+ "Use IAM users or roles with least-privilege policies instead."
14672
+ ]
14673
+ })
14674
+ );
14647
14675
  }
14648
- } catch (e) {
14649
- const msg = e instanceof Error ? e.message : String(e);
14650
- warnings.push(`Credential report check failed: ${msg}`);
14651
- }
14652
- if (rootHasAccessKey) {
14653
- findings.push(
14654
- makeFinding3({
14655
- riskScore: 9.5,
14656
- title: "Root account has active access keys",
14657
- resourceType: "AWS::IAM::Root",
14658
- resourceId: "root",
14659
- resourceArn: rootArn,
14660
- region: "global",
14661
- description: "The root account has one or more active access keys.",
14662
- impact: "Access keys for the root account provide unrestricted API access. If leaked, the entire account is compromised.",
14663
- remediationSteps: [
14664
- "Delete all root account access keys.",
14665
- "Use IAM users or roles with least-privilege policies instead."
14666
- ]
14667
- })
14668
- );
14669
14676
  }
14670
14677
  const users = [];
14671
14678
  let marker;
@@ -15494,6 +15501,416 @@ var VpcScanner = class {
15494
15501
  }
15495
15502
  };
15496
15503
 
15504
+ // src/scanners/service-detection.ts
15505
+ import {
15506
+ SecurityHubClient,
15507
+ DescribeHubCommand
15508
+ } from "@aws-sdk/client-securityhub";
15509
+ import {
15510
+ GuardDutyClient,
15511
+ ListDetectorsCommand
15512
+ } from "@aws-sdk/client-guardduty";
15513
+ import {
15514
+ Inspector2Client,
15515
+ BatchGetAccountStatusCommand
15516
+ } from "@aws-sdk/client-inspector2";
15517
+ import {
15518
+ ConfigServiceClient,
15519
+ DescribeConfigurationRecordersCommand
15520
+ } from "@aws-sdk/client-config-service";
15521
+ import {
15522
+ Macie2Client,
15523
+ GetMacieSessionCommand
15524
+ } from "@aws-sdk/client-macie2";
15525
+ import {
15526
+ CloudTrailClient as CloudTrailClient2,
15527
+ DescribeTrailsCommand as DescribeTrailsCommand2
15528
+ } from "@aws-sdk/client-cloudtrail";
15529
+ function makeFinding8(opts) {
15530
+ const severity = severityFromScore(opts.riskScore);
15531
+ return { ...opts, severity, priority: priorityFromSeverity(severity) };
15532
+ }
15533
+ function isAccessDenied(err) {
15534
+ if (!(err instanceof Error)) return false;
15535
+ const name = err.name ?? "";
15536
+ const code = err.Code ?? "";
15537
+ return name === "AccessDeniedException" || name === "UnauthorizedAccess" || name === "AccessDenied" || code === "AccessDeniedException" || code === "AccessDenied" || name === "ForbiddenException" || // AWS SDK v3 uses __type or $metadata for some errors
15538
+ (err.message?.includes("is not authorized to perform") ?? false) || (err.message?.includes("Access Denied") ?? false);
15539
+ }
15540
+ function isNotEnabled(err) {
15541
+ if (!(err instanceof Error)) return false;
15542
+ return err.name === "InvalidAccessException" || // Security Hub specific
15543
+ err.name === "DisabledException" || err.message.includes("not enabled") || err.message.includes("not subscribed");
15544
+ }
15545
+ function computeMaturityLevel(enabledCount) {
15546
+ if (enabledCount >= 6) return "comprehensive";
15547
+ if (enabledCount >= 4) return "advanced";
15548
+ if (enabledCount >= 2) return "intermediate";
15549
+ return "basic";
15550
+ }
15551
+ var ServiceDetectionScanner = class {
15552
+ moduleName = "service_detection";
15553
+ async scan(ctx) {
15554
+ const { region, partition, accountId } = ctx;
15555
+ const startMs = Date.now();
15556
+ const findings = [];
15557
+ const warnings = [];
15558
+ const services = [];
15559
+ try {
15560
+ const ct = createClient(CloudTrailClient2, region);
15561
+ const resp = await ct.send(new DescribeTrailsCommand2({}));
15562
+ const trails = resp.trailList ?? [];
15563
+ if (trails.length > 0) {
15564
+ services.push({
15565
+ name: "CloudTrail",
15566
+ enabled: true,
15567
+ details: `${trails.length} trail(s) configured`
15568
+ });
15569
+ } else {
15570
+ services.push({
15571
+ name: "CloudTrail",
15572
+ enabled: false,
15573
+ recommendation: "Create a multi-region trail for API logging"
15574
+ });
15575
+ }
15576
+ } catch (err) {
15577
+ if (isAccessDenied(err)) {
15578
+ warnings.push("CloudTrail: insufficient permissions to check status");
15579
+ services.push({ name: "CloudTrail", enabled: null, details: "Access denied" });
15580
+ } else if (isNotEnabled(err)) {
15581
+ services.push({
15582
+ name: "CloudTrail",
15583
+ enabled: false,
15584
+ recommendation: "Create a multi-region trail for API logging"
15585
+ });
15586
+ } else {
15587
+ warnings.push(`CloudTrail detection failed: ${err instanceof Error ? err.message : String(err)}`);
15588
+ services.push({ name: "CloudTrail", enabled: null, details: "Detection error" });
15589
+ }
15590
+ }
15591
+ try {
15592
+ const sh = createClient(SecurityHubClient, region);
15593
+ await sh.send(new DescribeHubCommand({}));
15594
+ services.push({
15595
+ name: "Security Hub",
15596
+ enabled: true,
15597
+ details: "Enabled with automated security checks"
15598
+ });
15599
+ } catch (err) {
15600
+ if (isAccessDenied(err)) {
15601
+ warnings.push("Security Hub: insufficient permissions to check status");
15602
+ services.push({ name: "Security Hub", enabled: null, details: "Access denied" });
15603
+ } else if (isNotEnabled(err)) {
15604
+ services.push({
15605
+ name: "Security Hub",
15606
+ enabled: false,
15607
+ recommendation: "Enable Security Hub for 300+ automated security checks",
15608
+ freeTrialAvailable: true
15609
+ });
15610
+ findings.push(
15611
+ makeFinding8({
15612
+ riskScore: 7.5,
15613
+ title: "AWS Security Hub is not enabled",
15614
+ resourceType: "AWS::SecurityHub::Hub",
15615
+ resourceId: "securityhub",
15616
+ resourceArn: `arn:${partition}:securityhub:${region}:${accountId}:hub/default`,
15617
+ region,
15618
+ description: "AWS Security Hub is not enabled in this region. Security Hub provides a comprehensive view of security alerts and compliance status.",
15619
+ impact: "Enables 300+ automated security checks across AWS services. Without it, security findings are fragmented across individual services.",
15620
+ remediationSteps: [
15621
+ "Open the AWS Security Hub console.",
15622
+ "Click 'Go to Security Hub' and enable it.",
15623
+ "Enable the AWS Foundational Security Best Practices standard.",
15624
+ "Security Hub offers a 30-day free trial."
15625
+ ]
15626
+ })
15627
+ );
15628
+ } else {
15629
+ warnings.push(`Security Hub detection failed: ${err instanceof Error ? err.message : String(err)}`);
15630
+ services.push({ name: "Security Hub", enabled: null, details: "Detection error" });
15631
+ }
15632
+ }
15633
+ try {
15634
+ const gd = createClient(GuardDutyClient, region);
15635
+ const resp = await gd.send(new ListDetectorsCommand({}));
15636
+ const detectors = resp.DetectorIds ?? [];
15637
+ if (detectors.length > 0) {
15638
+ services.push({
15639
+ name: "GuardDuty",
15640
+ enabled: true,
15641
+ details: `${detectors.length} detector(s) active`
15642
+ });
15643
+ } else {
15644
+ services.push({
15645
+ name: "GuardDuty",
15646
+ enabled: false,
15647
+ recommendation: "Enable GuardDuty for continuous threat detection",
15648
+ freeTrialAvailable: true
15649
+ });
15650
+ findings.push(
15651
+ makeFinding8({
15652
+ riskScore: 7.5,
15653
+ title: "Amazon GuardDuty is not enabled",
15654
+ resourceType: "AWS::GuardDuty::Detector",
15655
+ resourceId: "guardduty",
15656
+ resourceArn: `arn:${partition}:guardduty:${region}:${accountId}:detector/none`,
15657
+ region,
15658
+ description: "Amazon GuardDuty is not enabled in this region. GuardDuty provides intelligent threat detection by analyzing CloudTrail, VPC Flow Logs, and DNS logs.",
15659
+ impact: "Provides continuous threat detection for account compromise, instance compromise, and malicious reconnaissance. Without it, many attack patterns go undetected.",
15660
+ remediationSteps: [
15661
+ "Open the Amazon GuardDuty console.",
15662
+ "Click 'Get Started' and enable GuardDuty.",
15663
+ "GuardDuty offers a 30-day free trial.",
15664
+ "Consider enabling S3 protection and EKS protection add-ons."
15665
+ ]
15666
+ })
15667
+ );
15668
+ }
15669
+ } catch (err) {
15670
+ if (isAccessDenied(err)) {
15671
+ warnings.push("GuardDuty: insufficient permissions to check status");
15672
+ services.push({ name: "GuardDuty", enabled: null, details: "Access denied" });
15673
+ } else if (isNotEnabled(err)) {
15674
+ services.push({
15675
+ name: "GuardDuty",
15676
+ enabled: false,
15677
+ recommendation: "Enable GuardDuty for continuous threat detection",
15678
+ freeTrialAvailable: true
15679
+ });
15680
+ findings.push(
15681
+ makeFinding8({
15682
+ riskScore: 7.5,
15683
+ title: "Amazon GuardDuty is not enabled",
15684
+ resourceType: "AWS::GuardDuty::Detector",
15685
+ resourceId: "guardduty",
15686
+ resourceArn: `arn:${partition}:guardduty:${region}:${accountId}:detector/none`,
15687
+ region,
15688
+ description: "Amazon GuardDuty is not enabled in this region. GuardDuty provides intelligent threat detection by analyzing CloudTrail, VPC Flow Logs, and DNS logs.",
15689
+ impact: "Provides continuous threat detection for account compromise, instance compromise, and malicious reconnaissance. Without it, many attack patterns go undetected.",
15690
+ remediationSteps: [
15691
+ "Open the Amazon GuardDuty console.",
15692
+ "Click 'Get Started' and enable GuardDuty.",
15693
+ "GuardDuty offers a 30-day free trial.",
15694
+ "Consider enabling S3 protection and EKS protection add-ons."
15695
+ ]
15696
+ })
15697
+ );
15698
+ } else {
15699
+ warnings.push(`GuardDuty detection failed: ${err instanceof Error ? err.message : String(err)}`);
15700
+ services.push({ name: "GuardDuty", enabled: null, details: "Detection error" });
15701
+ }
15702
+ }
15703
+ try {
15704
+ const insp = createClient(Inspector2Client, region);
15705
+ const resp = await insp.send(new BatchGetAccountStatusCommand({ accountIds: [accountId] }));
15706
+ const accounts = resp.accounts ?? [];
15707
+ const active = accounts.some(
15708
+ (a) => a.state?.status === "ENABLED" || a.state?.status === "ENABLING"
15709
+ );
15710
+ if (active) {
15711
+ services.push({
15712
+ name: "Inspector",
15713
+ enabled: true,
15714
+ details: "Vulnerability scanning active"
15715
+ });
15716
+ } else {
15717
+ services.push({
15718
+ name: "Inspector",
15719
+ enabled: false,
15720
+ recommendation: "Enable Inspector to scan for software vulnerabilities",
15721
+ freeTrialAvailable: true
15722
+ });
15723
+ findings.push(
15724
+ makeFinding8({
15725
+ riskScore: 6,
15726
+ title: "Amazon Inspector is not enabled",
15727
+ resourceType: "AWS::Inspector2::AccountStatus",
15728
+ resourceId: "inspector",
15729
+ resourceArn: `arn:${partition}:inspector2:${region}:${accountId}:account`,
15730
+ region,
15731
+ description: "Amazon Inspector is not enabled in this region. Inspector automatically discovers and scans EC2 instances, containers, and Lambda functions for software vulnerabilities.",
15732
+ impact: "Scans for software vulnerabilities in EC2 instances, container images, and Lambda functions. Without it, known CVEs may go undetected.",
15733
+ remediationSteps: [
15734
+ "Open the Amazon Inspector console.",
15735
+ "Click 'Get Started' and enable Inspector.",
15736
+ "Inspector offers a 15-day free trial.",
15737
+ "Enable scanning for EC2, ECR, and Lambda as appropriate."
15738
+ ]
15739
+ })
15740
+ );
15741
+ }
15742
+ } catch (err) {
15743
+ if (isAccessDenied(err)) {
15744
+ warnings.push("Inspector: insufficient permissions to check status");
15745
+ services.push({ name: "Inspector", enabled: null, details: "Access denied" });
15746
+ } else if (isNotEnabled(err)) {
15747
+ services.push({
15748
+ name: "Inspector",
15749
+ enabled: false,
15750
+ recommendation: "Enable Inspector to scan for software vulnerabilities",
15751
+ freeTrialAvailable: true
15752
+ });
15753
+ findings.push(
15754
+ makeFinding8({
15755
+ riskScore: 6,
15756
+ title: "Amazon Inspector is not enabled",
15757
+ resourceType: "AWS::Inspector2::AccountStatus",
15758
+ resourceId: "inspector",
15759
+ resourceArn: `arn:${partition}:inspector2:${region}:${accountId}:account`,
15760
+ region,
15761
+ description: "Amazon Inspector is not enabled in this region. Inspector automatically discovers and scans EC2 instances, containers, and Lambda functions for software vulnerabilities.",
15762
+ impact: "Scans for software vulnerabilities in EC2 instances, container images, and Lambda functions. Without it, known CVEs may go undetected.",
15763
+ remediationSteps: [
15764
+ "Open the Amazon Inspector console.",
15765
+ "Click 'Get Started' and enable Inspector.",
15766
+ "Inspector offers a 15-day free trial.",
15767
+ "Enable scanning for EC2, ECR, and Lambda as appropriate."
15768
+ ]
15769
+ })
15770
+ );
15771
+ } else {
15772
+ warnings.push(`Inspector detection failed: ${err instanceof Error ? err.message : String(err)}`);
15773
+ services.push({ name: "Inspector", enabled: null, details: "Detection error" });
15774
+ }
15775
+ }
15776
+ try {
15777
+ const cfg = createClient(ConfigServiceClient, region);
15778
+ const resp = await cfg.send(new DescribeConfigurationRecordersCommand({}));
15779
+ const recorders = resp.ConfigurationRecorders ?? [];
15780
+ if (recorders.length > 0) {
15781
+ services.push({
15782
+ name: "AWS Config",
15783
+ enabled: true,
15784
+ details: `${recorders.length} recorder(s) configured`
15785
+ });
15786
+ } else {
15787
+ services.push({
15788
+ name: "AWS Config",
15789
+ enabled: false,
15790
+ recommendation: "Enable AWS Config to track configuration changes"
15791
+ });
15792
+ findings.push(
15793
+ makeFinding8({
15794
+ riskScore: 6,
15795
+ title: "AWS Config is not enabled",
15796
+ resourceType: "AWS::Config::ConfigurationRecorder",
15797
+ resourceId: "config",
15798
+ resourceArn: `arn:${partition}:config:${region}:${accountId}:configuration-recorder/none`,
15799
+ region,
15800
+ description: "AWS Config is not enabled in this region. Config continuously records resource configurations and enables compliance auditing.",
15801
+ impact: "Tracks configuration changes and enables compliance rules. Without it, configuration drift and non-compliant resources go undetected.",
15802
+ remediationSteps: [
15803
+ "Open the AWS Config console.",
15804
+ "Click 'Get Started' and configure a recorder.",
15805
+ "Select the resource types to record.",
15806
+ "Configure an S3 bucket for configuration snapshots."
15807
+ ]
15808
+ })
15809
+ );
15810
+ }
15811
+ } catch (err) {
15812
+ if (isAccessDenied(err)) {
15813
+ warnings.push("AWS Config: insufficient permissions to check status");
15814
+ services.push({ name: "AWS Config", enabled: null, details: "Access denied" });
15815
+ } else if (isNotEnabled(err)) {
15816
+ services.push({
15817
+ name: "AWS Config",
15818
+ enabled: false,
15819
+ recommendation: "Enable AWS Config to track configuration changes"
15820
+ });
15821
+ findings.push(
15822
+ makeFinding8({
15823
+ riskScore: 6,
15824
+ title: "AWS Config is not enabled",
15825
+ resourceType: "AWS::Config::ConfigurationRecorder",
15826
+ resourceId: "config",
15827
+ resourceArn: `arn:${partition}:config:${region}:${accountId}:configuration-recorder/none`,
15828
+ region,
15829
+ description: "AWS Config is not enabled in this region. Config continuously records resource configurations and enables compliance auditing.",
15830
+ impact: "Tracks configuration changes and enables compliance rules. Without it, configuration drift and non-compliant resources go undetected.",
15831
+ remediationSteps: [
15832
+ "Open the AWS Config console.",
15833
+ "Click 'Get Started' and configure a recorder.",
15834
+ "Select the resource types to record.",
15835
+ "Configure an S3 bucket for configuration snapshots."
15836
+ ]
15837
+ })
15838
+ );
15839
+ } else {
15840
+ warnings.push(`AWS Config detection failed: ${err instanceof Error ? err.message : String(err)}`);
15841
+ services.push({ name: "AWS Config", enabled: null, details: "Detection error" });
15842
+ }
15843
+ }
15844
+ if (region.startsWith("cn-")) {
15845
+ services.push({ name: "Macie", enabled: null, details: "Not available in China regions" });
15846
+ warnings.push("Macie is not available in AWS China regions.");
15847
+ } else {
15848
+ try {
15849
+ const mc = createClient(Macie2Client, region);
15850
+ await mc.send(new GetMacieSessionCommand({}));
15851
+ services.push({
15852
+ name: "Macie",
15853
+ enabled: true,
15854
+ details: "Sensitive data detection active"
15855
+ });
15856
+ } catch (err) {
15857
+ if (isAccessDenied(err)) {
15858
+ warnings.push("Macie: insufficient permissions to check status");
15859
+ services.push({ name: "Macie", enabled: null, details: "Access denied" });
15860
+ } else if (isNotEnabled(err)) {
15861
+ services.push({
15862
+ name: "Macie",
15863
+ enabled: false,
15864
+ recommendation: "Enable Macie to detect sensitive data in S3",
15865
+ freeTrialAvailable: true
15866
+ });
15867
+ findings.push(
15868
+ makeFinding8({
15869
+ riskScore: 5,
15870
+ title: "Amazon Macie is not enabled",
15871
+ resourceType: "AWS::Macie::Session",
15872
+ resourceId: "macie",
15873
+ resourceArn: `arn:${partition}:macie2:${region}:${accountId}:session`,
15874
+ region,
15875
+ description: "Amazon Macie is not enabled in this region. Macie uses machine learning to discover and protect sensitive data stored in S3.",
15876
+ impact: "Detects sensitive data (PII, credentials, financial data) in S3 buckets. Without it, sensitive data exposure may go unnoticed.",
15877
+ remediationSteps: [
15878
+ "Open the Amazon Macie console.",
15879
+ "Click 'Get Started' and enable Macie.",
15880
+ "Macie offers a 30-day free trial for sensitive data discovery.",
15881
+ "Configure automated sensitive data discovery jobs."
15882
+ ]
15883
+ })
15884
+ );
15885
+ } else {
15886
+ warnings.push(`Macie detection failed: ${err instanceof Error ? err.message : String(err)}`);
15887
+ services.push({ name: "Macie", enabled: null, details: "Detection error" });
15888
+ }
15889
+ }
15890
+ }
15891
+ const knownServices = services.filter((s) => s.enabled !== null);
15892
+ const enabledCount = services.filter((s) => s.enabled === true).length;
15893
+ const coveragePercent = knownServices.length > 0 ? Math.round(enabledCount / knownServices.length * 100) : 0;
15894
+ const maturityLevel = computeMaturityLevel(enabledCount);
15895
+ const detectionResult = {
15896
+ services,
15897
+ coveragePercent,
15898
+ maturityLevel
15899
+ };
15900
+ return {
15901
+ module: this.moduleName,
15902
+ status: "success",
15903
+ warnings: warnings.length > 0 ? warnings : void 0,
15904
+ resourcesScanned: services.length,
15905
+ findingsCount: findings.length,
15906
+ scanTimeMs: Date.now() - startMs,
15907
+ findings,
15908
+ // Attach the structured detection result as a custom property via the findings metadata
15909
+ ...{ serviceDetection: detectionResult }
15910
+ };
15911
+ }
15912
+ };
15913
+
15497
15914
  // src/tools/report-tool.ts
15498
15915
  var SEVERITY_ICON = {
15499
15916
  CRITICAL: "\u{1F534}",
@@ -15720,6 +16137,23 @@ Reviews VPC network configuration.
15720
16137
  - **Instances in default VPC** \u2014 Risk 7.0: Resources in the default VPC lack proper isolation.
15721
16138
  - **Missing VPC Flow Logs** \u2014 Risk 7.0: No network traffic logging enabled.
15722
16139
  - **Default SG with custom inbound rules** \u2014 Risk 5.5: Default security group modified with open rules.
16140
+
16141
+ ## 8. Service Detection (service_detection)
16142
+ Detects which AWS security services are enabled and assesses overall security maturity.
16143
+ - **Security Hub not enabled** \u2014 Risk 7.5: Provides 300+ automated security checks.
16144
+ - **GuardDuty not enabled** \u2014 Risk 7.5: Provides continuous threat detection.
16145
+ - **Inspector not enabled** \u2014 Risk 6.0: Scans for software vulnerabilities.
16146
+ - **AWS Config not enabled** \u2014 Risk 6.0: Tracks configuration changes.
16147
+ - **Macie not enabled** \u2014 Risk 5.0: Detects sensitive data in S3 (not available in China regions).
16148
+ - CloudTrail detection is included for coverage metrics; findings handled by the CloudTrail module.
16149
+
16150
+ ### Maturity Levels
16151
+ | Enabled Services | Level |
16152
+ |------------------|-------|
16153
+ | 0\u20131 | Basic |
16154
+ | 2\u20133 | Intermediate |
16155
+ | 4\u20135 | Advanced |
16156
+ | 6 | Comprehensive |
15723
16157
  `;
15724
16158
  var RISK_SCORING_CONTENT = `# Risk Scoring Model
15725
16159
 
@@ -15771,7 +16205,8 @@ var MODULE_DESCRIPTIONS = {
15771
16205
  cloudtrail: "Validates CloudTrail logging configuration (multi-region, log validation, CloudWatch integration).",
15772
16206
  rds: "Scans RDS instances for public accessibility, encryption, backups, and deletion protection.",
15773
16207
  ebs: "Checks EBS volumes and snapshots for encryption and public sharing.",
15774
- vpc: "Reviews VPC configuration including default VPC usage, flow logs, and default security groups."
16208
+ vpc: "Reviews VPC configuration including default VPC usage, flow logs, and default security groups.",
16209
+ service_detection: "Detects which AWS security services (Security Hub, GuardDuty, Inspector, Config, Macie) are enabled and assesses security maturity."
15775
16210
  };
15776
16211
  function summarizeResult(result) {
15777
16212
  const { summary } = result;
@@ -15813,7 +16248,8 @@ function createServer(defaultRegion) {
15813
16248
  new CloudTrailScanner(),
15814
16249
  new RdsScanner(),
15815
16250
  new EbsScanner(),
15816
- new VpcScanner()
16251
+ new VpcScanner(),
16252
+ new ServiceDetectionScanner()
15817
16253
  ];
15818
16254
  const scannerMap = /* @__PURE__ */ new Map();
15819
16255
  for (const s of allScanners) {
@@ -15821,7 +16257,7 @@ function createServer(defaultRegion) {
15821
16257
  }
15822
16258
  server.tool(
15823
16259
  "scan_all",
15824
- "Run all 7 security scanners in parallel. Read-only. Does not modify any AWS resources.",
16260
+ "Run all 8 security scanners in parallel (including service detection). Read-only. Does not modify any AWS resources.",
15825
16261
  { region: external_exports.string().optional().describe("AWS region to scan (default: server region)") },
15826
16262
  async ({ region }) => {
15827
16263
  try {
@@ -15845,7 +16281,8 @@ function createServer(defaultRegion) {
15845
16281
  { toolName: "scan_cloudtrail", moduleName: "cloudtrail", label: "CloudTrail" },
15846
16282
  { toolName: "scan_rds", moduleName: "rds", label: "RDS" },
15847
16283
  { toolName: "scan_ebs", moduleName: "ebs", label: "EBS" },
15848
- { toolName: "scan_vpc", moduleName: "vpc", label: "VPC" }
16284
+ { toolName: "scan_vpc", moduleName: "vpc", label: "VPC" },
16285
+ { toolName: "detect_services", moduleName: "service_detection", label: "Security Service Detection" }
15849
16286
  ];
15850
16287
  for (const { toolName, moduleName, label } of individualScanners) {
15851
16288
  server.tool(
@@ -15884,6 +16321,121 @@ function createServer(defaultRegion) {
15884
16321
  }
15885
16322
  }
15886
16323
  );
16324
+ server.tool(
16325
+ "generate_maturity_report",
16326
+ "Generate a security maturity assessment report from scan_all results. Requires service_detection module output. Read-only.",
16327
+ { scan_results: external_exports.string().describe("JSON string of FullScanResult from scan_all") },
16328
+ async ({ scan_results }) => {
16329
+ try {
16330
+ const parsed = JSON.parse(scan_results);
16331
+ const sdModule = parsed.modules.find((m) => m.module === "service_detection");
16332
+ if (!sdModule) {
16333
+ return {
16334
+ content: [{ type: "text", text: "Error: scan results do not include service_detection module. Run scan_all first." }],
16335
+ isError: true
16336
+ };
16337
+ }
16338
+ const detection = sdModule.serviceDetection;
16339
+ if (!detection) {
16340
+ return {
16341
+ content: [{ type: "text", text: "Error: service detection data is missing (possible JSON round-trip loss). Run scan_all to get fresh results with complete service detection data." }],
16342
+ isError: true
16343
+ };
16344
+ }
16345
+ const serviceImpacts = {
16346
+ "CloudTrail": "API activity logging",
16347
+ "Security Hub": "+300 security checks",
16348
+ "GuardDuty": "Threat detection",
16349
+ "Inspector": "Vulnerability scanning",
16350
+ "AWS Config": "Configuration tracking",
16351
+ "Macie": "Sensitive data detection"
16352
+ };
16353
+ const serviceFreeTrials = {
16354
+ "Security Hub": true,
16355
+ "GuardDuty": true,
16356
+ "Inspector": true,
16357
+ "Macie": true
16358
+ };
16359
+ const services = detection.services;
16360
+ const coveragePercent = detection.coveragePercent;
16361
+ const maturityLevel = detection.maturityLevel;
16362
+ const enabledCount = services.filter((s) => s.enabled === true).length;
16363
+ const knownCount = services.filter((s) => s.enabled !== null).length;
16364
+ const totalServices = services.length;
16365
+ const lines = [];
16366
+ lines.push("# AWS Security Maturity Assessment");
16367
+ lines.push("");
16368
+ lines.push(`## Account: ${parsed.accountId} | Region: ${parsed.region}`);
16369
+ lines.push("");
16370
+ lines.push(`## Security Service Coverage: ${coveragePercent}%`);
16371
+ lines.push(`## Maturity Level: ${maturityLevel.charAt(0).toUpperCase() + maturityLevel.slice(1)}`);
16372
+ lines.push("");
16373
+ lines.push("### Service Status");
16374
+ lines.push("");
16375
+ lines.push("| Service | Status | Impact |");
16376
+ lines.push("|---------|--------|--------|");
16377
+ for (const svc of services) {
16378
+ const status = svc.enabled === true ? "\u2705 Enabled" : svc.enabled === false ? "\u274C Not Enabled" : "\u26A0\uFE0F Unknown";
16379
+ const impact = serviceImpacts[svc.name] ?? "";
16380
+ lines.push(`| ${svc.name} | ${status} | ${impact} |`);
16381
+ }
16382
+ const unknowns = services.filter((s) => s.enabled === null);
16383
+ if (unknowns.length > 0) {
16384
+ lines.push("");
16385
+ lines.push(`> \u26A0\uFE0F ${unknowns.length} service(s) could not be checked (access denied or detection error). Re-run with appropriate permissions for accurate coverage.`);
16386
+ }
16387
+ const disabled = services.filter((s) => s.enabled === false);
16388
+ if (disabled.length > 0) {
16389
+ lines.push("");
16390
+ lines.push("### Recommendations (Priority Order)");
16391
+ lines.push("");
16392
+ const priorityOrder = ["Security Hub", "GuardDuty", "Inspector", "AWS Config", "Macie", "CloudTrail"];
16393
+ const sorted = disabled.sort(
16394
+ (a, b) => priorityOrder.indexOf(a.name) - priorityOrder.indexOf(b.name)
16395
+ );
16396
+ let idx = 1;
16397
+ for (const svc of sorted) {
16398
+ const trial = serviceFreeTrials[svc.name] ? " \u2014 free trial available" : "";
16399
+ lines.push(`${idx}. Enable ${svc.name}${trial}`);
16400
+ idx++;
16401
+ }
16402
+ }
16403
+ lines.push("");
16404
+ lines.push("### Maturity Roadmap");
16405
+ lines.push("");
16406
+ if (unknowns.length > 0) {
16407
+ lines.push(`- **Current**: ${maturityLevel.charAt(0).toUpperCase() + maturityLevel.slice(1)} (${enabledCount}/${knownCount} known services, ${unknowns.length} unknown)`);
16408
+ } else {
16409
+ lines.push(`- **Current**: ${maturityLevel.charAt(0).toUpperCase() + maturityLevel.slice(1)} (${enabledCount}/${totalServices} services)`);
16410
+ }
16411
+ if (maturityLevel !== "comprehensive") {
16412
+ const nextMilestones = {
16413
+ basic: { level: "Intermediate", target: 2, suggestions: ["Security Hub", "GuardDuty"] },
16414
+ intermediate: { level: "Advanced", target: 4, suggestions: ["Inspector", "AWS Config"] },
16415
+ advanced: { level: "Comprehensive", target: 6, suggestions: ["Macie"] }
16416
+ };
16417
+ const next = nextMilestones[maturityLevel];
16418
+ if (next) {
16419
+ const remaining = next.suggestions.filter(
16420
+ (s) => services.some((svc) => svc.name === s && !svc.enabled)
16421
+ );
16422
+ if (remaining.length > 0) {
16423
+ lines.push(`- **Next milestone**: ${next.level} (${next.target}/${knownCount}) \u2014 enable ${remaining.join(" + ")}`);
16424
+ }
16425
+ }
16426
+ lines.push(`- **Target**: Comprehensive (${knownCount}/${knownCount})`);
16427
+ }
16428
+ lines.push("");
16429
+ const report = lines.join("\n");
16430
+ return { content: [{ type: "text", text: report }] };
16431
+ } catch (err) {
16432
+ return {
16433
+ content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
16434
+ isError: true
16435
+ };
16436
+ }
16437
+ }
16438
+ );
15887
16439
  server.tool(
15888
16440
  "save_results",
15889
16441
  "Saves scan results to local disk or S3 for dashboard display. Does not modify any AWS resources.",