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.
- package/dashboard/dist/data.json +3 -3
- package/dist/bin/aws-security-mcp.js +608 -56
- package/dist/bin/aws-security-mcp.js.map +1 -1
- package/dist/src/index.js +608 -56
- package/dist/src/index.js.map +1 -1
- package/package.json +6 -1
package/dist/src/index.js
CHANGED
|
@@ -14384,62 +14384,69 @@ var IamScanner = class {
|
|
|
14384
14384
|
const summaryMap = summary.SummaryMap ?? {};
|
|
14385
14385
|
let resourcesScanned = 1;
|
|
14386
14386
|
const rootArn = `arn:${partition}:iam::${accountId}:root`;
|
|
14387
|
-
|
|
14388
|
-
|
|
14389
|
-
|
|
14390
|
-
|
|
14391
|
-
|
|
14392
|
-
|
|
14393
|
-
|
|
14394
|
-
|
|
14395
|
-
|
|
14396
|
-
|
|
14397
|
-
|
|
14398
|
-
|
|
14399
|
-
"
|
|
14400
|
-
|
|
14401
|
-
|
|
14402
|
-
|
|
14403
|
-
|
|
14404
|
-
|
|
14387
|
+
const isChinaRegion = region.startsWith("cn-");
|
|
14388
|
+
if (!isChinaRegion) {
|
|
14389
|
+
if (summaryMap["AccountMFAEnabled"] === 0) {
|
|
14390
|
+
findings.push(
|
|
14391
|
+
makeFinding3({
|
|
14392
|
+
riskScore: 10,
|
|
14393
|
+
title: "Root account does not have MFA enabled",
|
|
14394
|
+
resourceType: "AWS::IAM::Root",
|
|
14395
|
+
resourceId: "root",
|
|
14396
|
+
resourceArn: rootArn,
|
|
14397
|
+
region: "global",
|
|
14398
|
+
description: "The AWS root account does not have multi-factor authentication enabled.",
|
|
14399
|
+
impact: "Compromised root credentials would grant unrestricted access to all AWS resources with no second factor of authentication.",
|
|
14400
|
+
remediationSteps: [
|
|
14401
|
+
"Enable MFA on the root account immediately using a hardware or virtual MFA device.",
|
|
14402
|
+
"Store the MFA device in a secure location.",
|
|
14403
|
+
"Avoid using the root account for daily operations."
|
|
14404
|
+
]
|
|
14405
|
+
})
|
|
14406
|
+
);
|
|
14407
|
+
}
|
|
14408
|
+
} else {
|
|
14409
|
+
warnings.push("Root user checks skipped: AWS China regions use partner-managed accounts without root user.");
|
|
14405
14410
|
}
|
|
14406
|
-
|
|
14407
|
-
|
|
14408
|
-
|
|
14409
|
-
|
|
14410
|
-
|
|
14411
|
-
|
|
14412
|
-
|
|
14413
|
-
|
|
14414
|
-
|
|
14415
|
-
|
|
14416
|
-
|
|
14417
|
-
|
|
14418
|
-
|
|
14411
|
+
if (!isChinaRegion) {
|
|
14412
|
+
let rootHasAccessKey = false;
|
|
14413
|
+
try {
|
|
14414
|
+
const content = await waitForCredentialReport(client);
|
|
14415
|
+
if (content) {
|
|
14416
|
+
const csv = Buffer.from(content).toString("utf-8");
|
|
14417
|
+
const lines = csv.split("\n");
|
|
14418
|
+
const headers = lines[0]?.split(",") ?? [];
|
|
14419
|
+
const ak1Idx = headers.indexOf("access_key_1_active");
|
|
14420
|
+
const ak2Idx = headers.indexOf("access_key_2_active");
|
|
14421
|
+
if (lines.length > 1) {
|
|
14422
|
+
const rootRow = lines[1]?.split(",") ?? [];
|
|
14423
|
+
if (ak1Idx >= 0 && rootRow[ak1Idx] === "true" || ak2Idx >= 0 && rootRow[ak2Idx] === "true") {
|
|
14424
|
+
rootHasAccessKey = true;
|
|
14425
|
+
}
|
|
14419
14426
|
}
|
|
14420
14427
|
}
|
|
14428
|
+
} catch (e) {
|
|
14429
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
14430
|
+
warnings.push(`Credential report check failed: ${msg}`);
|
|
14431
|
+
}
|
|
14432
|
+
if (rootHasAccessKey) {
|
|
14433
|
+
findings.push(
|
|
14434
|
+
makeFinding3({
|
|
14435
|
+
riskScore: 9.5,
|
|
14436
|
+
title: "Root account has active access keys",
|
|
14437
|
+
resourceType: "AWS::IAM::Root",
|
|
14438
|
+
resourceId: "root",
|
|
14439
|
+
resourceArn: rootArn,
|
|
14440
|
+
region: "global",
|
|
14441
|
+
description: "The root account has one or more active access keys.",
|
|
14442
|
+
impact: "Access keys for the root account provide unrestricted API access. If leaked, the entire account is compromised.",
|
|
14443
|
+
remediationSteps: [
|
|
14444
|
+
"Delete all root account access keys.",
|
|
14445
|
+
"Use IAM users or roles with least-privilege policies instead."
|
|
14446
|
+
]
|
|
14447
|
+
})
|
|
14448
|
+
);
|
|
14421
14449
|
}
|
|
14422
|
-
} catch (e) {
|
|
14423
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
14424
|
-
warnings.push(`Credential report check failed: ${msg}`);
|
|
14425
|
-
}
|
|
14426
|
-
if (rootHasAccessKey) {
|
|
14427
|
-
findings.push(
|
|
14428
|
-
makeFinding3({
|
|
14429
|
-
riskScore: 9.5,
|
|
14430
|
-
title: "Root account has active access keys",
|
|
14431
|
-
resourceType: "AWS::IAM::Root",
|
|
14432
|
-
resourceId: "root",
|
|
14433
|
-
resourceArn: rootArn,
|
|
14434
|
-
region: "global",
|
|
14435
|
-
description: "The root account has one or more active access keys.",
|
|
14436
|
-
impact: "Access keys for the root account provide unrestricted API access. If leaked, the entire account is compromised.",
|
|
14437
|
-
remediationSteps: [
|
|
14438
|
-
"Delete all root account access keys.",
|
|
14439
|
-
"Use IAM users or roles with least-privilege policies instead."
|
|
14440
|
-
]
|
|
14441
|
-
})
|
|
14442
|
-
);
|
|
14443
14450
|
}
|
|
14444
14451
|
const users = [];
|
|
14445
14452
|
let marker;
|
|
@@ -15268,6 +15275,416 @@ var VpcScanner = class {
|
|
|
15268
15275
|
}
|
|
15269
15276
|
};
|
|
15270
15277
|
|
|
15278
|
+
// src/scanners/service-detection.ts
|
|
15279
|
+
import {
|
|
15280
|
+
SecurityHubClient,
|
|
15281
|
+
DescribeHubCommand
|
|
15282
|
+
} from "@aws-sdk/client-securityhub";
|
|
15283
|
+
import {
|
|
15284
|
+
GuardDutyClient,
|
|
15285
|
+
ListDetectorsCommand
|
|
15286
|
+
} from "@aws-sdk/client-guardduty";
|
|
15287
|
+
import {
|
|
15288
|
+
Inspector2Client,
|
|
15289
|
+
BatchGetAccountStatusCommand
|
|
15290
|
+
} from "@aws-sdk/client-inspector2";
|
|
15291
|
+
import {
|
|
15292
|
+
ConfigServiceClient,
|
|
15293
|
+
DescribeConfigurationRecordersCommand
|
|
15294
|
+
} from "@aws-sdk/client-config-service";
|
|
15295
|
+
import {
|
|
15296
|
+
Macie2Client,
|
|
15297
|
+
GetMacieSessionCommand
|
|
15298
|
+
} from "@aws-sdk/client-macie2";
|
|
15299
|
+
import {
|
|
15300
|
+
CloudTrailClient as CloudTrailClient2,
|
|
15301
|
+
DescribeTrailsCommand as DescribeTrailsCommand2
|
|
15302
|
+
} from "@aws-sdk/client-cloudtrail";
|
|
15303
|
+
function makeFinding8(opts) {
|
|
15304
|
+
const severity = severityFromScore(opts.riskScore);
|
|
15305
|
+
return { ...opts, severity, priority: priorityFromSeverity(severity) };
|
|
15306
|
+
}
|
|
15307
|
+
function isAccessDenied(err) {
|
|
15308
|
+
if (!(err instanceof Error)) return false;
|
|
15309
|
+
const name = err.name ?? "";
|
|
15310
|
+
const code = err.Code ?? "";
|
|
15311
|
+
return name === "AccessDeniedException" || name === "UnauthorizedAccess" || name === "AccessDenied" || code === "AccessDeniedException" || code === "AccessDenied" || name === "ForbiddenException" || // AWS SDK v3 uses __type or $metadata for some errors
|
|
15312
|
+
(err.message?.includes("is not authorized to perform") ?? false) || (err.message?.includes("Access Denied") ?? false);
|
|
15313
|
+
}
|
|
15314
|
+
function isNotEnabled(err) {
|
|
15315
|
+
if (!(err instanceof Error)) return false;
|
|
15316
|
+
return err.name === "InvalidAccessException" || // Security Hub specific
|
|
15317
|
+
err.name === "DisabledException" || err.message.includes("not enabled") || err.message.includes("not subscribed");
|
|
15318
|
+
}
|
|
15319
|
+
function computeMaturityLevel(enabledCount) {
|
|
15320
|
+
if (enabledCount >= 6) return "comprehensive";
|
|
15321
|
+
if (enabledCount >= 4) return "advanced";
|
|
15322
|
+
if (enabledCount >= 2) return "intermediate";
|
|
15323
|
+
return "basic";
|
|
15324
|
+
}
|
|
15325
|
+
var ServiceDetectionScanner = class {
|
|
15326
|
+
moduleName = "service_detection";
|
|
15327
|
+
async scan(ctx) {
|
|
15328
|
+
const { region, partition, accountId } = ctx;
|
|
15329
|
+
const startMs = Date.now();
|
|
15330
|
+
const findings = [];
|
|
15331
|
+
const warnings = [];
|
|
15332
|
+
const services = [];
|
|
15333
|
+
try {
|
|
15334
|
+
const ct = createClient(CloudTrailClient2, region);
|
|
15335
|
+
const resp = await ct.send(new DescribeTrailsCommand2({}));
|
|
15336
|
+
const trails = resp.trailList ?? [];
|
|
15337
|
+
if (trails.length > 0) {
|
|
15338
|
+
services.push({
|
|
15339
|
+
name: "CloudTrail",
|
|
15340
|
+
enabled: true,
|
|
15341
|
+
details: `${trails.length} trail(s) configured`
|
|
15342
|
+
});
|
|
15343
|
+
} else {
|
|
15344
|
+
services.push({
|
|
15345
|
+
name: "CloudTrail",
|
|
15346
|
+
enabled: false,
|
|
15347
|
+
recommendation: "Create a multi-region trail for API logging"
|
|
15348
|
+
});
|
|
15349
|
+
}
|
|
15350
|
+
} catch (err) {
|
|
15351
|
+
if (isAccessDenied(err)) {
|
|
15352
|
+
warnings.push("CloudTrail: insufficient permissions to check status");
|
|
15353
|
+
services.push({ name: "CloudTrail", enabled: null, details: "Access denied" });
|
|
15354
|
+
} else if (isNotEnabled(err)) {
|
|
15355
|
+
services.push({
|
|
15356
|
+
name: "CloudTrail",
|
|
15357
|
+
enabled: false,
|
|
15358
|
+
recommendation: "Create a multi-region trail for API logging"
|
|
15359
|
+
});
|
|
15360
|
+
} else {
|
|
15361
|
+
warnings.push(`CloudTrail detection failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
15362
|
+
services.push({ name: "CloudTrail", enabled: null, details: "Detection error" });
|
|
15363
|
+
}
|
|
15364
|
+
}
|
|
15365
|
+
try {
|
|
15366
|
+
const sh = createClient(SecurityHubClient, region);
|
|
15367
|
+
await sh.send(new DescribeHubCommand({}));
|
|
15368
|
+
services.push({
|
|
15369
|
+
name: "Security Hub",
|
|
15370
|
+
enabled: true,
|
|
15371
|
+
details: "Enabled with automated security checks"
|
|
15372
|
+
});
|
|
15373
|
+
} catch (err) {
|
|
15374
|
+
if (isAccessDenied(err)) {
|
|
15375
|
+
warnings.push("Security Hub: insufficient permissions to check status");
|
|
15376
|
+
services.push({ name: "Security Hub", enabled: null, details: "Access denied" });
|
|
15377
|
+
} else if (isNotEnabled(err)) {
|
|
15378
|
+
services.push({
|
|
15379
|
+
name: "Security Hub",
|
|
15380
|
+
enabled: false,
|
|
15381
|
+
recommendation: "Enable Security Hub for 300+ automated security checks",
|
|
15382
|
+
freeTrialAvailable: true
|
|
15383
|
+
});
|
|
15384
|
+
findings.push(
|
|
15385
|
+
makeFinding8({
|
|
15386
|
+
riskScore: 7.5,
|
|
15387
|
+
title: "AWS Security Hub is not enabled",
|
|
15388
|
+
resourceType: "AWS::SecurityHub::Hub",
|
|
15389
|
+
resourceId: "securityhub",
|
|
15390
|
+
resourceArn: `arn:${partition}:securityhub:${region}:${accountId}:hub/default`,
|
|
15391
|
+
region,
|
|
15392
|
+
description: "AWS Security Hub is not enabled in this region. Security Hub provides a comprehensive view of security alerts and compliance status.",
|
|
15393
|
+
impact: "Enables 300+ automated security checks across AWS services. Without it, security findings are fragmented across individual services.",
|
|
15394
|
+
remediationSteps: [
|
|
15395
|
+
"Open the AWS Security Hub console.",
|
|
15396
|
+
"Click 'Go to Security Hub' and enable it.",
|
|
15397
|
+
"Enable the AWS Foundational Security Best Practices standard.",
|
|
15398
|
+
"Security Hub offers a 30-day free trial."
|
|
15399
|
+
]
|
|
15400
|
+
})
|
|
15401
|
+
);
|
|
15402
|
+
} else {
|
|
15403
|
+
warnings.push(`Security Hub detection failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
15404
|
+
services.push({ name: "Security Hub", enabled: null, details: "Detection error" });
|
|
15405
|
+
}
|
|
15406
|
+
}
|
|
15407
|
+
try {
|
|
15408
|
+
const gd = createClient(GuardDutyClient, region);
|
|
15409
|
+
const resp = await gd.send(new ListDetectorsCommand({}));
|
|
15410
|
+
const detectors = resp.DetectorIds ?? [];
|
|
15411
|
+
if (detectors.length > 0) {
|
|
15412
|
+
services.push({
|
|
15413
|
+
name: "GuardDuty",
|
|
15414
|
+
enabled: true,
|
|
15415
|
+
details: `${detectors.length} detector(s) active`
|
|
15416
|
+
});
|
|
15417
|
+
} else {
|
|
15418
|
+
services.push({
|
|
15419
|
+
name: "GuardDuty",
|
|
15420
|
+
enabled: false,
|
|
15421
|
+
recommendation: "Enable GuardDuty for continuous threat detection",
|
|
15422
|
+
freeTrialAvailable: true
|
|
15423
|
+
});
|
|
15424
|
+
findings.push(
|
|
15425
|
+
makeFinding8({
|
|
15426
|
+
riskScore: 7.5,
|
|
15427
|
+
title: "Amazon GuardDuty is not enabled",
|
|
15428
|
+
resourceType: "AWS::GuardDuty::Detector",
|
|
15429
|
+
resourceId: "guardduty",
|
|
15430
|
+
resourceArn: `arn:${partition}:guardduty:${region}:${accountId}:detector/none`,
|
|
15431
|
+
region,
|
|
15432
|
+
description: "Amazon GuardDuty is not enabled in this region. GuardDuty provides intelligent threat detection by analyzing CloudTrail, VPC Flow Logs, and DNS logs.",
|
|
15433
|
+
impact: "Provides continuous threat detection for account compromise, instance compromise, and malicious reconnaissance. Without it, many attack patterns go undetected.",
|
|
15434
|
+
remediationSteps: [
|
|
15435
|
+
"Open the Amazon GuardDuty console.",
|
|
15436
|
+
"Click 'Get Started' and enable GuardDuty.",
|
|
15437
|
+
"GuardDuty offers a 30-day free trial.",
|
|
15438
|
+
"Consider enabling S3 protection and EKS protection add-ons."
|
|
15439
|
+
]
|
|
15440
|
+
})
|
|
15441
|
+
);
|
|
15442
|
+
}
|
|
15443
|
+
} catch (err) {
|
|
15444
|
+
if (isAccessDenied(err)) {
|
|
15445
|
+
warnings.push("GuardDuty: insufficient permissions to check status");
|
|
15446
|
+
services.push({ name: "GuardDuty", enabled: null, details: "Access denied" });
|
|
15447
|
+
} else if (isNotEnabled(err)) {
|
|
15448
|
+
services.push({
|
|
15449
|
+
name: "GuardDuty",
|
|
15450
|
+
enabled: false,
|
|
15451
|
+
recommendation: "Enable GuardDuty for continuous threat detection",
|
|
15452
|
+
freeTrialAvailable: true
|
|
15453
|
+
});
|
|
15454
|
+
findings.push(
|
|
15455
|
+
makeFinding8({
|
|
15456
|
+
riskScore: 7.5,
|
|
15457
|
+
title: "Amazon GuardDuty is not enabled",
|
|
15458
|
+
resourceType: "AWS::GuardDuty::Detector",
|
|
15459
|
+
resourceId: "guardduty",
|
|
15460
|
+
resourceArn: `arn:${partition}:guardduty:${region}:${accountId}:detector/none`,
|
|
15461
|
+
region,
|
|
15462
|
+
description: "Amazon GuardDuty is not enabled in this region. GuardDuty provides intelligent threat detection by analyzing CloudTrail, VPC Flow Logs, and DNS logs.",
|
|
15463
|
+
impact: "Provides continuous threat detection for account compromise, instance compromise, and malicious reconnaissance. Without it, many attack patterns go undetected.",
|
|
15464
|
+
remediationSteps: [
|
|
15465
|
+
"Open the Amazon GuardDuty console.",
|
|
15466
|
+
"Click 'Get Started' and enable GuardDuty.",
|
|
15467
|
+
"GuardDuty offers a 30-day free trial.",
|
|
15468
|
+
"Consider enabling S3 protection and EKS protection add-ons."
|
|
15469
|
+
]
|
|
15470
|
+
})
|
|
15471
|
+
);
|
|
15472
|
+
} else {
|
|
15473
|
+
warnings.push(`GuardDuty detection failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
15474
|
+
services.push({ name: "GuardDuty", enabled: null, details: "Detection error" });
|
|
15475
|
+
}
|
|
15476
|
+
}
|
|
15477
|
+
try {
|
|
15478
|
+
const insp = createClient(Inspector2Client, region);
|
|
15479
|
+
const resp = await insp.send(new BatchGetAccountStatusCommand({ accountIds: [accountId] }));
|
|
15480
|
+
const accounts = resp.accounts ?? [];
|
|
15481
|
+
const active = accounts.some(
|
|
15482
|
+
(a) => a.state?.status === "ENABLED" || a.state?.status === "ENABLING"
|
|
15483
|
+
);
|
|
15484
|
+
if (active) {
|
|
15485
|
+
services.push({
|
|
15486
|
+
name: "Inspector",
|
|
15487
|
+
enabled: true,
|
|
15488
|
+
details: "Vulnerability scanning active"
|
|
15489
|
+
});
|
|
15490
|
+
} else {
|
|
15491
|
+
services.push({
|
|
15492
|
+
name: "Inspector",
|
|
15493
|
+
enabled: false,
|
|
15494
|
+
recommendation: "Enable Inspector to scan for software vulnerabilities",
|
|
15495
|
+
freeTrialAvailable: true
|
|
15496
|
+
});
|
|
15497
|
+
findings.push(
|
|
15498
|
+
makeFinding8({
|
|
15499
|
+
riskScore: 6,
|
|
15500
|
+
title: "Amazon Inspector is not enabled",
|
|
15501
|
+
resourceType: "AWS::Inspector2::AccountStatus",
|
|
15502
|
+
resourceId: "inspector",
|
|
15503
|
+
resourceArn: `arn:${partition}:inspector2:${region}:${accountId}:account`,
|
|
15504
|
+
region,
|
|
15505
|
+
description: "Amazon Inspector is not enabled in this region. Inspector automatically discovers and scans EC2 instances, containers, and Lambda functions for software vulnerabilities.",
|
|
15506
|
+
impact: "Scans for software vulnerabilities in EC2 instances, container images, and Lambda functions. Without it, known CVEs may go undetected.",
|
|
15507
|
+
remediationSteps: [
|
|
15508
|
+
"Open the Amazon Inspector console.",
|
|
15509
|
+
"Click 'Get Started' and enable Inspector.",
|
|
15510
|
+
"Inspector offers a 15-day free trial.",
|
|
15511
|
+
"Enable scanning for EC2, ECR, and Lambda as appropriate."
|
|
15512
|
+
]
|
|
15513
|
+
})
|
|
15514
|
+
);
|
|
15515
|
+
}
|
|
15516
|
+
} catch (err) {
|
|
15517
|
+
if (isAccessDenied(err)) {
|
|
15518
|
+
warnings.push("Inspector: insufficient permissions to check status");
|
|
15519
|
+
services.push({ name: "Inspector", enabled: null, details: "Access denied" });
|
|
15520
|
+
} else if (isNotEnabled(err)) {
|
|
15521
|
+
services.push({
|
|
15522
|
+
name: "Inspector",
|
|
15523
|
+
enabled: false,
|
|
15524
|
+
recommendation: "Enable Inspector to scan for software vulnerabilities",
|
|
15525
|
+
freeTrialAvailable: true
|
|
15526
|
+
});
|
|
15527
|
+
findings.push(
|
|
15528
|
+
makeFinding8({
|
|
15529
|
+
riskScore: 6,
|
|
15530
|
+
title: "Amazon Inspector is not enabled",
|
|
15531
|
+
resourceType: "AWS::Inspector2::AccountStatus",
|
|
15532
|
+
resourceId: "inspector",
|
|
15533
|
+
resourceArn: `arn:${partition}:inspector2:${region}:${accountId}:account`,
|
|
15534
|
+
region,
|
|
15535
|
+
description: "Amazon Inspector is not enabled in this region. Inspector automatically discovers and scans EC2 instances, containers, and Lambda functions for software vulnerabilities.",
|
|
15536
|
+
impact: "Scans for software vulnerabilities in EC2 instances, container images, and Lambda functions. Without it, known CVEs may go undetected.",
|
|
15537
|
+
remediationSteps: [
|
|
15538
|
+
"Open the Amazon Inspector console.",
|
|
15539
|
+
"Click 'Get Started' and enable Inspector.",
|
|
15540
|
+
"Inspector offers a 15-day free trial.",
|
|
15541
|
+
"Enable scanning for EC2, ECR, and Lambda as appropriate."
|
|
15542
|
+
]
|
|
15543
|
+
})
|
|
15544
|
+
);
|
|
15545
|
+
} else {
|
|
15546
|
+
warnings.push(`Inspector detection failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
15547
|
+
services.push({ name: "Inspector", enabled: null, details: "Detection error" });
|
|
15548
|
+
}
|
|
15549
|
+
}
|
|
15550
|
+
try {
|
|
15551
|
+
const cfg = createClient(ConfigServiceClient, region);
|
|
15552
|
+
const resp = await cfg.send(new DescribeConfigurationRecordersCommand({}));
|
|
15553
|
+
const recorders = resp.ConfigurationRecorders ?? [];
|
|
15554
|
+
if (recorders.length > 0) {
|
|
15555
|
+
services.push({
|
|
15556
|
+
name: "AWS Config",
|
|
15557
|
+
enabled: true,
|
|
15558
|
+
details: `${recorders.length} recorder(s) configured`
|
|
15559
|
+
});
|
|
15560
|
+
} else {
|
|
15561
|
+
services.push({
|
|
15562
|
+
name: "AWS Config",
|
|
15563
|
+
enabled: false,
|
|
15564
|
+
recommendation: "Enable AWS Config to track configuration changes"
|
|
15565
|
+
});
|
|
15566
|
+
findings.push(
|
|
15567
|
+
makeFinding8({
|
|
15568
|
+
riskScore: 6,
|
|
15569
|
+
title: "AWS Config is not enabled",
|
|
15570
|
+
resourceType: "AWS::Config::ConfigurationRecorder",
|
|
15571
|
+
resourceId: "config",
|
|
15572
|
+
resourceArn: `arn:${partition}:config:${region}:${accountId}:configuration-recorder/none`,
|
|
15573
|
+
region,
|
|
15574
|
+
description: "AWS Config is not enabled in this region. Config continuously records resource configurations and enables compliance auditing.",
|
|
15575
|
+
impact: "Tracks configuration changes and enables compliance rules. Without it, configuration drift and non-compliant resources go undetected.",
|
|
15576
|
+
remediationSteps: [
|
|
15577
|
+
"Open the AWS Config console.",
|
|
15578
|
+
"Click 'Get Started' and configure a recorder.",
|
|
15579
|
+
"Select the resource types to record.",
|
|
15580
|
+
"Configure an S3 bucket for configuration snapshots."
|
|
15581
|
+
]
|
|
15582
|
+
})
|
|
15583
|
+
);
|
|
15584
|
+
}
|
|
15585
|
+
} catch (err) {
|
|
15586
|
+
if (isAccessDenied(err)) {
|
|
15587
|
+
warnings.push("AWS Config: insufficient permissions to check status");
|
|
15588
|
+
services.push({ name: "AWS Config", enabled: null, details: "Access denied" });
|
|
15589
|
+
} else if (isNotEnabled(err)) {
|
|
15590
|
+
services.push({
|
|
15591
|
+
name: "AWS Config",
|
|
15592
|
+
enabled: false,
|
|
15593
|
+
recommendation: "Enable AWS Config to track configuration changes"
|
|
15594
|
+
});
|
|
15595
|
+
findings.push(
|
|
15596
|
+
makeFinding8({
|
|
15597
|
+
riskScore: 6,
|
|
15598
|
+
title: "AWS Config is not enabled",
|
|
15599
|
+
resourceType: "AWS::Config::ConfigurationRecorder",
|
|
15600
|
+
resourceId: "config",
|
|
15601
|
+
resourceArn: `arn:${partition}:config:${region}:${accountId}:configuration-recorder/none`,
|
|
15602
|
+
region,
|
|
15603
|
+
description: "AWS Config is not enabled in this region. Config continuously records resource configurations and enables compliance auditing.",
|
|
15604
|
+
impact: "Tracks configuration changes and enables compliance rules. Without it, configuration drift and non-compliant resources go undetected.",
|
|
15605
|
+
remediationSteps: [
|
|
15606
|
+
"Open the AWS Config console.",
|
|
15607
|
+
"Click 'Get Started' and configure a recorder.",
|
|
15608
|
+
"Select the resource types to record.",
|
|
15609
|
+
"Configure an S3 bucket for configuration snapshots."
|
|
15610
|
+
]
|
|
15611
|
+
})
|
|
15612
|
+
);
|
|
15613
|
+
} else {
|
|
15614
|
+
warnings.push(`AWS Config detection failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
15615
|
+
services.push({ name: "AWS Config", enabled: null, details: "Detection error" });
|
|
15616
|
+
}
|
|
15617
|
+
}
|
|
15618
|
+
if (region.startsWith("cn-")) {
|
|
15619
|
+
services.push({ name: "Macie", enabled: null, details: "Not available in China regions" });
|
|
15620
|
+
warnings.push("Macie is not available in AWS China regions.");
|
|
15621
|
+
} else {
|
|
15622
|
+
try {
|
|
15623
|
+
const mc = createClient(Macie2Client, region);
|
|
15624
|
+
await mc.send(new GetMacieSessionCommand({}));
|
|
15625
|
+
services.push({
|
|
15626
|
+
name: "Macie",
|
|
15627
|
+
enabled: true,
|
|
15628
|
+
details: "Sensitive data detection active"
|
|
15629
|
+
});
|
|
15630
|
+
} catch (err) {
|
|
15631
|
+
if (isAccessDenied(err)) {
|
|
15632
|
+
warnings.push("Macie: insufficient permissions to check status");
|
|
15633
|
+
services.push({ name: "Macie", enabled: null, details: "Access denied" });
|
|
15634
|
+
} else if (isNotEnabled(err)) {
|
|
15635
|
+
services.push({
|
|
15636
|
+
name: "Macie",
|
|
15637
|
+
enabled: false,
|
|
15638
|
+
recommendation: "Enable Macie to detect sensitive data in S3",
|
|
15639
|
+
freeTrialAvailable: true
|
|
15640
|
+
});
|
|
15641
|
+
findings.push(
|
|
15642
|
+
makeFinding8({
|
|
15643
|
+
riskScore: 5,
|
|
15644
|
+
title: "Amazon Macie is not enabled",
|
|
15645
|
+
resourceType: "AWS::Macie::Session",
|
|
15646
|
+
resourceId: "macie",
|
|
15647
|
+
resourceArn: `arn:${partition}:macie2:${region}:${accountId}:session`,
|
|
15648
|
+
region,
|
|
15649
|
+
description: "Amazon Macie is not enabled in this region. Macie uses machine learning to discover and protect sensitive data stored in S3.",
|
|
15650
|
+
impact: "Detects sensitive data (PII, credentials, financial data) in S3 buckets. Without it, sensitive data exposure may go unnoticed.",
|
|
15651
|
+
remediationSteps: [
|
|
15652
|
+
"Open the Amazon Macie console.",
|
|
15653
|
+
"Click 'Get Started' and enable Macie.",
|
|
15654
|
+
"Macie offers a 30-day free trial for sensitive data discovery.",
|
|
15655
|
+
"Configure automated sensitive data discovery jobs."
|
|
15656
|
+
]
|
|
15657
|
+
})
|
|
15658
|
+
);
|
|
15659
|
+
} else {
|
|
15660
|
+
warnings.push(`Macie detection failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
15661
|
+
services.push({ name: "Macie", enabled: null, details: "Detection error" });
|
|
15662
|
+
}
|
|
15663
|
+
}
|
|
15664
|
+
}
|
|
15665
|
+
const knownServices = services.filter((s) => s.enabled !== null);
|
|
15666
|
+
const enabledCount = services.filter((s) => s.enabled === true).length;
|
|
15667
|
+
const coveragePercent = knownServices.length > 0 ? Math.round(enabledCount / knownServices.length * 100) : 0;
|
|
15668
|
+
const maturityLevel = computeMaturityLevel(enabledCount);
|
|
15669
|
+
const detectionResult = {
|
|
15670
|
+
services,
|
|
15671
|
+
coveragePercent,
|
|
15672
|
+
maturityLevel
|
|
15673
|
+
};
|
|
15674
|
+
return {
|
|
15675
|
+
module: this.moduleName,
|
|
15676
|
+
status: "success",
|
|
15677
|
+
warnings: warnings.length > 0 ? warnings : void 0,
|
|
15678
|
+
resourcesScanned: services.length,
|
|
15679
|
+
findingsCount: findings.length,
|
|
15680
|
+
scanTimeMs: Date.now() - startMs,
|
|
15681
|
+
findings,
|
|
15682
|
+
// Attach the structured detection result as a custom property via the findings metadata
|
|
15683
|
+
...{ serviceDetection: detectionResult }
|
|
15684
|
+
};
|
|
15685
|
+
}
|
|
15686
|
+
};
|
|
15687
|
+
|
|
15271
15688
|
// src/tools/report-tool.ts
|
|
15272
15689
|
var SEVERITY_ICON = {
|
|
15273
15690
|
CRITICAL: "\u{1F534}",
|
|
@@ -15494,6 +15911,23 @@ Reviews VPC network configuration.
|
|
|
15494
15911
|
- **Instances in default VPC** \u2014 Risk 7.0: Resources in the default VPC lack proper isolation.
|
|
15495
15912
|
- **Missing VPC Flow Logs** \u2014 Risk 7.0: No network traffic logging enabled.
|
|
15496
15913
|
- **Default SG with custom inbound rules** \u2014 Risk 5.5: Default security group modified with open rules.
|
|
15914
|
+
|
|
15915
|
+
## 8. Service Detection (service_detection)
|
|
15916
|
+
Detects which AWS security services are enabled and assesses overall security maturity.
|
|
15917
|
+
- **Security Hub not enabled** \u2014 Risk 7.5: Provides 300+ automated security checks.
|
|
15918
|
+
- **GuardDuty not enabled** \u2014 Risk 7.5: Provides continuous threat detection.
|
|
15919
|
+
- **Inspector not enabled** \u2014 Risk 6.0: Scans for software vulnerabilities.
|
|
15920
|
+
- **AWS Config not enabled** \u2014 Risk 6.0: Tracks configuration changes.
|
|
15921
|
+
- **Macie not enabled** \u2014 Risk 5.0: Detects sensitive data in S3 (not available in China regions).
|
|
15922
|
+
- CloudTrail detection is included for coverage metrics; findings handled by the CloudTrail module.
|
|
15923
|
+
|
|
15924
|
+
### Maturity Levels
|
|
15925
|
+
| Enabled Services | Level |
|
|
15926
|
+
|------------------|-------|
|
|
15927
|
+
| 0\u20131 | Basic |
|
|
15928
|
+
| 2\u20133 | Intermediate |
|
|
15929
|
+
| 4\u20135 | Advanced |
|
|
15930
|
+
| 6 | Comprehensive |
|
|
15497
15931
|
`;
|
|
15498
15932
|
var RISK_SCORING_CONTENT = `# Risk Scoring Model
|
|
15499
15933
|
|
|
@@ -15545,7 +15979,8 @@ var MODULE_DESCRIPTIONS = {
|
|
|
15545
15979
|
cloudtrail: "Validates CloudTrail logging configuration (multi-region, log validation, CloudWatch integration).",
|
|
15546
15980
|
rds: "Scans RDS instances for public accessibility, encryption, backups, and deletion protection.",
|
|
15547
15981
|
ebs: "Checks EBS volumes and snapshots for encryption and public sharing.",
|
|
15548
|
-
vpc: "Reviews VPC configuration including default VPC usage, flow logs, and default security groups."
|
|
15982
|
+
vpc: "Reviews VPC configuration including default VPC usage, flow logs, and default security groups.",
|
|
15983
|
+
service_detection: "Detects which AWS security services (Security Hub, GuardDuty, Inspector, Config, Macie) are enabled and assesses security maturity."
|
|
15549
15984
|
};
|
|
15550
15985
|
function summarizeResult(result) {
|
|
15551
15986
|
const { summary } = result;
|
|
@@ -15587,7 +16022,8 @@ function createServer(defaultRegion) {
|
|
|
15587
16022
|
new CloudTrailScanner(),
|
|
15588
16023
|
new RdsScanner(),
|
|
15589
16024
|
new EbsScanner(),
|
|
15590
|
-
new VpcScanner()
|
|
16025
|
+
new VpcScanner(),
|
|
16026
|
+
new ServiceDetectionScanner()
|
|
15591
16027
|
];
|
|
15592
16028
|
const scannerMap = /* @__PURE__ */ new Map();
|
|
15593
16029
|
for (const s of allScanners) {
|
|
@@ -15595,7 +16031,7 @@ function createServer(defaultRegion) {
|
|
|
15595
16031
|
}
|
|
15596
16032
|
server.tool(
|
|
15597
16033
|
"scan_all",
|
|
15598
|
-
"Run all
|
|
16034
|
+
"Run all 8 security scanners in parallel (including service detection). Read-only. Does not modify any AWS resources.",
|
|
15599
16035
|
{ region: external_exports.string().optional().describe("AWS region to scan (default: server region)") },
|
|
15600
16036
|
async ({ region }) => {
|
|
15601
16037
|
try {
|
|
@@ -15619,7 +16055,8 @@ function createServer(defaultRegion) {
|
|
|
15619
16055
|
{ toolName: "scan_cloudtrail", moduleName: "cloudtrail", label: "CloudTrail" },
|
|
15620
16056
|
{ toolName: "scan_rds", moduleName: "rds", label: "RDS" },
|
|
15621
16057
|
{ toolName: "scan_ebs", moduleName: "ebs", label: "EBS" },
|
|
15622
|
-
{ toolName: "scan_vpc", moduleName: "vpc", label: "VPC" }
|
|
16058
|
+
{ toolName: "scan_vpc", moduleName: "vpc", label: "VPC" },
|
|
16059
|
+
{ toolName: "detect_services", moduleName: "service_detection", label: "Security Service Detection" }
|
|
15623
16060
|
];
|
|
15624
16061
|
for (const { toolName, moduleName, label } of individualScanners) {
|
|
15625
16062
|
server.tool(
|
|
@@ -15658,6 +16095,121 @@ function createServer(defaultRegion) {
|
|
|
15658
16095
|
}
|
|
15659
16096
|
}
|
|
15660
16097
|
);
|
|
16098
|
+
server.tool(
|
|
16099
|
+
"generate_maturity_report",
|
|
16100
|
+
"Generate a security maturity assessment report from scan_all results. Requires service_detection module output. Read-only.",
|
|
16101
|
+
{ scan_results: external_exports.string().describe("JSON string of FullScanResult from scan_all") },
|
|
16102
|
+
async ({ scan_results }) => {
|
|
16103
|
+
try {
|
|
16104
|
+
const parsed = JSON.parse(scan_results);
|
|
16105
|
+
const sdModule = parsed.modules.find((m) => m.module === "service_detection");
|
|
16106
|
+
if (!sdModule) {
|
|
16107
|
+
return {
|
|
16108
|
+
content: [{ type: "text", text: "Error: scan results do not include service_detection module. Run scan_all first." }],
|
|
16109
|
+
isError: true
|
|
16110
|
+
};
|
|
16111
|
+
}
|
|
16112
|
+
const detection = sdModule.serviceDetection;
|
|
16113
|
+
if (!detection) {
|
|
16114
|
+
return {
|
|
16115
|
+
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." }],
|
|
16116
|
+
isError: true
|
|
16117
|
+
};
|
|
16118
|
+
}
|
|
16119
|
+
const serviceImpacts = {
|
|
16120
|
+
"CloudTrail": "API activity logging",
|
|
16121
|
+
"Security Hub": "+300 security checks",
|
|
16122
|
+
"GuardDuty": "Threat detection",
|
|
16123
|
+
"Inspector": "Vulnerability scanning",
|
|
16124
|
+
"AWS Config": "Configuration tracking",
|
|
16125
|
+
"Macie": "Sensitive data detection"
|
|
16126
|
+
};
|
|
16127
|
+
const serviceFreeTrials = {
|
|
16128
|
+
"Security Hub": true,
|
|
16129
|
+
"GuardDuty": true,
|
|
16130
|
+
"Inspector": true,
|
|
16131
|
+
"Macie": true
|
|
16132
|
+
};
|
|
16133
|
+
const services = detection.services;
|
|
16134
|
+
const coveragePercent = detection.coveragePercent;
|
|
16135
|
+
const maturityLevel = detection.maturityLevel;
|
|
16136
|
+
const enabledCount = services.filter((s) => s.enabled === true).length;
|
|
16137
|
+
const knownCount = services.filter((s) => s.enabled !== null).length;
|
|
16138
|
+
const totalServices = services.length;
|
|
16139
|
+
const lines = [];
|
|
16140
|
+
lines.push("# AWS Security Maturity Assessment");
|
|
16141
|
+
lines.push("");
|
|
16142
|
+
lines.push(`## Account: ${parsed.accountId} | Region: ${parsed.region}`);
|
|
16143
|
+
lines.push("");
|
|
16144
|
+
lines.push(`## Security Service Coverage: ${coveragePercent}%`);
|
|
16145
|
+
lines.push(`## Maturity Level: ${maturityLevel.charAt(0).toUpperCase() + maturityLevel.slice(1)}`);
|
|
16146
|
+
lines.push("");
|
|
16147
|
+
lines.push("### Service Status");
|
|
16148
|
+
lines.push("");
|
|
16149
|
+
lines.push("| Service | Status | Impact |");
|
|
16150
|
+
lines.push("|---------|--------|--------|");
|
|
16151
|
+
for (const svc of services) {
|
|
16152
|
+
const status = svc.enabled === true ? "\u2705 Enabled" : svc.enabled === false ? "\u274C Not Enabled" : "\u26A0\uFE0F Unknown";
|
|
16153
|
+
const impact = serviceImpacts[svc.name] ?? "";
|
|
16154
|
+
lines.push(`| ${svc.name} | ${status} | ${impact} |`);
|
|
16155
|
+
}
|
|
16156
|
+
const unknowns = services.filter((s) => s.enabled === null);
|
|
16157
|
+
if (unknowns.length > 0) {
|
|
16158
|
+
lines.push("");
|
|
16159
|
+
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.`);
|
|
16160
|
+
}
|
|
16161
|
+
const disabled = services.filter((s) => s.enabled === false);
|
|
16162
|
+
if (disabled.length > 0) {
|
|
16163
|
+
lines.push("");
|
|
16164
|
+
lines.push("### Recommendations (Priority Order)");
|
|
16165
|
+
lines.push("");
|
|
16166
|
+
const priorityOrder = ["Security Hub", "GuardDuty", "Inspector", "AWS Config", "Macie", "CloudTrail"];
|
|
16167
|
+
const sorted = disabled.sort(
|
|
16168
|
+
(a, b) => priorityOrder.indexOf(a.name) - priorityOrder.indexOf(b.name)
|
|
16169
|
+
);
|
|
16170
|
+
let idx = 1;
|
|
16171
|
+
for (const svc of sorted) {
|
|
16172
|
+
const trial = serviceFreeTrials[svc.name] ? " \u2014 free trial available" : "";
|
|
16173
|
+
lines.push(`${idx}. Enable ${svc.name}${trial}`);
|
|
16174
|
+
idx++;
|
|
16175
|
+
}
|
|
16176
|
+
}
|
|
16177
|
+
lines.push("");
|
|
16178
|
+
lines.push("### Maturity Roadmap");
|
|
16179
|
+
lines.push("");
|
|
16180
|
+
if (unknowns.length > 0) {
|
|
16181
|
+
lines.push(`- **Current**: ${maturityLevel.charAt(0).toUpperCase() + maturityLevel.slice(1)} (${enabledCount}/${knownCount} known services, ${unknowns.length} unknown)`);
|
|
16182
|
+
} else {
|
|
16183
|
+
lines.push(`- **Current**: ${maturityLevel.charAt(0).toUpperCase() + maturityLevel.slice(1)} (${enabledCount}/${totalServices} services)`);
|
|
16184
|
+
}
|
|
16185
|
+
if (maturityLevel !== "comprehensive") {
|
|
16186
|
+
const nextMilestones = {
|
|
16187
|
+
basic: { level: "Intermediate", target: 2, suggestions: ["Security Hub", "GuardDuty"] },
|
|
16188
|
+
intermediate: { level: "Advanced", target: 4, suggestions: ["Inspector", "AWS Config"] },
|
|
16189
|
+
advanced: { level: "Comprehensive", target: 6, suggestions: ["Macie"] }
|
|
16190
|
+
};
|
|
16191
|
+
const next = nextMilestones[maturityLevel];
|
|
16192
|
+
if (next) {
|
|
16193
|
+
const remaining = next.suggestions.filter(
|
|
16194
|
+
(s) => services.some((svc) => svc.name === s && !svc.enabled)
|
|
16195
|
+
);
|
|
16196
|
+
if (remaining.length > 0) {
|
|
16197
|
+
lines.push(`- **Next milestone**: ${next.level} (${next.target}/${knownCount}) \u2014 enable ${remaining.join(" + ")}`);
|
|
16198
|
+
}
|
|
16199
|
+
}
|
|
16200
|
+
lines.push(`- **Target**: Comprehensive (${knownCount}/${knownCount})`);
|
|
16201
|
+
}
|
|
16202
|
+
lines.push("");
|
|
16203
|
+
const report = lines.join("\n");
|
|
16204
|
+
return { content: [{ type: "text", text: report }] };
|
|
16205
|
+
} catch (err) {
|
|
16206
|
+
return {
|
|
16207
|
+
content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
|
|
16208
|
+
isError: true
|
|
16209
|
+
};
|
|
16210
|
+
}
|
|
16211
|
+
}
|
|
16212
|
+
);
|
|
15661
16213
|
server.tool(
|
|
15662
16214
|
"save_results",
|
|
15663
16215
|
"Saves scan results to local disk or S3 for dashboard display. Does not modify any AWS resources.",
|