aws-security-mcp 0.4.3 → 0.5.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/README.md +18 -9
- package/dist/bin/aws-security-mcp.js +350 -16
- package/dist/bin/aws-security-mcp.js.map +1 -1
- package/dist/src/index.js +350 -16
- package/dist/src/index.js.map +1 -1
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# aws-security-mcp
|
|
2
2
|
|
|
3
|
-
MCP server for automated AWS security scanning —
|
|
3
|
+
MCP server for automated AWS security scanning — 19 modules, risk scoring, zero write operations.
|
|
4
4
|
|
|
5
5
|
<!-- badges -->
|
|
6
6
|

|
|
@@ -9,7 +9,7 @@ MCP server for automated AWS security scanning — 17 modules, risk scoring, zer
|
|
|
9
9
|
|
|
10
10
|
## Features
|
|
11
11
|
|
|
12
|
-
- **
|
|
12
|
+
- **19 Security Scan Modules** — 15 unique scanners + 4 aggregation scanners (Security Hub, GuardDuty, Inspector, Trusted Advisor, Config Rules, Access Analyzer, Patch Compliance)
|
|
13
13
|
- **Risk Scoring** — every finding scored 0-10 with severity (CRITICAL/HIGH/MEDIUM/LOW) and priority (P0-P3)
|
|
14
14
|
- **100% Read-Only** — uses only Describe/Get/List API calls; never modifies your AWS resources
|
|
15
15
|
- **Multi-Account Support** — scan all accounts in an AWS Organization via `org_mode` with cross-account role assumption
|
|
@@ -113,7 +113,7 @@ For multi-account scanning across an AWS Organization:
|
|
|
113
113
|
|
|
114
114
|
| Tool | Description |
|
|
115
115
|
|------|-------------|
|
|
116
|
-
| `scan_all` | Run all
|
|
116
|
+
| `scan_all` | Run all 19 security scanners in parallel (supports org_mode) |
|
|
117
117
|
| `detect_services` | Detect enabled AWS security services and assess maturity |
|
|
118
118
|
| `scan_secret_exposure` | Check Lambda env vars and EC2 userData for exposed secrets |
|
|
119
119
|
| `scan_ssl_certificate` | Check ACM certificates for expiry and failed status |
|
|
@@ -131,6 +131,8 @@ For multi-account scanning across an AWS Organization:
|
|
|
131
131
|
| `scan_config_rules_findings` | Aggregate findings from AWS Config Rules |
|
|
132
132
|
| `scan_access_analyzer_findings` | Aggregate findings from IAM Access Analyzer |
|
|
133
133
|
| `scan_patch_compliance_findings` | Aggregate findings from SSM Patch Compliance |
|
|
134
|
+
| `scan_imdsv2_enforcement` | Check EC2 instances for IMDSv2 enforcement |
|
|
135
|
+
| `scan_waf_coverage` | Check internet-facing ALBs for WAF Web ACL protection |
|
|
134
136
|
| `scan_group` | Run a predefined group of scanners for a specific scenario |
|
|
135
137
|
| `list_groups` | List available scan groups |
|
|
136
138
|
| `list_modules` | List available scan modules with descriptions |
|
|
@@ -167,6 +169,8 @@ Attach this policy to the IAM user or role running the scanner. All actions are
|
|
|
167
169
|
"config:DescribeConfigurationRecorders",
|
|
168
170
|
"config:GetComplianceDetailsByConfigRule",
|
|
169
171
|
|
|
172
|
+
"elasticloadbalancing:DescribeLoadBalancers",
|
|
173
|
+
|
|
170
174
|
"ec2:DescribeAddresses",
|
|
171
175
|
"ec2:DescribeInstanceAttribute",
|
|
172
176
|
"ec2:DescribeInstances",
|
|
@@ -229,7 +233,10 @@ Attach this policy to the IAM user or role running the scanner. All actions are
|
|
|
229
233
|
"sts:GetCallerIdentity",
|
|
230
234
|
|
|
231
235
|
"support:DescribeTrustedAdvisorChecks",
|
|
232
|
-
"support:DescribeTrustedAdvisorCheckResult"
|
|
236
|
+
"support:DescribeTrustedAdvisorCheckResult",
|
|
237
|
+
|
|
238
|
+
"wafv2:GetWebACL",
|
|
239
|
+
"wafv2:GetWebACLForResource"
|
|
233
240
|
],
|
|
234
241
|
"Resource": "*"
|
|
235
242
|
}
|
|
@@ -239,7 +246,7 @@ Attach this policy to the IAM user or role running the scanner. All actions are
|
|
|
239
246
|
|
|
240
247
|
## Scan Modules
|
|
241
248
|
|
|
242
|
-
### Unique Scanners (
|
|
249
|
+
### Unique Scanners (15)
|
|
243
250
|
|
|
244
251
|
| Module | What It Checks | Risk Score Range |
|
|
245
252
|
|--------|---------------|-----------------|
|
|
@@ -256,6 +263,8 @@ Attach this policy to the IAM user or role running the scanner. All actions are
|
|
|
256
263
|
| **Config Rules** | AWS Config Rules compliance status | 3.0 - 9.5 |
|
|
257
264
|
| **Access Analyzer** | IAM Access Analyzer external access findings | 3.0 - 9.5 |
|
|
258
265
|
| **Patch Compliance** | SSM Patch Manager compliance status for managed instances | 3.0 - 9.5 |
|
|
266
|
+
| **IMDSv2 Enforcement** | EC2 instances not enforcing IMDSv2 (HttpTokens != required) | 7.5 |
|
|
267
|
+
| **WAF Coverage** | Internet-facing ALBs without WAF Web ACL protection | 7.5 |
|
|
259
268
|
|
|
260
269
|
### Aggregation Scanners (4)
|
|
261
270
|
|
|
@@ -281,16 +290,16 @@ Pre-defined scanner groupings for common scenarios:
|
|
|
281
290
|
|
|
282
291
|
| Group | Description | Modules |
|
|
283
292
|
|-------|-------------|---------|
|
|
284
|
-
| `mlps3_precheck` | GB/T 22239-2019 等保三级预检 |
|
|
285
|
-
| `hw_defense` | 护网蓝队加固 |
|
|
286
|
-
| `exposure` | 公网暴露面评估 |
|
|
293
|
+
| `mlps3_precheck` | GB/T 22239-2019 等保三级预检 | 17 modules |
|
|
294
|
+
| `hw_defense` | 护网蓝队加固 | 14 modules |
|
|
295
|
+
| `exposure` | 公网暴露面评估 | 8 modules |
|
|
287
296
|
| `data_encryption` | 数据加密审计 | 2 modules |
|
|
288
297
|
| `least_privilege` | 最小权限审计 | 3 modules |
|
|
289
298
|
| `log_integrity` | 日志完整性审计 | 2 modules |
|
|
290
299
|
| `disaster_recovery` | 灾备评估 | 2 modules |
|
|
291
300
|
| `idle_resources` | 闲置资源清理 | 2 modules |
|
|
292
301
|
| `tag_compliance` | 资源标签合规 | 1 module |
|
|
293
|
-
| `new_account_baseline` | 新账户基线检查 |
|
|
302
|
+
| `new_account_baseline` | 新账户基线检查 | 7 modules |
|
|
294
303
|
| `aggregation` | 安全服务聚合 | 7 modules |
|
|
295
304
|
|
|
296
305
|
Use `list_groups` to see all available groups with their module lists.
|
|
@@ -237,7 +237,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
237
237
|
import { z } from "zod";
|
|
238
238
|
|
|
239
239
|
// src/version.ts
|
|
240
|
-
var VERSION = "0.
|
|
240
|
+
var VERSION = "0.5.0";
|
|
241
241
|
|
|
242
242
|
// src/utils/aws-client.ts
|
|
243
243
|
import { STSClient, GetCallerIdentityCommand } from "@aws-sdk/client-sts";
|
|
@@ -3961,6 +3961,242 @@ var PatchComplianceFindingsScanner = class {
|
|
|
3961
3961
|
}
|
|
3962
3962
|
};
|
|
3963
3963
|
|
|
3964
|
+
// src/scanners/imdsv2-enforcement.ts
|
|
3965
|
+
import {
|
|
3966
|
+
EC2Client as EC2Client6,
|
|
3967
|
+
DescribeInstancesCommand as DescribeInstancesCommand5
|
|
3968
|
+
} from "@aws-sdk/client-ec2";
|
|
3969
|
+
function makeFinding11(opts) {
|
|
3970
|
+
const severity = severityFromScore(opts.riskScore);
|
|
3971
|
+
return { ...opts, severity, priority: priorityFromSeverity(severity) };
|
|
3972
|
+
}
|
|
3973
|
+
var Imdsv2EnforcementScanner = class {
|
|
3974
|
+
moduleName = "imdsv2_enforcement";
|
|
3975
|
+
async scan(ctx) {
|
|
3976
|
+
const { region, partition, accountId } = ctx;
|
|
3977
|
+
const startMs = Date.now();
|
|
3978
|
+
const findings = [];
|
|
3979
|
+
const warnings = [];
|
|
3980
|
+
try {
|
|
3981
|
+
const client = createClient(EC2Client6, region, ctx.credentials);
|
|
3982
|
+
const instances = [];
|
|
3983
|
+
let nextToken;
|
|
3984
|
+
do {
|
|
3985
|
+
const resp = await client.send(
|
|
3986
|
+
new DescribeInstancesCommand5({
|
|
3987
|
+
Filters: [{ Name: "instance-state-name", Values: ["running"] }],
|
|
3988
|
+
NextToken: nextToken
|
|
3989
|
+
})
|
|
3990
|
+
);
|
|
3991
|
+
if (resp.Reservations) {
|
|
3992
|
+
for (const reservation of resp.Reservations) {
|
|
3993
|
+
if (reservation.Instances) {
|
|
3994
|
+
instances.push(...reservation.Instances);
|
|
3995
|
+
}
|
|
3996
|
+
}
|
|
3997
|
+
}
|
|
3998
|
+
nextToken = resp.NextToken;
|
|
3999
|
+
} while (nextToken);
|
|
4000
|
+
for (const instance of instances) {
|
|
4001
|
+
const instanceId = instance.InstanceId ?? "unknown";
|
|
4002
|
+
const instanceType = instance.InstanceType ?? "unknown";
|
|
4003
|
+
const state = instance.State?.Name ?? "unknown";
|
|
4004
|
+
const httpTokens = instance.MetadataOptions?.HttpTokens ?? "unknown";
|
|
4005
|
+
const hopLimit = instance.MetadataOptions?.HttpPutResponseHopLimit ?? 1;
|
|
4006
|
+
const instanceArn = `arn:${partition}:ec2:${region}:${accountId}:instance/${instanceId}`;
|
|
4007
|
+
if (httpTokens !== "required") {
|
|
4008
|
+
const description = [
|
|
4009
|
+
`EC2 instance ${instanceId} (type: ${instanceType}, state: ${state}) has HttpTokens set to "${httpTokens}".`,
|
|
4010
|
+
`IMDSv1 is accessible, allowing unauthenticated metadata requests.`
|
|
4011
|
+
];
|
|
4012
|
+
if (hopLimit > 1) {
|
|
4013
|
+
description.push(`HttpPutResponseHopLimit is ${hopLimit} (>1), which may allow containers to reach IMDS.`);
|
|
4014
|
+
}
|
|
4015
|
+
findings.push(
|
|
4016
|
+
makeFinding11({
|
|
4017
|
+
riskScore: 7.5,
|
|
4018
|
+
title: `EC2 instance ${instanceId} does not enforce IMDSv2`,
|
|
4019
|
+
resourceType: "AWS::EC2::Instance",
|
|
4020
|
+
resourceId: instanceId,
|
|
4021
|
+
resourceArn: instanceArn,
|
|
4022
|
+
region,
|
|
4023
|
+
description: description.join(" "),
|
|
4024
|
+
impact: "IMDSv1 allows attackers to steal IAM role credentials via SSRF attacks",
|
|
4025
|
+
remediationSteps: [
|
|
4026
|
+
"Enforce IMDSv2 by setting HttpTokens to 'required'.",
|
|
4027
|
+
"Run: aws ec2 modify-instance-metadata-options --instance-id " + instanceId + " --http-tokens required --http-endpoint enabled",
|
|
4028
|
+
"Set HttpPutResponseHopLimit to 1 unless running containers that need metadata access.",
|
|
4029
|
+
"Update launch templates and Auto Scaling groups to enforce IMDSv2 for new instances."
|
|
4030
|
+
]
|
|
4031
|
+
})
|
|
4032
|
+
);
|
|
4033
|
+
} else if (hopLimit > 1) {
|
|
4034
|
+
warnings.push(
|
|
4035
|
+
`Instance ${instanceId} enforces IMDSv2 but HttpPutResponseHopLimit is ${hopLimit} (>1). Verify this is intentional for containerized workloads.`
|
|
4036
|
+
);
|
|
4037
|
+
}
|
|
4038
|
+
}
|
|
4039
|
+
return {
|
|
4040
|
+
module: this.moduleName,
|
|
4041
|
+
status: "success",
|
|
4042
|
+
warnings: warnings.length > 0 ? warnings : void 0,
|
|
4043
|
+
resourcesScanned: instances.length,
|
|
4044
|
+
findingsCount: findings.length,
|
|
4045
|
+
scanTimeMs: Date.now() - startMs,
|
|
4046
|
+
findings
|
|
4047
|
+
};
|
|
4048
|
+
} catch (err) {
|
|
4049
|
+
return {
|
|
4050
|
+
module: this.moduleName,
|
|
4051
|
+
status: "error",
|
|
4052
|
+
error: err instanceof Error ? err.message : String(err),
|
|
4053
|
+
warnings: warnings.length > 0 ? warnings : void 0,
|
|
4054
|
+
resourcesScanned: 0,
|
|
4055
|
+
findingsCount: 0,
|
|
4056
|
+
scanTimeMs: Date.now() - startMs,
|
|
4057
|
+
findings: []
|
|
4058
|
+
};
|
|
4059
|
+
}
|
|
4060
|
+
}
|
|
4061
|
+
};
|
|
4062
|
+
|
|
4063
|
+
// src/scanners/waf-coverage.ts
|
|
4064
|
+
import {
|
|
4065
|
+
ElasticLoadBalancingV2Client,
|
|
4066
|
+
DescribeLoadBalancersCommand
|
|
4067
|
+
} from "@aws-sdk/client-elastic-load-balancing-v2";
|
|
4068
|
+
import {
|
|
4069
|
+
WAFV2Client,
|
|
4070
|
+
GetWebACLForResourceCommand
|
|
4071
|
+
} from "@aws-sdk/client-wafv2";
|
|
4072
|
+
function makeFinding12(opts) {
|
|
4073
|
+
const severity = severityFromScore(opts.riskScore);
|
|
4074
|
+
return { ...opts, severity, priority: priorityFromSeverity(severity) };
|
|
4075
|
+
}
|
|
4076
|
+
var WafCoverageScanner = class {
|
|
4077
|
+
moduleName = "waf_coverage";
|
|
4078
|
+
async scan(ctx) {
|
|
4079
|
+
const { region } = ctx;
|
|
4080
|
+
const startMs = Date.now();
|
|
4081
|
+
const findings = [];
|
|
4082
|
+
const warnings = [];
|
|
4083
|
+
try {
|
|
4084
|
+
const elbClient = createClient(ElasticLoadBalancingV2Client, region, ctx.credentials);
|
|
4085
|
+
const wafClient = createClient(WAFV2Client, region, ctx.credentials);
|
|
4086
|
+
const loadBalancers = [];
|
|
4087
|
+
let marker;
|
|
4088
|
+
do {
|
|
4089
|
+
const resp = await elbClient.send(
|
|
4090
|
+
new DescribeLoadBalancersCommand({ Marker: marker })
|
|
4091
|
+
);
|
|
4092
|
+
if (resp.LoadBalancers) {
|
|
4093
|
+
loadBalancers.push(...resp.LoadBalancers);
|
|
4094
|
+
}
|
|
4095
|
+
marker = resp.NextMarker;
|
|
4096
|
+
} while (marker);
|
|
4097
|
+
const internetFacing = loadBalancers.filter((lb) => lb.Scheme === "internet-facing");
|
|
4098
|
+
for (const lb of internetFacing) {
|
|
4099
|
+
const lbName = lb.LoadBalancerName ?? "unknown";
|
|
4100
|
+
const lbArn = lb.LoadBalancerArn ?? "unknown";
|
|
4101
|
+
const lbType = lb.Type ?? "unknown";
|
|
4102
|
+
if (lbType !== "application") {
|
|
4103
|
+
warnings.push(
|
|
4104
|
+
`Skipping ${lbType} load balancer "${lbName}" \u2014 WAF Web ACL association is only supported for ALBs.`
|
|
4105
|
+
);
|
|
4106
|
+
continue;
|
|
4107
|
+
}
|
|
4108
|
+
try {
|
|
4109
|
+
const wafResp = await wafClient.send(
|
|
4110
|
+
new GetWebACLForResourceCommand({ ResourceArn: lbArn })
|
|
4111
|
+
);
|
|
4112
|
+
if (!wafResp.WebACL) {
|
|
4113
|
+
findings.push(
|
|
4114
|
+
makeFinding12({
|
|
4115
|
+
riskScore: 7.5,
|
|
4116
|
+
title: `Internet-facing ALB ${lbName} has no WAF protection`,
|
|
4117
|
+
resourceType: "AWS::ElasticLoadBalancingV2::LoadBalancer",
|
|
4118
|
+
resourceId: lbName,
|
|
4119
|
+
resourceArn: lbArn,
|
|
4120
|
+
region,
|
|
4121
|
+
description: `Internet-facing Application Load Balancer "${lbName}" does not have a WAF Web ACL associated. Traffic is not inspected for common web exploits.`,
|
|
4122
|
+
impact: "Without WAF, the ALB is exposed to SQL injection, XSS, and other OWASP Top 10 attacks",
|
|
4123
|
+
remediationSteps: [
|
|
4124
|
+
"Create a WAFv2 Web ACL with managed rule groups (e.g., AWSManagedRulesCommonRuleSet).",
|
|
4125
|
+
"Associate the Web ACL with the ALB using the REGIONAL scope.",
|
|
4126
|
+
"Enable WAF logging for visibility into blocked requests.",
|
|
4127
|
+
"Consider adding rate-based rules to mitigate DDoS at the application layer."
|
|
4128
|
+
]
|
|
4129
|
+
})
|
|
4130
|
+
);
|
|
4131
|
+
}
|
|
4132
|
+
} catch (wafErr) {
|
|
4133
|
+
const errMsg = wafErr instanceof Error ? wafErr.message : String(wafErr);
|
|
4134
|
+
const errName = wafErr instanceof Error ? wafErr.name ?? "" : "";
|
|
4135
|
+
if (errName === "WAFNonexistentItemException") {
|
|
4136
|
+
findings.push(
|
|
4137
|
+
makeFinding12({
|
|
4138
|
+
riskScore: 7.5,
|
|
4139
|
+
title: `Internet-facing ALB ${lbName} has no WAF protection`,
|
|
4140
|
+
resourceType: "AWS::ElasticLoadBalancingV2::LoadBalancer",
|
|
4141
|
+
resourceId: lbName,
|
|
4142
|
+
resourceArn: lbArn,
|
|
4143
|
+
region,
|
|
4144
|
+
description: `Internet-facing Application Load Balancer "${lbName}" does not have a WAF Web ACL associated. Traffic is not inspected for common web exploits.`,
|
|
4145
|
+
impact: "Without WAF, the ALB is exposed to SQL injection, XSS, and other OWASP Top 10 attacks",
|
|
4146
|
+
remediationSteps: [
|
|
4147
|
+
"Create a WAFv2 Web ACL with managed rule groups (e.g., AWSManagedRulesCommonRuleSet).",
|
|
4148
|
+
"Associate the Web ACL with the ALB using the REGIONAL scope.",
|
|
4149
|
+
"Enable WAF logging for visibility into blocked requests.",
|
|
4150
|
+
"Consider adding rate-based rules to mitigate DDoS at the application layer."
|
|
4151
|
+
]
|
|
4152
|
+
})
|
|
4153
|
+
);
|
|
4154
|
+
} else if (errName === "AccessDeniedException" || errName === "WAFInvalidParameterException") {
|
|
4155
|
+
warnings.push(
|
|
4156
|
+
`Could not check WAF for ALB "${lbName}": ${errMsg}. Ensure wafv2:GetWebACLForResource permission is granted.`
|
|
4157
|
+
);
|
|
4158
|
+
} else {
|
|
4159
|
+
warnings.push(`Error checking WAF for ALB "${lbName}": ${errMsg}`);
|
|
4160
|
+
}
|
|
4161
|
+
}
|
|
4162
|
+
}
|
|
4163
|
+
return {
|
|
4164
|
+
module: this.moduleName,
|
|
4165
|
+
status: "success",
|
|
4166
|
+
warnings: warnings.length > 0 ? warnings : void 0,
|
|
4167
|
+
resourcesScanned: internetFacing.length,
|
|
4168
|
+
findingsCount: findings.length,
|
|
4169
|
+
scanTimeMs: Date.now() - startMs,
|
|
4170
|
+
findings
|
|
4171
|
+
};
|
|
4172
|
+
} catch (err) {
|
|
4173
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
4174
|
+
const errName = err instanceof Error ? err.name ?? "" : "";
|
|
4175
|
+
if (errName === "AccessDeniedException" || errName === "UnrecognizedClientException") {
|
|
4176
|
+
return {
|
|
4177
|
+
module: this.moduleName,
|
|
4178
|
+
status: "success",
|
|
4179
|
+
warnings: [`WAF coverage check skipped: ${errMsg}`],
|
|
4180
|
+
resourcesScanned: 0,
|
|
4181
|
+
findingsCount: 0,
|
|
4182
|
+
scanTimeMs: Date.now() - startMs,
|
|
4183
|
+
findings: []
|
|
4184
|
+
};
|
|
4185
|
+
}
|
|
4186
|
+
return {
|
|
4187
|
+
module: this.moduleName,
|
|
4188
|
+
status: "error",
|
|
4189
|
+
error: errMsg,
|
|
4190
|
+
warnings: warnings.length > 0 ? warnings : void 0,
|
|
4191
|
+
resourcesScanned: 0,
|
|
4192
|
+
findingsCount: 0,
|
|
4193
|
+
scanTimeMs: Date.now() - startMs,
|
|
4194
|
+
findings: []
|
|
4195
|
+
};
|
|
4196
|
+
}
|
|
4197
|
+
}
|
|
4198
|
+
};
|
|
4199
|
+
|
|
3964
4200
|
// src/tools/report-tool.ts
|
|
3965
4201
|
var SEVERITY_ICON = {
|
|
3966
4202
|
CRITICAL: "\u{1F534}",
|
|
@@ -5192,13 +5428,13 @@ var SCAN_GROUPS = {
|
|
|
5192
5428
|
mlps3_precheck: {
|
|
5193
5429
|
name: "\u7B49\u4FDD\u4E09\u7EA7\u9884\u68C0",
|
|
5194
5430
|
description: "GB/T 22239-2019 \u7B49\u4FDD\u4E09\u7EA7 AWS \u4E91\u79DF\u6237\u5C42\u914D\u7F6E\u68C0\u67E5",
|
|
5195
|
-
modules: ["service_detection", "secret_exposure", "ssl_certificate", "dns_dangling", "network_reachability", "iam_privilege_escalation", "tag_compliance", "disaster_recovery", "security_hub_findings", "guardduty_findings", "inspector_findings", "trusted_advisor_findings", "config_rules_findings", "access_analyzer_findings", "patch_compliance_findings"],
|
|
5431
|
+
modules: ["service_detection", "secret_exposure", "ssl_certificate", "dns_dangling", "network_reachability", "iam_privilege_escalation", "tag_compliance", "disaster_recovery", "security_hub_findings", "guardduty_findings", "inspector_findings", "trusted_advisor_findings", "config_rules_findings", "access_analyzer_findings", "patch_compliance_findings", "imdsv2_enforcement", "waf_coverage"],
|
|
5196
5432
|
reportType: "mlps3"
|
|
5197
5433
|
},
|
|
5198
5434
|
hw_defense: {
|
|
5199
5435
|
name: "\u62A4\u7F51\u84DD\u961F\u52A0\u56FA",
|
|
5200
5436
|
description: "\u62A4\u7F51\u524D\u5B89\u5168\u81EA\u67E5 \u2014 \u653B\u51FB\u9762+\u5F31\u70B9\u8BC4\u4F30",
|
|
5201
|
-
modules: ["service_detection", "secret_exposure", "network_reachability", "dns_dangling", "ssl_certificate", "iam_privilege_escalation", "security_hub_findings", "guardduty_findings", "inspector_findings", "config_rules_findings", "access_analyzer_findings", "patch_compliance_findings"],
|
|
5437
|
+
modules: ["service_detection", "secret_exposure", "network_reachability", "dns_dangling", "ssl_certificate", "iam_privilege_escalation", "security_hub_findings", "guardduty_findings", "inspector_findings", "config_rules_findings", "access_analyzer_findings", "patch_compliance_findings", "imdsv2_enforcement", "waf_coverage"],
|
|
5202
5438
|
findingsFilter: {
|
|
5203
5439
|
guardDutyTypes: ["Backdoor", "Trojan", "PenTest", "CryptoCurrency"],
|
|
5204
5440
|
minSeverity: "MEDIUM"
|
|
@@ -5207,7 +5443,7 @@ var SCAN_GROUPS = {
|
|
|
5207
5443
|
exposure: {
|
|
5208
5444
|
name: "\u516C\u7F51\u66B4\u9732\u9762\u8BC4\u4F30",
|
|
5209
5445
|
description: "\u8BC4\u4F30\u516C\u7F51\u53EF\u8FBE\u7684\u8D44\u6E90\u548C\u7AEF\u53E3",
|
|
5210
|
-
modules: ["network_reachability", "dns_dangling", "public_access_verify", "ssl_certificate", "security_hub_findings", "access_analyzer_findings"],
|
|
5446
|
+
modules: ["network_reachability", "dns_dangling", "public_access_verify", "ssl_certificate", "security_hub_findings", "access_analyzer_findings", "imdsv2_enforcement", "waf_coverage"],
|
|
5211
5447
|
findingsFilter: {
|
|
5212
5448
|
securityHubCategories: ["network", "public", "exposure", "port"]
|
|
5213
5449
|
}
|
|
@@ -5254,7 +5490,7 @@ var SCAN_GROUPS = {
|
|
|
5254
5490
|
new_account_baseline: {
|
|
5255
5491
|
name: "\u65B0\u8D26\u6237\u57FA\u7EBF\u68C0\u67E5",
|
|
5256
5492
|
description: "\u65B0 AWS \u8D26\u6237\u5B89\u5168\u57FA\u7EBF",
|
|
5257
|
-
modules: ["service_detection", "secret_exposure", "iam_privilege_escalation", "security_hub_findings", "guardduty_findings", "access_analyzer_findings"]
|
|
5493
|
+
modules: ["service_detection", "secret_exposure", "iam_privilege_escalation", "security_hub_findings", "guardduty_findings", "access_analyzer_findings", "imdsv2_enforcement"]
|
|
5258
5494
|
},
|
|
5259
5495
|
aggregation: {
|
|
5260
5496
|
name: "\u5B89\u5168\u670D\u52A1\u805A\u5408",
|
|
@@ -5264,7 +5500,7 @@ var SCAN_GROUPS = {
|
|
|
5264
5500
|
};
|
|
5265
5501
|
|
|
5266
5502
|
// src/resources/index.ts
|
|
5267
|
-
var SECURITY_RULES_CONTENT = `# AWS Security Scan Modules & Rules (
|
|
5503
|
+
var SECURITY_RULES_CONTENT = `# AWS Security Scan Modules & Rules (19 modules)
|
|
5268
5504
|
|
|
5269
5505
|
## 1. Service Detection (service_detection)
|
|
5270
5506
|
Detects which AWS security services are enabled and assesses overall security maturity.
|
|
@@ -5358,6 +5594,21 @@ Checks patch compliance status for SSM-managed instances.
|
|
|
5358
5594
|
- Missing non-security patches \u2192 MEDIUM (5.5).
|
|
5359
5595
|
- Instances without patch data flagged as LOW (3.0) for visibility.
|
|
5360
5596
|
- Includes platform info, missing/failed counts, and last scan time.
|
|
5597
|
+
|
|
5598
|
+
## 18. IMDSv2 Enforcement (imdsv2_enforcement)
|
|
5599
|
+
Checks if EC2 instances enforce IMDSv2 (Instance Metadata Service v2).
|
|
5600
|
+
- Lists all running EC2 instances and checks MetadataOptions.HttpTokens.
|
|
5601
|
+
- **HttpTokens != "required"** \u2014 Risk 7.5: IMDSv1 allows credential theft via SSRF attacks.
|
|
5602
|
+
- Also checks HttpPutResponseHopLimit \u2014 values >1 on containerized workloads noted as warning.
|
|
5603
|
+
- Remediation: Set HttpTokens to "required" via modify-instance-metadata-options.
|
|
5604
|
+
|
|
5605
|
+
## 19. WAF Coverage (waf_coverage)
|
|
5606
|
+
Checks if internet-facing ALBs have WAF Web ACL associated for protection.
|
|
5607
|
+
- Lists all ELBv2 load balancers, filters to internet-facing only.
|
|
5608
|
+
- For each internet-facing ALB, checks WAFv2 Web ACL association.
|
|
5609
|
+
- **No WAF Web ACL** \u2014 Risk 7.5: ALB exposed to SQL injection, XSS, and OWASP Top 10 attacks.
|
|
5610
|
+
- NLBs (L4) are skipped as WAF does not apply \u2014 noted in warnings.
|
|
5611
|
+
- Gracefully handles WAFv2 access denied or unavailable regions.
|
|
5361
5612
|
`;
|
|
5362
5613
|
var RISK_SCORING_CONTENT = `# Risk Scoring Model
|
|
5363
5614
|
|
|
@@ -5422,8 +5673,63 @@ var MODULE_DESCRIPTIONS = {
|
|
|
5422
5673
|
trusted_advisor_findings: "Aggregates security checks from AWS Trusted Advisor \u2014 requires Business or Enterprise Support plan.",
|
|
5423
5674
|
config_rules_findings: "Pulls non-compliant AWS Config Rule evaluation results \u2014 configuration compliance violations across all resource types.",
|
|
5424
5675
|
access_analyzer_findings: "Pulls active IAM Access Analyzer findings \u2014 resources accessible from outside the account (external principals, public access).",
|
|
5425
|
-
patch_compliance_findings: "Checks SSM Patch Manager compliance \u2014 managed instances with missing or failed security and system patches."
|
|
5676
|
+
patch_compliance_findings: "Checks SSM Patch Manager compliance \u2014 managed instances with missing or failed security and system patches.",
|
|
5677
|
+
imdsv2_enforcement: "Checks if EC2 instances enforce IMDSv2 (HttpTokens: required) \u2014 IMDSv1 allows credential theft via SSRF.",
|
|
5678
|
+
waf_coverage: "Checks if internet-facing ALBs have WAF Web ACL associated for protection against common web exploits."
|
|
5426
5679
|
};
|
|
5680
|
+
var HW_DEFENSE_CHECKLIST = `
|
|
5681
|
+
\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
5682
|
+
\u{1F4CB} \u62A4\u7F51\u884C\u52A8\u8865\u5145\u63D0\u9192\uFF08\u8D85\u51FA\u81EA\u52A8\u5316\u626B\u63CF\u8303\u56F4\uFF09
|
|
5683
|
+
\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
5684
|
+
|
|
5685
|
+
\u4EE5\u4E0B\u4E8B\u9879\u9700\u8981\u4EBA\u5DE5\u786E\u8BA4\u548C\u6267\u884C\uFF1A
|
|
5686
|
+
|
|
5687
|
+
\u26A0\uFE0F \u5E94\u6025\u9694\u79BB/\u6B62\u8840\u65B9\u6848
|
|
5688
|
+
\u25A1 \u51C6\u5907\u4E13\u7528\u9694\u79BB\u5B89\u5168\u7EC4\uFF08\u65E0 Inbound/Outbound \u89C4\u5219\uFF09
|
|
5689
|
+
\u25A1 \u5236\u5B9A\u5B9E\u4F8B\u9694\u79BB SOP\uFF1A\u544A\u8B66 \u2192 \u6392\u67E5 \u2192 \u5C01\u9501\u653B\u51FBIP \u2192 \u7F51\u7EDC\u9694\u79BB \u2192 \u5B89\u5168\u5904\u7F6E \u2192 \u8BB0\u5F55\u653B\u51FB\u9879
|
|
5690
|
+
\u25A1 \u660E\u786E\u5404\u7CFB\u7EDF\uFF08\u751F\u4EA7\u6838\u5FC3/\u751F\u4EA7\u975E\u6838\u5FC3/\u6D4B\u8BD5/\u5F00\u53D1\uFF09\u7684\u5E94\u6025\u5904\u7F6E\u65B9\u5F0F
|
|
5691
|
+
\u25A1 \u660E\u786E\u5404\u9879\u76EE\u8D26\u6237\u53CA\u8D44\u6E90\u7684\u8D1F\u8D23\u4EBA\u4E0E\u8054\u7CFB\u65B9\u5F0F
|
|
5692
|
+
|
|
5693
|
+
\u26A0\uFE0F \u6D4B\u8BD5/\u5F00\u53D1\u73AF\u5883\u5904\u7F6E
|
|
5694
|
+
\u25A1 \u975E\u6838\u5FC3\u7CFB\u7EDF\u5728\u62A4\u7F51\u671F\u95F4\u5173\u95ED
|
|
5695
|
+
\u25A1 \u6D4B\u8BD5/\u5F00\u53D1\u73AF\u5883\u5173\u95ED\u6216\u4E0E\u751F\u4EA7\u4FDD\u6301\u540C\u7B49\u5B89\u5168\u57FA\u7EBF
|
|
5696
|
+
\u25A1 \u786E\u8BA4\u54EA\u4E9B\u73AF\u5883\u53EF\u4EE5\u7D27\u6025\u5173\u505C\uFF0C\u907F\u514D\u653B\u51FB\u6269\u6563
|
|
5697
|
+
|
|
5698
|
+
\u26A0\uFE0F \u503C\u5B88\u56E2\u961F\u7EC4\u5EFA
|
|
5699
|
+
\u25A1 7\xD724 \u76D1\u63A7\u5FEB\u901F\u54CD\u5E94\u56E2\u961F
|
|
5700
|
+
\u25A1 \u6280\u672F\u4E0E\u98CE\u9669\u5206\u6790\u7EC4
|
|
5701
|
+
\u25A1 \u5B89\u5168\u7B56\u7565\u4E0B\u53D1\u7EC4
|
|
5702
|
+
\u25A1 \u4E1A\u52A1\u54CD\u5E94\u7EC4
|
|
5703
|
+
\u25A1 \u660E\u786E AWS TAM/Support \u8054\u7CFB\u65B9\u5F0F\uFF08ES/EOP \u5BA2\u6237\uFF09
|
|
5704
|
+
|
|
5705
|
+
\u26A0\uFE0F \u51FA\u5165\u7AD9\u8DEF\u5F84\u67B6\u6784\u56FE
|
|
5706
|
+
\u25A1 \u786E\u4FDD\u6240\u6709\u4E92\u8054\u7F51/DX \u4E13\u7EBF\u51FA\u5165\u7AD9\u8DEF\u5F84\u5728\u67B6\u6784\u56FE\u4E2D\u6E05\u6670\u6807\u6CE8
|
|
5707
|
+
\u25A1 \u660E\u786E\u5404 ELB/Public EC2/S3/DX \u7684\u6570\u636E\u6D41\u5411
|
|
5708
|
+
\u25A1 \u8BC6\u522B\u6240\u6709\u9762\u5411\u4E92\u8054\u7F51\u7684\u6570\u636E\u4EA4\u4E92\u63A5\u53E3
|
|
5709
|
+
|
|
5710
|
+
\u26A0\uFE0F \u4E3B\u52A8\u5F0F\u6E17\u900F\u6D4B\u8BD5
|
|
5711
|
+
\u25A1 \u62A4\u7F51\u524D\u8054\u7CFB\u5B89\u5168\u5382\u5546\uFF08\u9752\u85E4/\u957F\u4EAD/\u5FAE\u6B65\u7B49\uFF09\u8FDB\u884C\u6A21\u62DF\u653B\u51FB\u6F14\u7EC3
|
|
5712
|
+
\u25A1 \u57FA\u4E8E\u6E17\u900F\u6D4B\u8BD5\u62A5\u544A\u8FDB\u884C\u6B63\u5F0F\u62A4\u7F51\u524D\u7684\u5B89\u5168\u52A0\u56FA
|
|
5713
|
+
\u25A1 \u5173\u6CE8 AWS \u5B89\u5168\u516C\u544A\uFF08\u5DF2\u77E5\u6F0F\u6D1E\u4E0E\u8865\u4E01\uFF09
|
|
5714
|
+
|
|
5715
|
+
\u26A0\uFE0F WAR-ROOM \u5B9E\u65F6\u6C9F\u901A
|
|
5716
|
+
\u25A1 \u521B\u5EFA\u62A4\u7F51\u671F\u95F4\u4E13\u7528\u6C9F\u901A\u6E20\u9053\uFF08\u4F01\u5FAE/\u9489\u9489/\u98DE\u4E66/Chime\uFF09
|
|
5717
|
+
\u25A1 \u4E0E AWS TAM \u5EFA\u7ACB WAR-ROOM \u8054\u7CFB\uFF08\u4F01\u4E1A\u7EA7\u652F\u6301\u5BA2\u6237\uFF09
|
|
5718
|
+
\u25A1 \u7EDF\u4E00\u6848\u4F8B\u6807\u9898\u683C\u5F0F\uFF1A"\u3010\u62A4\u7F51\u3011+ \u95EE\u9898\u63CF\u8FF0"
|
|
5719
|
+
|
|
5720
|
+
\u26A0\uFE0F \u5BC6\u7801\u4E0E\u51ED\u8BC1\u7BA1\u7406
|
|
5721
|
+
\u25A1 \u6240\u6709 IAM \u7528\u6237\u7ED1\u5B9A MFA
|
|
5722
|
+
\u25A1 AKSK \u8F6E\u8F6C\u5468\u671F \u2264 90 \u5929
|
|
5723
|
+
\u25A1 \u907F\u514D\u5171\u4EAB\u8D26\u6237\u4F7F\u7528
|
|
5724
|
+
\u25A1 S3/Lambda/\u5E94\u7528\u4EE3\u7801\u4E2D\u65E0\u660E\u6587\u5BC6\u7801
|
|
5725
|
+
|
|
5726
|
+
\u26A0\uFE0F \u62A4\u7F51\u540E\u4F18\u5316
|
|
5727
|
+
\u25A1 \u9488\u5BF9\u653B\u51FB\u62A5\u544A\u9010\u9879\u5E94\u7B54\u4E0E\u4FEE\u590D
|
|
5728
|
+
\u25A1 \u4E0E\u5B89\u5168\u56E2\u961F\u5EFA\u7ACB\u5468\u671F\u6027\u5B89\u5168\u7EF4\u62A4\u6D41\u7A0B
|
|
5729
|
+
\u25A1 \u6301\u7EED\u8865\u5168\u5B89\u5168\u98CE\u9669
|
|
5730
|
+
|
|
5731
|
+
\u53C2\u8003\uFF1AAWS \u62A4\u7F51\u884C\u52A8 Standard Operation Procedure (Compliance IEM)
|
|
5732
|
+
`;
|
|
5427
5733
|
function summarizeResult(result) {
|
|
5428
5734
|
const { summary } = result;
|
|
5429
5735
|
const lines = [
|
|
@@ -5474,7 +5780,9 @@ function createServer(defaultRegion) {
|
|
|
5474
5780
|
new TrustedAdvisorFindingsScanner(),
|
|
5475
5781
|
new ConfigRulesFindingsScanner(),
|
|
5476
5782
|
new AccessAnalyzerFindingsScanner(),
|
|
5477
|
-
new PatchComplianceFindingsScanner()
|
|
5783
|
+
new PatchComplianceFindingsScanner(),
|
|
5784
|
+
new Imdsv2EnforcementScanner(),
|
|
5785
|
+
new WafCoverageScanner()
|
|
5478
5786
|
];
|
|
5479
5787
|
const scannerMap = /* @__PURE__ */ new Map();
|
|
5480
5788
|
for (const s of allScanners) {
|
|
@@ -5530,7 +5838,9 @@ function createServer(defaultRegion) {
|
|
|
5530
5838
|
{ toolName: "scan_trusted_advisor_findings", moduleName: "trusted_advisor_findings", label: "Trusted Advisor Findings" },
|
|
5531
5839
|
{ toolName: "scan_config_rules_findings", moduleName: "config_rules_findings", label: "Config Rules Findings" },
|
|
5532
5840
|
{ toolName: "scan_access_analyzer_findings", moduleName: "access_analyzer_findings", label: "Access Analyzer Findings" },
|
|
5533
|
-
{ toolName: "scan_patch_compliance_findings", moduleName: "patch_compliance_findings", label: "Patch Compliance Findings" }
|
|
5841
|
+
{ toolName: "scan_patch_compliance_findings", moduleName: "patch_compliance_findings", label: "Patch Compliance Findings" },
|
|
5842
|
+
{ toolName: "scan_imdsv2_enforcement", moduleName: "imdsv2_enforcement", label: "IMDSv2 Enforcement" },
|
|
5843
|
+
{ toolName: "scan_waf_coverage", moduleName: "waf_coverage", label: "WAF Coverage" }
|
|
5534
5844
|
];
|
|
5535
5845
|
for (const { toolName, moduleName, label } of individualScanners) {
|
|
5536
5846
|
server.tool(
|
|
@@ -5653,12 +5963,17 @@ function createServer(defaultRegion) {
|
|
|
5653
5963
|
lines.push("");
|
|
5654
5964
|
lines.push(`Warning: ${missingModules.length} requested module(s) not available: ${missingModules.join(", ")}`);
|
|
5655
5965
|
}
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
|
|
5660
|
-
|
|
5661
|
-
|
|
5966
|
+
const content = [
|
|
5967
|
+
{ type: "text", text: lines.join("\n") },
|
|
5968
|
+
{ type: "text", text: JSON.stringify(result, null, 2) }
|
|
5969
|
+
];
|
|
5970
|
+
if (group === "hw_defense") {
|
|
5971
|
+
const summaryContent = content[0];
|
|
5972
|
+
if (summaryContent && summaryContent.type === "text") {
|
|
5973
|
+
summaryContent.text += "\n\n" + HW_DEFENSE_CHECKLIST;
|
|
5974
|
+
}
|
|
5975
|
+
}
|
|
5976
|
+
return { content };
|
|
5662
5977
|
} catch (err) {
|
|
5663
5978
|
return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
5664
5979
|
}
|
|
@@ -5962,7 +6277,7 @@ Deploy this as a StackSet from your Management Account to all member accounts.`
|
|
|
5962
6277
|
server.resource(
|
|
5963
6278
|
"security-rules",
|
|
5964
6279
|
"security://rules",
|
|
5965
|
-
{ description: "Describes all
|
|
6280
|
+
{ description: "Describes all 19 scan modules and their check rules", mimeType: "text/markdown" },
|
|
5966
6281
|
async () => ({
|
|
5967
6282
|
contents: [{ uri: "security://rules", text: SECURITY_RULES_CONTENT, mimeType: "text/markdown" }]
|
|
5968
6283
|
})
|
|
@@ -6009,6 +6324,25 @@ ${finding}`
|
|
|
6009
6324
|
]
|
|
6010
6325
|
})
|
|
6011
6326
|
);
|
|
6327
|
+
server.prompt(
|
|
6328
|
+
"hw_defense_checklist",
|
|
6329
|
+
"\u62A4\u7F51\u884C\u52A8\u5B8C\u6574\u68C0\u67E5\u6E05\u5355 \u2014 \u5305\u542B\u81EA\u52A8\u5316\u626B\u63CF\u9879\u548C\u4EBA\u5DE5\u68C0\u67E5\u9879",
|
|
6330
|
+
async () => ({
|
|
6331
|
+
messages: [
|
|
6332
|
+
{
|
|
6333
|
+
role: "user",
|
|
6334
|
+
content: {
|
|
6335
|
+
type: "text",
|
|
6336
|
+
text: `\u8BF7\u57FA\u4E8E\u4EE5\u4E0B\u62A4\u7F51\u884C\u52A8\u68C0\u67E5\u6E05\u5355\uFF0C\u5E2E\u52A9\u6211\u5236\u5B9A\u62A4\u7F51\u51C6\u5907\u8BA1\u5212\uFF1A
|
|
6337
|
+
|
|
6338
|
+
${HW_DEFENSE_CHECKLIST}
|
|
6339
|
+
|
|
6340
|
+
\u81EA\u52A8\u5316\u626B\u63CF\u90E8\u5206\u8BF7\u4F7F\u7528 scan_group hw_defense \u6267\u884C\u3002\u4EE5\u4E0A\u4EBA\u5DE5\u68C0\u67E5\u9879\u8BF7\u9010\u9879\u786E\u8BA4\u5E76\u63D0\u4F9B\u5177\u4F53\u5EFA\u8BAE\u3002`
|
|
6341
|
+
}
|
|
6342
|
+
}
|
|
6343
|
+
]
|
|
6344
|
+
})
|
|
6345
|
+
);
|
|
6012
6346
|
return server;
|
|
6013
6347
|
}
|
|
6014
6348
|
async function startServer(defaultRegion) {
|