aws-security-mcp 0.5.2 → 0.6.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 +1 -8
- package/dist/bin/aws-security-mcp.js +886 -557
- package/dist/bin/aws-security-mcp.js.map +1 -1
- package/dist/src/index.d.ts +6 -4
- package/dist/src/index.js +886 -557
- package/dist/src/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -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.6.0";
|
|
241
241
|
|
|
242
242
|
// src/utils/aws-client.ts
|
|
243
243
|
import { STSClient, GetCallerIdentityCommand } from "@aws-sdk/client-sts";
|
|
@@ -316,7 +316,9 @@ async function listOrgAccounts(region) {
|
|
|
316
316
|
var AGGREGATION_MODULES = /* @__PURE__ */ new Set([
|
|
317
317
|
"security_hub_findings",
|
|
318
318
|
"guardduty_findings",
|
|
319
|
-
"inspector_findings"
|
|
319
|
+
"inspector_findings",
|
|
320
|
+
"config_rules_findings",
|
|
321
|
+
"access_analyzer_findings"
|
|
320
322
|
]);
|
|
321
323
|
function buildSummary(modules) {
|
|
322
324
|
let critical = 0;
|
|
@@ -708,9 +710,15 @@ var ServiceDetectionScanner = class {
|
|
|
708
710
|
const insp = createClient(Inspector2Client, region, ctx.credentials);
|
|
709
711
|
const resp = await insp.send(new BatchGetAccountStatusCommand({ accountIds: [accountId] }));
|
|
710
712
|
const accounts = resp.accounts ?? [];
|
|
711
|
-
const active = accounts.some(
|
|
712
|
-
|
|
713
|
-
|
|
713
|
+
const active = accounts.some((a) => {
|
|
714
|
+
const s = a.state?.status;
|
|
715
|
+
if (s === "ENABLED" || s === "ENABLING") return true;
|
|
716
|
+
const rs = a.resourceState;
|
|
717
|
+
if (!rs) return false;
|
|
718
|
+
return ["ec2", "ecr", "lambda", "lambdaCode", "codeRepository"].some(
|
|
719
|
+
(k) => rs[k]?.status === "ENABLED"
|
|
720
|
+
);
|
|
721
|
+
});
|
|
714
722
|
if (active) {
|
|
715
723
|
services.push({
|
|
716
724
|
name: "Inspector",
|
|
@@ -2439,7 +2447,6 @@ import {
|
|
|
2439
2447
|
DescribeNetworkInterfacesCommand,
|
|
2440
2448
|
DescribeSecurityGroupsCommand as DescribeSecurityGroupsCommand2
|
|
2441
2449
|
} from "@aws-sdk/client-ec2";
|
|
2442
|
-
var THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
2443
2450
|
function makeFinding9(opts) {
|
|
2444
2451
|
const severity = severityFromScore(opts.riskScore);
|
|
2445
2452
|
return { ...opts, severity, priority: priorityFromSeverity(severity) };
|
|
@@ -2964,14 +2971,21 @@ var SecurityHubFindingsScanner = class {
|
|
|
2964
2971
|
const resourceType = f.Resources?.[0]?.Type ?? "AWS::Unknown";
|
|
2965
2972
|
const resourceArn = resourceId.startsWith("arn:") ? resourceId : `arn:${partition}:securityhub:${region}:${accountId}:finding/${f.Id ?? "unknown"}`;
|
|
2966
2973
|
const remediationSteps = [];
|
|
2967
|
-
|
|
2968
|
-
|
|
2974
|
+
const title = f.Title ?? "Security Hub Finding";
|
|
2975
|
+
if (/^KB\d+$/.test(title)) {
|
|
2976
|
+
remediationSteps.push(`Install Windows patch ${title} via WSUS or SSM Patch Manager`);
|
|
2977
|
+
remediationSteps.push(`Microsoft KB article: https://support.microsoft.com/help/${title}`);
|
|
2978
|
+
} else if (/^CVE-/.test(title)) {
|
|
2979
|
+
remediationSteps.push(`Fix vulnerability ${title}: update affected software to patched version`);
|
|
2980
|
+
} else {
|
|
2981
|
+
remediationSteps.push(title);
|
|
2969
2982
|
}
|
|
2970
2983
|
if (f.Remediation?.Recommendation?.Url) {
|
|
2971
|
-
remediationSteps.push(`
|
|
2984
|
+
remediationSteps.push(`Documentation: ${f.Remediation.Recommendation.Url}`);
|
|
2972
2985
|
}
|
|
2973
|
-
|
|
2974
|
-
|
|
2986
|
+
const recText = f.Remediation?.Recommendation?.Text ?? "";
|
|
2987
|
+
if (recText && !["See References", "None Provided", ""].includes(recText.trim())) {
|
|
2988
|
+
remediationSteps.push(recText);
|
|
2975
2989
|
}
|
|
2976
2990
|
findings.push({
|
|
2977
2991
|
severity,
|
|
@@ -3124,10 +3138,11 @@ var GuardDutyFindingsScanner = class {
|
|
|
3124
3138
|
impact: `GuardDuty threat type: ${gdf.Type ?? "unknown"} (severity ${gdSeverity})`,
|
|
3125
3139
|
riskScore: score,
|
|
3126
3140
|
remediationSteps: [
|
|
3127
|
-
|
|
3128
|
-
`
|
|
3129
|
-
"
|
|
3130
|
-
|
|
3141
|
+
`Investigate ${gdf.Type ?? "unknown threat"}: ${gdf.Title ?? "threat detected"}`,
|
|
3142
|
+
gdf.Description ? `Details: ${gdf.Description.substring(0, 200)}` : "",
|
|
3143
|
+
"Isolate affected resources if compromise is confirmed.",
|
|
3144
|
+
"Review CloudTrail logs for related suspicious activity."
|
|
3145
|
+
].filter(Boolean),
|
|
3131
3146
|
priority: priorityFromSeverity(severity),
|
|
3132
3147
|
module: this.moduleName,
|
|
3133
3148
|
accountId: gdf.AccountId ?? accountId
|
|
@@ -3218,18 +3233,44 @@ var InspectorFindingsScanner = class {
|
|
|
3218
3233
|
const resourceType = f.resources?.[0]?.type ?? "AWS::Unknown";
|
|
3219
3234
|
const resourceArn = resourceId.startsWith("arn:") ? resourceId : `arn:${partition}:inspector2:${region}:${accountId}:finding/${f.findingArn ?? "unknown"}`;
|
|
3220
3235
|
const remediationSteps = [];
|
|
3221
|
-
|
|
3236
|
+
const vulnPkgs = f.packageVulnerabilityDetails?.vulnerablePackages;
|
|
3237
|
+
if (vulnPkgs?.length) {
|
|
3238
|
+
for (const pkg of vulnPkgs.slice(0, 3)) {
|
|
3239
|
+
const name = pkg.name ?? "unknown-package";
|
|
3240
|
+
const installed = pkg.version ?? "unknown";
|
|
3241
|
+
const fixed = pkg.fixedInVersion ?? "latest";
|
|
3242
|
+
const cveRef = cveId ? ` to fix ${cveId}` : "";
|
|
3243
|
+
remediationSteps.push(`Update ${name} from ${installed} to ${fixed}${cveRef}`);
|
|
3244
|
+
}
|
|
3245
|
+
} else if (f.remediation?.recommendation?.text) {
|
|
3222
3246
|
remediationSteps.push(f.remediation.recommendation.text);
|
|
3223
3247
|
}
|
|
3248
|
+
const genericPatterns = ["See References", "None Provided", "Review the finding"];
|
|
3249
|
+
if (remediationSteps.length === 0 || genericPatterns.some((p) => remediationSteps[0]?.startsWith(p))) {
|
|
3250
|
+
remediationSteps.length = 0;
|
|
3251
|
+
const rawTitle = f.title ?? "";
|
|
3252
|
+
if (rawTitle.includes("KB")) {
|
|
3253
|
+
const kbMatch = rawTitle.match(/KB\d+/);
|
|
3254
|
+
const kb = kbMatch ? kbMatch[0] : "patch";
|
|
3255
|
+
remediationSteps.push(`Install Windows patch ${kb} via WSUS or AWS Systems Manager Patch Manager`);
|
|
3256
|
+
remediationSteps.push(`Run: aws ssm send-command --document-name "AWS-InstallWindowsUpdates" --targets "Key=InstanceIds,Values=${resourceId}"`);
|
|
3257
|
+
if (kbMatch) {
|
|
3258
|
+
remediationSteps.push(`Microsoft KB article: https://support.microsoft.com/help/${kb}`);
|
|
3259
|
+
}
|
|
3260
|
+
} else if (rawTitle.includes("CVE-") || cveId) {
|
|
3261
|
+
const cveMatch = rawTitle.match(/CVE-[\d-]+/);
|
|
3262
|
+
const cve = cveMatch ? cveMatch[0] : cveId ?? "vulnerability";
|
|
3263
|
+
remediationSteps.push(`Fix ${cve}: update the affected software package to the latest patched version`);
|
|
3264
|
+
} else {
|
|
3265
|
+
remediationSteps.push(`Review and remediate: ${rawTitle}`);
|
|
3266
|
+
}
|
|
3267
|
+
}
|
|
3224
3268
|
if (f.remediation?.recommendation?.Url) {
|
|
3225
|
-
remediationSteps.push(`
|
|
3269
|
+
remediationSteps.push(`Documentation: ${f.remediation.recommendation.Url}`);
|
|
3226
3270
|
}
|
|
3227
3271
|
if (f.packageVulnerabilityDetails?.referenceUrls?.length) {
|
|
3228
3272
|
remediationSteps.push(`CVE references: ${f.packageVulnerabilityDetails.referenceUrls.slice(0, 3).join(", ")}`);
|
|
3229
3273
|
}
|
|
3230
|
-
if (remediationSteps.length === 0) {
|
|
3231
|
-
remediationSteps.push("Review the finding in the Amazon Inspector console and apply the recommended patch or update.");
|
|
3232
|
-
}
|
|
3233
3274
|
const description = f.description ?? titleBase;
|
|
3234
3275
|
const impact = cveId ? `Vulnerability ${cveId} \u2014 CVSS: ${f.packageVulnerabilityDetails?.cvss?.[0]?.baseScore ?? "N/A"}` : `Inspector finding type: ${f.type ?? "unknown"}`;
|
|
3235
3276
|
findings.push({
|
|
@@ -3564,10 +3605,10 @@ var ConfigRulesFindingsScanner = class {
|
|
|
3564
3605
|
impact: `Resource is non-compliant with Config Rule: ${ruleName}`,
|
|
3565
3606
|
riskScore,
|
|
3566
3607
|
remediationSteps: [
|
|
3567
|
-
`
|
|
3568
|
-
`
|
|
3569
|
-
|
|
3570
|
-
],
|
|
3608
|
+
`Fix Config Rule violation: ${ruleName}`,
|
|
3609
|
+
annotation ? `Details: ${annotation}` : "",
|
|
3610
|
+
`Resource: ${resourceType}/${resourceId}`
|
|
3611
|
+
].filter(Boolean),
|
|
3571
3612
|
priority: priorityFromSeverity(severity),
|
|
3572
3613
|
module: this.moduleName,
|
|
3573
3614
|
accountId
|
|
@@ -3716,13 +3757,13 @@ var AccessAnalyzerFindingsScanner = class {
|
|
|
3716
3757
|
const title = buildFindingTitle(aaf);
|
|
3717
3758
|
const impact = external ? `Resource is accessible from outside the account. Type: ${aaf.findingType ?? "unknown"}` : `Unused access detected \u2014 review and remove to follow least-privilege. Type: ${aaf.findingType ?? "unknown"}`;
|
|
3718
3759
|
const remediationSteps = external ? [
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3760
|
+
`Restrict external access on ${resourceType} ${resourceId}`,
|
|
3761
|
+
"Remove or narrow the resource policy to eliminate unintended external access.",
|
|
3762
|
+
`Resource ARN: ${resourceArn}`
|
|
3722
3763
|
] : [
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3764
|
+
`Remove unused access on ${resourceType} ${resourceId}`,
|
|
3765
|
+
"Remove unused permissions, roles, or credentials to follow least-privilege.",
|
|
3766
|
+
`Resource ARN: ${resourceArn}`
|
|
3726
3767
|
];
|
|
3727
3768
|
findings.push({
|
|
3728
3769
|
severity,
|
|
@@ -4197,6 +4238,479 @@ var WafCoverageScanner = class {
|
|
|
4197
4238
|
}
|
|
4198
4239
|
};
|
|
4199
4240
|
|
|
4241
|
+
// src/i18n/zh.ts
|
|
4242
|
+
var zhI18n = {
|
|
4243
|
+
// HTML Security Report
|
|
4244
|
+
securityReportTitle: "AWS \u5B89\u5168\u626B\u63CF\u62A5\u544A",
|
|
4245
|
+
securityScore: "\u5B89\u5168\u8BC4\u5206",
|
|
4246
|
+
critical: "\u4E25\u91CD",
|
|
4247
|
+
high: "\u9AD8",
|
|
4248
|
+
medium: "\u4E2D",
|
|
4249
|
+
low: "\u4F4E",
|
|
4250
|
+
scanStatistics: "\u626B\u63CF\u7EDF\u8BA1",
|
|
4251
|
+
module: "\u6A21\u5757",
|
|
4252
|
+
resources: "\u8D44\u6E90",
|
|
4253
|
+
findings: "\u53D1\u73B0",
|
|
4254
|
+
status: "\u72B6\u6001",
|
|
4255
|
+
allFindings: "\u6240\u6709\u53D1\u73B0",
|
|
4256
|
+
recommendations: "\u5EFA\u8BAE",
|
|
4257
|
+
unique: "\u53BB\u91CD",
|
|
4258
|
+
showMore: "\u663E\u793A\u66F4\u591A",
|
|
4259
|
+
noIssuesFound: "\u672A\u53D1\u73B0\u5B89\u5168\u95EE\u9898\u3002",
|
|
4260
|
+
allModulesClean: "\u6240\u6709\u6A21\u5757\u6B63\u5E38",
|
|
4261
|
+
generatedBy: "\u7531 AWS Security MCP Server \u751F\u6210",
|
|
4262
|
+
informationalOnly: "\u672C\u62A5\u544A\u4EC5\u4F9B\u53C2\u8003\u3002",
|
|
4263
|
+
// MLPS Report
|
|
4264
|
+
mlpsTitle: "\u7B49\u4FDD\u4E09\u7EA7\u9884\u68C0\u62A5\u544A",
|
|
4265
|
+
mlpsDisclaimer: "\u672C\u62A5\u544A\u4E3A\u7B49\u4FDD\u4E09\u7EA7\u9884\u68C0\u53C2\u8003\uFF0C\u63D0\u4F9B\u4E91\u5E73\u53F0\u914D\u7F6E\u68C0\u67E5\u6570\u636E\u4E0E\u5EFA\u8BAE\u3002\u5408\u89C4\u5224\u5B9A\uFF08\u7B26\u5408/\u90E8\u5206\u7B26\u5408/\u4E0D\u7B26\u5408\uFF09\u9700\u7531\u6301\u8BC1\u6D4B\u8BC4\u673A\u6784\u6839\u636E\u5B9E\u9645\u60C5\u51B5\u786E\u8BA4\u3002\uFF08GB/T 22239-2019 \u5B8C\u6574\u68C0\u67E5\u6E05\u5355 184 \u9879\uFF09",
|
|
4266
|
+
checkedItems: "\u5DF2\u68C0\u67E5\u9879",
|
|
4267
|
+
noIssues: "\u672A\u53D1\u73B0\u95EE\u9898",
|
|
4268
|
+
issuesFound: "\u53D1\u73B0\u95EE\u9898",
|
|
4269
|
+
notChecked: "\u672A\u68C0\u67E5",
|
|
4270
|
+
cloudProvider: "\u4E91\u5E73\u53F0\u8D1F\u8D23",
|
|
4271
|
+
manualReview: "\u9700\u4EBA\u5DE5\u8BC4\u4F30",
|
|
4272
|
+
notApplicable: "\u4E0D\u9002\u7528",
|
|
4273
|
+
checkResult: "\u68C0\u67E5\u7ED3\u679C",
|
|
4274
|
+
noRelatedIssues: "\u68C0\u67E5\u7ED3\u679C\uFF1A\u672A\u53D1\u73B0\u76F8\u5173\u95EE\u9898",
|
|
4275
|
+
issuesFoundCount: (n) => `\u68C0\u67E5\u7ED3\u679C\uFF1A\u53D1\u73B0 ${n} \u4E2A\u76F8\u5173\u95EE\u9898`,
|
|
4276
|
+
remediation: "\u5EFA\u8BAE",
|
|
4277
|
+
remediationItems: (n) => `\u5EFA\u8BAE\u6574\u6539\u9879\uFF08${n} \u9879\u53BB\u91CD\uFF09`,
|
|
4278
|
+
showRemaining: (n) => `\u663E\u793A\u5176\u4F59 ${n} \u9879`,
|
|
4279
|
+
// HW Defense Checklist
|
|
4280
|
+
hwChecklistTitle: "\u{1F4CB} \u62A4\u7F51\u884C\u52A8\u8865\u5145\u63D0\u9192\uFF08\u8D85\u51FA\u81EA\u52A8\u5316\u626B\u63CF\u8303\u56F4\uFF09",
|
|
4281
|
+
hwChecklistSubtitle: "\u4EE5\u4E0B\u4E8B\u9879\u9700\u8981\u4EBA\u5DE5\u786E\u8BA4\u548C\u6267\u884C\uFF1A",
|
|
4282
|
+
hwEmergencyIsolation: `\u26A0\uFE0F \u5E94\u6025\u9694\u79BB/\u6B62\u8840\u65B9\u6848
|
|
4283
|
+
\u25A1 \u51C6\u5907\u4E13\u7528\u9694\u79BB\u5B89\u5168\u7EC4\uFF08\u65E0 Inbound/Outbound \u89C4\u5219\uFF09
|
|
4284
|
+
\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
|
|
4285
|
+
\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
|
|
4286
|
+
\u25A1 \u660E\u786E\u5404\u9879\u76EE\u8D26\u6237\u53CA\u8D44\u6E90\u7684\u8D1F\u8D23\u4EBA\u4E0E\u8054\u7CFB\u65B9\u5F0F`,
|
|
4287
|
+
hwTestEnvShutdown: `\u26A0\uFE0F \u6D4B\u8BD5/\u5F00\u53D1\u73AF\u5883\u5904\u7F6E
|
|
4288
|
+
\u25A1 \u975E\u6838\u5FC3\u7CFB\u7EDF\u5728\u62A4\u7F51\u671F\u95F4\u5173\u95ED
|
|
4289
|
+
\u25A1 \u6D4B\u8BD5/\u5F00\u53D1\u73AF\u5883\u5173\u95ED\u6216\u4E0E\u751F\u4EA7\u4FDD\u6301\u540C\u7B49\u5B89\u5168\u57FA\u7EBF
|
|
4290
|
+
\u25A1 \u786E\u8BA4\u54EA\u4E9B\u73AF\u5883\u53EF\u4EE5\u7D27\u6025\u5173\u505C\uFF0C\u907F\u514D\u653B\u51FB\u6269\u6563`,
|
|
4291
|
+
hwDutyTeam: `\u26A0\uFE0F \u503C\u5B88\u56E2\u961F\u7EC4\u5EFA
|
|
4292
|
+
\u25A1 7\xD724 \u76D1\u63A7\u5FEB\u901F\u54CD\u5E94\u56E2\u961F
|
|
4293
|
+
\u25A1 \u6280\u672F\u4E0E\u98CE\u9669\u5206\u6790\u7EC4
|
|
4294
|
+
\u25A1 \u5B89\u5168\u7B56\u7565\u4E0B\u53D1\u7EC4
|
|
4295
|
+
\u25A1 \u4E1A\u52A1\u54CD\u5E94\u7EC4
|
|
4296
|
+
\u25A1 \u660E\u786E AWS TAM/Support \u8054\u7CFB\u65B9\u5F0F\uFF08ES/EOP \u5BA2\u6237\uFF09`,
|
|
4297
|
+
hwNetworkDiagram: `\u26A0\uFE0F \u51FA\u5165\u7AD9\u8DEF\u5F84\u67B6\u6784\u56FE
|
|
4298
|
+
\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
|
|
4299
|
+
\u25A1 \u660E\u786E\u5404 ELB/Public EC2/S3/DX \u7684\u6570\u636E\u6D41\u5411
|
|
4300
|
+
\u25A1 \u8BC6\u522B\u6240\u6709\u9762\u5411\u4E92\u8054\u7F51\u7684\u6570\u636E\u4EA4\u4E92\u63A5\u53E3`,
|
|
4301
|
+
hwPentest: `\u26A0\uFE0F \u4E3B\u52A8\u5F0F\u6E17\u900F\u6D4B\u8BD5
|
|
4302
|
+
\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
|
|
4303
|
+
\u25A1 \u57FA\u4E8E\u6E17\u900F\u6D4B\u8BD5\u62A5\u544A\u8FDB\u884C\u6B63\u5F0F\u62A4\u7F51\u524D\u7684\u5B89\u5168\u52A0\u56FA
|
|
4304
|
+
\u25A1 \u5173\u6CE8 AWS \u5B89\u5168\u516C\u544A\uFF08\u5DF2\u77E5\u6F0F\u6D1E\u4E0E\u8865\u4E01\uFF09`,
|
|
4305
|
+
hwWarRoom: `\u26A0\uFE0F WAR-ROOM \u5B9E\u65F6\u6C9F\u901A
|
|
4306
|
+
\u25A1 \u521B\u5EFA\u62A4\u7F51\u671F\u95F4\u4E13\u7528\u6C9F\u901A\u6E20\u9053\uFF08\u4F01\u5FAE/\u9489\u9489/\u98DE\u4E66/Chime\uFF09
|
|
4307
|
+
\u25A1 \u4E0E AWS TAM \u5EFA\u7ACB WAR-ROOM \u8054\u7CFB\uFF08\u4F01\u4E1A\u7EA7\u652F\u6301\u5BA2\u6237\uFF09
|
|
4308
|
+
\u25A1 \u7EDF\u4E00\u6848\u4F8B\u6807\u9898\u683C\u5F0F\uFF1A\u201C\u3010\u62A4\u7F51\u3011+ \u95EE\u9898\u63CF\u8FF0\u201D`,
|
|
4309
|
+
hwCredentials: `\u26A0\uFE0F \u5BC6\u7801\u4E0E\u51ED\u8BC1\u7BA1\u7406
|
|
4310
|
+
\u25A1 \u6240\u6709 IAM \u7528\u6237\u7ED1\u5B9A MFA
|
|
4311
|
+
\u25A1 AKSK \u8F6E\u8F6C\u5468\u671F \u2264 90 \u5929
|
|
4312
|
+
\u25A1 \u907F\u514D\u5171\u4EAB\u8D26\u6237\u4F7F\u7528
|
|
4313
|
+
\u25A1 S3/Lambda/\u5E94\u7528\u4EE3\u7801\u4E2D\u65E0\u660E\u6587\u5BC6\u7801`,
|
|
4314
|
+
hwPostOptimization: `\u26A0\uFE0F \u62A4\u7F51\u540E\u4F18\u5316
|
|
4315
|
+
\u25A1 \u9488\u5BF9\u653B\u51FB\u62A5\u544A\u9010\u9879\u5E94\u7B54\u4E0E\u4FEE\u590D
|
|
4316
|
+
\u25A1 \u4E0E\u5B89\u5168\u56E2\u961F\u5EFA\u7ACB\u5468\u671F\u6027\u5B89\u5168\u7EF4\u62A4\u6D41\u7A0B
|
|
4317
|
+
\u25A1 \u6301\u7EED\u8865\u5168\u5B89\u5168\u98CE\u9669`,
|
|
4318
|
+
hwReference: "\u53C2\u8003\uFF1AAWS \u62A4\u7F51\u884C\u52A8 Standard Operation Procedure (Compliance IEM)",
|
|
4319
|
+
// Service Reminders
|
|
4320
|
+
serviceReminderTitle: "\u26A1 \u4EE5\u4E0B\u5B89\u5168\u670D\u52A1\u672A\u542F\u7528\uFF0C\u90E8\u5206\u68C0\u67E5\u65E0\u6CD5\u6267\u884C\uFF1A",
|
|
4321
|
+
serviceReminderFooter: "\u542F\u7528\u4EE5\u4E0A\u670D\u52A1\u540E\u91CD\u65B0\u626B\u63CF\u53EF\u83B7\u5F97\u66F4\u5B8C\u6574\u7684\u5B89\u5168\u8BC4\u4F30\u3002",
|
|
4322
|
+
serviceImpact: "\u5F71\u54CD",
|
|
4323
|
+
serviceAction: "\u5EFA\u8BAE",
|
|
4324
|
+
// Common
|
|
4325
|
+
account: "\u8D26\u6237",
|
|
4326
|
+
region: "\u533A\u57DF",
|
|
4327
|
+
scanTime: "\u626B\u63CF\u65F6\u95F4",
|
|
4328
|
+
duration: "\u8017\u65F6",
|
|
4329
|
+
severityDistribution: "\u4E25\u91CD\u6027\u5206\u5E03",
|
|
4330
|
+
findingsByModule: "\u6309\u6A21\u5757\u5206\u7C7B\u7684\u53D1\u73B0",
|
|
4331
|
+
details: "\u8BE6\u60C5",
|
|
4332
|
+
// Extended — HTML Security Report extras
|
|
4333
|
+
topHighestRiskFindings: (n) => `\u524D ${n} \u9879\u6700\u9AD8\u98CE\u9669\u53D1\u73B0`,
|
|
4334
|
+
resource: "\u8D44\u6E90",
|
|
4335
|
+
impact: "\u5F71\u54CD",
|
|
4336
|
+
riskScore: "\u98CE\u9669\u8BC4\u5206",
|
|
4337
|
+
showRemainingFindings: (n) => `\u663E\u793A\u5269\u4F59 ${n} \u9879\u53D1\u73B0\u2026`,
|
|
4338
|
+
trendTitle: "30\u65E5\u8D8B\u52BF",
|
|
4339
|
+
findingsBySeverity: "\u6309\u4E25\u91CD\u6027\u5206\u7C7B\u7684\u53D1\u73B0",
|
|
4340
|
+
showMoreCount: (n) => `\u663E\u793A\u5269\u4F59 ${n} \u9879\u2026`,
|
|
4341
|
+
// Extended — MLPS extras
|
|
4342
|
+
// Markdown report
|
|
4343
|
+
executiveSummary: "\u6267\u884C\u6458\u8981",
|
|
4344
|
+
totalFindingsLabel: "\u53D1\u73B0\u603B\u6570",
|
|
4345
|
+
description: "\u63CF\u8FF0",
|
|
4346
|
+
priority: "\u4F18\u5148\u7EA7",
|
|
4347
|
+
noFindingsForSeverity: (severity) => `\u65E0${severity}\u53D1\u73B0\u3002`,
|
|
4348
|
+
preCheckOverview: "\u9884\u68C0\u603B\u89C8",
|
|
4349
|
+
accountInfo: "\u8D26\u6237\u4FE1\u606F",
|
|
4350
|
+
checkedCount: (total, clean, issues) => `\u5DF2\u68C0\u67E5: ${total} \u9879\uFF08\u672A\u53D1\u73B0\u95EE\u9898: ${clean} \u9879 | \u53D1\u73B0\u95EE\u9898: ${issues} \u9879\uFF09`,
|
|
4351
|
+
uncheckedCount: (n) => `\u672A\u68C0\u67E5: ${n} \u9879\uFF08\u5BF9\u5E94\u626B\u63CF\u6A21\u5757\u672A\u8FD0\u884C\uFF09`,
|
|
4352
|
+
cloudProviderCount: (n) => `\u4E91\u5E73\u53F0\u8D1F\u8D23: ${n} \u9879`,
|
|
4353
|
+
manualReviewCount: (n) => `\u9700\u4EBA\u5DE5\u8BC4\u4F30: ${n} \u9879`,
|
|
4354
|
+
naCount: (n) => `\u4E0D\u9002\u7528: ${n} \u9879`,
|
|
4355
|
+
naNote: (n) => `\u4E0D\u9002\u7528\u9879: ${n} \u9879\uFF08\u7269\u8054\u7F51/\u65E0\u7EBF\u7F51\u7EDC/\u79FB\u52A8\u7EC8\u7AEF/\u5DE5\u63A7\u7CFB\u7EDF/\u53EF\u4FE1\u9A8C\u8BC1\u7B49\uFF09`,
|
|
4356
|
+
unknownNote: (n) => `\uFF08${n} \u9879\u672A\u68C0\u67E5\uFF0C\u5BF9\u5E94\u626B\u63CF\u6A21\u5757\u672A\u8FD0\u884C\uFF09`,
|
|
4357
|
+
cloudItemsNote: (n) => `\u4EE5\u4E0B ${n} \u9879\u7531 AWS \u4E91\u5E73\u53F0\u8D1F\u8D23\uFF0C\u6839\u636E\u5B89\u5168\u8D23\u4EFB\u5171\u62C5\u6A21\u578B\u4E0D\u5728\u672C\u62A5\u544A\u68C0\u67E5\u8303\u56F4\u5185\u3002`,
|
|
4358
|
+
mlpsFooterGenerated: (version) => `\u7531 AWS Security MCP Server v${version} \u751F\u6210`,
|
|
4359
|
+
mlpsFooterDisclaimer: "\u672C\u62A5\u544A\u4E3A\u8BC1\u636E\u6536\u96C6\u53C2\u8003\uFF0C\u4E0D\u5305\u542B\u5408\u89C4\u5224\u5B9A\u3002\u5B8C\u6574\u7B49\u4FDD\u6D4B\u8BC4\u9700\u7531\u6301\u8BC1\u6D4B\u8BC4\u673A\u6784\u6267\u884C\u3002",
|
|
4360
|
+
andMore: (n) => `... \u53CA\u5176\u4ED6 ${n} \u9879`,
|
|
4361
|
+
remediationByPriority: "\u5EFA\u8BAE\u6574\u6539\u9879\uFF08\u6309\u4F18\u5148\u7EA7\uFF09",
|
|
4362
|
+
affectedResources: (n) => `\u6D89\u53CA ${n} \u4E2A\u8D44\u6E90`,
|
|
4363
|
+
installWindowsPatches: (n, kbs) => `\u5B89\u88C5 ${n} \u4E2A Windows \u8865\u4E01 (${kbs})`,
|
|
4364
|
+
mlpsCategorySection: {
|
|
4365
|
+
"\u5B89\u5168\u7269\u7406\u73AF\u5883": "\u4E00\u3001\u5B89\u5168\u7269\u7406\u73AF\u5883",
|
|
4366
|
+
"\u5B89\u5168\u901A\u4FE1\u7F51\u7EDC": "\u4E8C\u3001\u5B89\u5168\u901A\u4FE1\u7F51\u7EDC",
|
|
4367
|
+
"\u5B89\u5168\u533A\u57DF\u8FB9\u754C": "\u4E09\u3001\u5B89\u5168\u533A\u57DF\u8FB9\u754C",
|
|
4368
|
+
"\u5B89\u5168\u8BA1\u7B97\u73AF\u5883": "\u56DB\u3001\u5B89\u5168\u8BA1\u7B97\u73AF\u5883",
|
|
4369
|
+
"\u5B89\u5168\u7BA1\u7406\u4E2D\u5FC3": "\u4E94\u3001\u5B89\u5168\u7BA1\u7406\u4E2D\u5FC3"
|
|
4370
|
+
},
|
|
4371
|
+
// Service Recommendations
|
|
4372
|
+
notEnabled: "\u672A\u542F\u7528",
|
|
4373
|
+
serviceRecommendations: {
|
|
4374
|
+
security_hub_findings: {
|
|
4375
|
+
icon: "\u{1F534}",
|
|
4376
|
+
service: "Security Hub",
|
|
4377
|
+
impact: "\u65E0\u6CD5\u83B7\u53D6 300+ \u9879\u81EA\u52A8\u5316\u5B89\u5168\u68C0\u67E5\uFF08FSBP/CIS/PCI DSS \u6807\u51C6\uFF09",
|
|
4378
|
+
action: "\u542F\u7528 Security Hub \u83B7\u5F97\u6700\u5168\u9762\u7684\u5B89\u5168\u6001\u52BF\u8BC4\u4F30"
|
|
4379
|
+
},
|
|
4380
|
+
guardduty_findings: {
|
|
4381
|
+
icon: "\u{1F534}",
|
|
4382
|
+
service: "GuardDuty",
|
|
4383
|
+
impact: "\u65E0\u6CD5\u68C0\u6D4B\u5A01\u80C1\u6D3B\u52A8\uFF08\u6076\u610F IP\u3001\u5F02\u5E38 API \u8C03\u7528\u3001\u52A0\u5BC6\u8D27\u5E01\u6316\u77FF\u7B49\uFF09",
|
|
4384
|
+
action: "\u542F\u7528 GuardDuty \u83B7\u5F97\u6301\u7EED\u5A01\u80C1\u68C0\u6D4B\u80FD\u529B"
|
|
4385
|
+
},
|
|
4386
|
+
inspector_findings: {
|
|
4387
|
+
icon: "\u{1F7E1}",
|
|
4388
|
+
service: "Inspector",
|
|
4389
|
+
impact: "\u65E0\u6CD5\u626B\u63CF EC2/Lambda/\u5BB9\u5668\u7684\u8F6F\u4EF6\u6F0F\u6D1E\uFF08CVE\uFF09",
|
|
4390
|
+
action: "\u542F\u7528 Inspector \u53D1\u73B0\u5DF2\u77E5\u5B89\u5168\u6F0F\u6D1E"
|
|
4391
|
+
},
|
|
4392
|
+
trusted_advisor_findings: {
|
|
4393
|
+
icon: "\u{1F7E1}",
|
|
4394
|
+
service: "Trusted Advisor",
|
|
4395
|
+
impact: "\u65E0\u6CD5\u83B7\u53D6 AWS \u6700\u4F73\u5B9E\u8DF5\u5B89\u5168\u68C0\u67E5",
|
|
4396
|
+
action: "\u5347\u7EA7\u81F3 Business/Enterprise Support \u8BA1\u5212\u4EE5\u4F7F\u7528 Trusted Advisor \u5B89\u5168\u68C0\u67E5"
|
|
4397
|
+
},
|
|
4398
|
+
config_rules_findings: {
|
|
4399
|
+
icon: "\u{1F7E1}",
|
|
4400
|
+
service: "AWS Config",
|
|
4401
|
+
impact: "\u65E0\u6CD5\u68C0\u67E5\u8D44\u6E90\u914D\u7F6E\u5408\u89C4\u72B6\u6001",
|
|
4402
|
+
action: "\u542F\u7528 AWS Config \u5E76\u914D\u7F6E Config Rules"
|
|
4403
|
+
},
|
|
4404
|
+
access_analyzer_findings: {
|
|
4405
|
+
icon: "\u{1F7E1}",
|
|
4406
|
+
service: "IAM Access Analyzer",
|
|
4407
|
+
impact: "\u65E0\u6CD5\u68C0\u6D4B\u8D44\u6E90\u662F\u5426\u88AB\u5916\u90E8\u8D26\u53F7\u6216\u516C\u7F51\u8BBF\u95EE",
|
|
4408
|
+
action: "\u521B\u5EFA IAM Access Analyzer\uFF08\u8D26\u6237\u7EA7\u6216\u7EC4\u7EC7\u7EA7\uFF09"
|
|
4409
|
+
},
|
|
4410
|
+
patch_compliance_findings: {
|
|
4411
|
+
icon: "\u{1F7E1}",
|
|
4412
|
+
service: "SSM Patch Manager",
|
|
4413
|
+
impact: "\u65E0\u6CD5\u68C0\u67E5\u5B9E\u4F8B\u8865\u4E01\u5408\u89C4\u72B6\u6001",
|
|
4414
|
+
action: "\u5B89\u88C5 SSM Agent \u5E76\u914D\u7F6E Patch Manager"
|
|
4415
|
+
}
|
|
4416
|
+
},
|
|
4417
|
+
// HW Checklist (full composite)
|
|
4418
|
+
hwChecklist: `
|
|
4419
|
+
\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
|
|
4420
|
+
\u{1F4CB} \u62A4\u7F51\u884C\u52A8\u8865\u5145\u63D0\u9192\uFF08\u8D85\u51FA\u81EA\u52A8\u5316\u626B\u63CF\u8303\u56F4\uFF09
|
|
4421
|
+
\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
|
|
4422
|
+
|
|
4423
|
+
\u4EE5\u4E0B\u4E8B\u9879\u9700\u8981\u4EBA\u5DE5\u786E\u8BA4\u548C\u6267\u884C\uFF1A
|
|
4424
|
+
|
|
4425
|
+
\u26A0\uFE0F \u5E94\u6025\u9694\u79BB/\u6B62\u8840\u65B9\u6848
|
|
4426
|
+
\u25A1 \u51C6\u5907\u4E13\u7528\u9694\u79BB\u5B89\u5168\u7EC4\uFF08\u65E0 Inbound/Outbound \u89C4\u5219\uFF09
|
|
4427
|
+
\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
|
|
4428
|
+
\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
|
|
4429
|
+
\u25A1 \u660E\u786E\u5404\u9879\u76EE\u8D26\u6237\u53CA\u8D44\u6E90\u7684\u8D1F\u8D23\u4EBA\u4E0E\u8054\u7CFB\u65B9\u5F0F
|
|
4430
|
+
|
|
4431
|
+
\u26A0\uFE0F \u6D4B\u8BD5/\u5F00\u53D1\u73AF\u5883\u5904\u7F6E
|
|
4432
|
+
\u25A1 \u975E\u6838\u5FC3\u7CFB\u7EDF\u5728\u62A4\u7F51\u671F\u95F4\u5173\u95ED
|
|
4433
|
+
\u25A1 \u6D4B\u8BD5/\u5F00\u53D1\u73AF\u5883\u5173\u95ED\u6216\u4E0E\u751F\u4EA7\u4FDD\u6301\u540C\u7B49\u5B89\u5168\u57FA\u7EBF
|
|
4434
|
+
\u25A1 \u786E\u8BA4\u54EA\u4E9B\u73AF\u5883\u53EF\u4EE5\u7D27\u6025\u5173\u505C\uFF0C\u907F\u514D\u653B\u51FB\u6269\u6563
|
|
4435
|
+
|
|
4436
|
+
\u26A0\uFE0F \u503C\u5B88\u56E2\u961F\u7EC4\u5EFA
|
|
4437
|
+
\u25A1 7\xD724 \u76D1\u63A7\u5FEB\u901F\u54CD\u5E94\u56E2\u961F
|
|
4438
|
+
\u25A1 \u6280\u672F\u4E0E\u98CE\u9669\u5206\u6790\u7EC4
|
|
4439
|
+
\u25A1 \u5B89\u5168\u7B56\u7565\u4E0B\u53D1\u7EC4
|
|
4440
|
+
\u25A1 \u4E1A\u52A1\u54CD\u5E94\u7EC4
|
|
4441
|
+
\u25A1 \u660E\u786E AWS TAM/Support \u8054\u7CFB\u65B9\u5F0F\uFF08ES/EOP \u5BA2\u6237\uFF09
|
|
4442
|
+
|
|
4443
|
+
\u26A0\uFE0F \u51FA\u5165\u7AD9\u8DEF\u5F84\u67B6\u6784\u56FE
|
|
4444
|
+
\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
|
|
4445
|
+
\u25A1 \u660E\u786E\u5404 ELB/Public EC2/S3/DX \u7684\u6570\u636E\u6D41\u5411
|
|
4446
|
+
\u25A1 \u8BC6\u522B\u6240\u6709\u9762\u5411\u4E92\u8054\u7F51\u7684\u6570\u636E\u4EA4\u4E92\u63A5\u53E3
|
|
4447
|
+
|
|
4448
|
+
\u26A0\uFE0F \u4E3B\u52A8\u5F0F\u6E17\u900F\u6D4B\u8BD5
|
|
4449
|
+
\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
|
|
4450
|
+
\u25A1 \u57FA\u4E8E\u6E17\u900F\u6D4B\u8BD5\u62A5\u544A\u8FDB\u884C\u6B63\u5F0F\u62A4\u7F51\u524D\u7684\u5B89\u5168\u52A0\u56FA
|
|
4451
|
+
\u25A1 \u5173\u6CE8 AWS \u5B89\u5168\u516C\u544A\uFF08\u5DF2\u77E5\u6F0F\u6D1E\u4E0E\u8865\u4E01\uFF09
|
|
4452
|
+
|
|
4453
|
+
\u26A0\uFE0F WAR-ROOM \u5B9E\u65F6\u6C9F\u901A
|
|
4454
|
+
\u25A1 \u521B\u5EFA\u62A4\u7F51\u671F\u95F4\u4E13\u7528\u6C9F\u901A\u6E20\u9053\uFF08\u4F01\u5FAE/\u9489\u9489/\u98DE\u4E66/Chime\uFF09
|
|
4455
|
+
\u25A1 \u4E0E AWS TAM \u5EFA\u7ACB WAR-ROOM \u8054\u7CFB\uFF08\u4F01\u4E1A\u7EA7\u652F\u6301\u5BA2\u6237\uFF09
|
|
4456
|
+
\u25A1 \u7EDF\u4E00\u6848\u4F8B\u6807\u9898\u683C\u5F0F\uFF1A\u201C\u3010\u62A4\u7F51\u3011+ \u95EE\u9898\u63CF\u8FF0\u201D
|
|
4457
|
+
|
|
4458
|
+
\u26A0\uFE0F \u5BC6\u7801\u4E0E\u51ED\u8BC1\u7BA1\u7406
|
|
4459
|
+
\u25A1 \u6240\u6709 IAM \u7528\u6237\u7ED1\u5B9A MFA
|
|
4460
|
+
\u25A1 AKSK \u8F6E\u8F6C\u5468\u671F \u2264 90 \u5929
|
|
4461
|
+
\u25A1 \u907F\u514D\u5171\u4EAB\u8D26\u6237\u4F7F\u7528
|
|
4462
|
+
\u25A1 S3/Lambda/\u5E94\u7528\u4EE3\u7801\u4E2D\u65E0\u660E\u6587\u5BC6\u7801
|
|
4463
|
+
|
|
4464
|
+
\u26A0\uFE0F \u62A4\u7F51\u540E\u4F18\u5316
|
|
4465
|
+
\u25A1 \u9488\u5BF9\u653B\u51FB\u62A5\u544A\u9010\u9879\u5E94\u7B54\u4E0E\u4FEE\u590D
|
|
4466
|
+
\u25A1 \u4E0E\u5B89\u5168\u56E2\u961F\u5EFA\u7ACB\u5468\u671F\u6027\u5B89\u5168\u7EF4\u62A4\u6D41\u7A0B
|
|
4467
|
+
\u25A1 \u6301\u7EED\u8865\u5168\u5B89\u5168\u98CE\u9669
|
|
4468
|
+
|
|
4469
|
+
\u53C2\u8003\uFF1AAWS \u62A4\u7F51\u884C\u52A8 Standard Operation Procedure (Compliance IEM)
|
|
4470
|
+
`
|
|
4471
|
+
};
|
|
4472
|
+
|
|
4473
|
+
// src/i18n/en.ts
|
|
4474
|
+
var enI18n = {
|
|
4475
|
+
// HTML Security Report
|
|
4476
|
+
securityReportTitle: "AWS Security Scan Report",
|
|
4477
|
+
securityScore: "Security Score",
|
|
4478
|
+
critical: "Critical",
|
|
4479
|
+
high: "High",
|
|
4480
|
+
medium: "Medium",
|
|
4481
|
+
low: "Low",
|
|
4482
|
+
scanStatistics: "Scan Statistics",
|
|
4483
|
+
module: "Module",
|
|
4484
|
+
resources: "Resources",
|
|
4485
|
+
findings: "Findings",
|
|
4486
|
+
status: "Status",
|
|
4487
|
+
allFindings: "All Findings",
|
|
4488
|
+
recommendations: "Recommendations",
|
|
4489
|
+
unique: "unique",
|
|
4490
|
+
showMore: "Show more",
|
|
4491
|
+
noIssuesFound: "No security issues found.",
|
|
4492
|
+
allModulesClean: "All modules clean",
|
|
4493
|
+
generatedBy: "Generated by AWS Security MCP Server",
|
|
4494
|
+
informationalOnly: "This report is for informational purposes only.",
|
|
4495
|
+
// MLPS Report
|
|
4496
|
+
mlpsTitle: "MLPS Level 3 Pre-Check Report",
|
|
4497
|
+
mlpsDisclaimer: "This report is for MLPS Level 3 pre-check reference, providing cloud platform configuration check data and recommendations. Compliance determination (compliant/partially compliant/non-compliant) must be confirmed by a certified assessment institution. (GB/T 22239-2019 full checklist: 184 items)",
|
|
4498
|
+
checkedItems: "Checked Items",
|
|
4499
|
+
noIssues: "No Issues Found",
|
|
4500
|
+
issuesFound: "Issues Found",
|
|
4501
|
+
notChecked: "Not Checked",
|
|
4502
|
+
cloudProvider: "Cloud Provider Responsible",
|
|
4503
|
+
manualReview: "Manual Review Required",
|
|
4504
|
+
notApplicable: "Not Applicable",
|
|
4505
|
+
checkResult: "Check Result",
|
|
4506
|
+
noRelatedIssues: "Check Result: No related issues found",
|
|
4507
|
+
issuesFoundCount: (n) => `Check Result: Found ${n} related issue${n === 1 ? "" : "s"}`,
|
|
4508
|
+
remediation: "Remediation",
|
|
4509
|
+
remediationItems: (n) => `Remediation Items (${n} unique)`,
|
|
4510
|
+
showRemaining: (n) => `Show remaining ${n} items`,
|
|
4511
|
+
// HW Defense Checklist
|
|
4512
|
+
hwChecklistTitle: "\u{1F4CB} Cyber Defense Drill Supplementary Reminders (Beyond Automated Scanning)",
|
|
4513
|
+
hwChecklistSubtitle: "The following items require manual verification and execution:",
|
|
4514
|
+
hwEmergencyIsolation: `\u26A0\uFE0F Emergency Isolation / Incident Response Plan
|
|
4515
|
+
\u25A1 Prepare dedicated isolation security groups (no Inbound/Outbound rules)
|
|
4516
|
+
\u25A1 Establish instance isolation SOP: Alert \u2192 Investigate \u2192 Block attacker IP \u2192 Network isolation \u2192 Security response \u2192 Log attack details
|
|
4517
|
+
\u25A1 Define emergency response procedures for each system (production core/non-core/test/dev)
|
|
4518
|
+
\u25A1 Identify responsible personnel and contacts for each project account and resource`,
|
|
4519
|
+
hwTestEnvShutdown: `\u26A0\uFE0F Test/Development Environment Handling
|
|
4520
|
+
\u25A1 Shut down non-critical systems during the drill period
|
|
4521
|
+
\u25A1 Shut down test/dev environments or maintain same security baseline as production
|
|
4522
|
+
\u25A1 Confirm which environments can be emergency-stopped to prevent attack propagation`,
|
|
4523
|
+
hwDutyTeam: `\u26A0\uFE0F On-Duty Team Formation
|
|
4524
|
+
\u25A1 7\xD724 monitoring and rapid response team
|
|
4525
|
+
\u25A1 Technical and risk analysis team
|
|
4526
|
+
\u25A1 Security policy deployment team
|
|
4527
|
+
\u25A1 Business response team
|
|
4528
|
+
\u25A1 Confirm AWS TAM/Support contact information (ES/EOP customers)`,
|
|
4529
|
+
hwNetworkDiagram: `\u26A0\uFE0F Ingress/Egress Path Architecture Diagram
|
|
4530
|
+
\u25A1 Ensure all Internet/DX dedicated line ingress/egress paths are clearly marked in architecture diagrams
|
|
4531
|
+
\u25A1 Clarify data flow for each ELB/Public EC2/S3/DX
|
|
4532
|
+
\u25A1 Identify all internet-facing data interaction interfaces`,
|
|
4533
|
+
hwPentest: `\u26A0\uFE0F Proactive Penetration Testing
|
|
4534
|
+
\u25A1 Contact security vendors for simulated attack drills before the exercise
|
|
4535
|
+
\u25A1 Conduct security hardening based on penetration test reports
|
|
4536
|
+
\u25A1 Monitor AWS security advisories (known vulnerabilities and patches)`,
|
|
4537
|
+
hwWarRoom: `\u26A0\uFE0F WAR-ROOM Real-Time Communication
|
|
4538
|
+
\u25A1 Create dedicated communication channels for the drill period (Teams/Slack/Chime)
|
|
4539
|
+
\u25A1 Establish WAR-ROOM connection with AWS TAM (Enterprise Support customers)
|
|
4540
|
+
\u25A1 Standardize case title format: "[CyberDrill] + Issue Description"`,
|
|
4541
|
+
hwCredentials: `\u26A0\uFE0F Password & Credential Management
|
|
4542
|
+
\u25A1 All IAM users must have MFA enabled
|
|
4543
|
+
\u25A1 Access key rotation cycle \u2264 90 days
|
|
4544
|
+
\u25A1 Avoid shared account usage
|
|
4545
|
+
\u25A1 No plaintext passwords in S3/Lambda/application code`,
|
|
4546
|
+
hwPostOptimization: `\u26A0\uFE0F Post-Drill Optimization
|
|
4547
|
+
\u25A1 Address and remediate each item from the attack report
|
|
4548
|
+
\u25A1 Establish periodic security maintenance processes with the security team
|
|
4549
|
+
\u25A1 Continuously fill security risk gaps`,
|
|
4550
|
+
hwReference: "Reference: AWS Cyber Defense Drill Standard Operation Procedure (Compliance IEM)",
|
|
4551
|
+
// Service Reminders
|
|
4552
|
+
serviceReminderTitle: "\u26A1 The following security services are not enabled; some checks cannot be performed:",
|
|
4553
|
+
serviceReminderFooter: "Re-scan after enabling the above services for a more complete security assessment.",
|
|
4554
|
+
serviceImpact: "Impact",
|
|
4555
|
+
serviceAction: "Action",
|
|
4556
|
+
// Common
|
|
4557
|
+
account: "Account",
|
|
4558
|
+
region: "Region",
|
|
4559
|
+
scanTime: "Scan Time",
|
|
4560
|
+
duration: "Duration",
|
|
4561
|
+
severityDistribution: "Severity Distribution",
|
|
4562
|
+
findingsByModule: "Findings by Module",
|
|
4563
|
+
details: "Details",
|
|
4564
|
+
// Extended \u2014 HTML Security Report extras
|
|
4565
|
+
topHighestRiskFindings: (n) => `Top ${n} Highest Risk Findings`,
|
|
4566
|
+
resource: "Resource",
|
|
4567
|
+
impact: "Impact",
|
|
4568
|
+
riskScore: "Risk Score",
|
|
4569
|
+
showRemainingFindings: (n) => `Show remaining ${n} findings\u2026`,
|
|
4570
|
+
trendTitle: "30-Day Trends",
|
|
4571
|
+
findingsBySeverity: "Findings by Severity",
|
|
4572
|
+
showMoreCount: (n) => `Show ${n} more\u2026`,
|
|
4573
|
+
// Extended \u2014 MLPS extras
|
|
4574
|
+
// Markdown report
|
|
4575
|
+
executiveSummary: "Executive Summary",
|
|
4576
|
+
totalFindingsLabel: "Total Findings",
|
|
4577
|
+
description: "Description",
|
|
4578
|
+
priority: "Priority",
|
|
4579
|
+
noFindingsForSeverity: (severity) => `No ${severity.toLowerCase()} findings.`,
|
|
4580
|
+
preCheckOverview: "Pre-Check Overview",
|
|
4581
|
+
accountInfo: "Account Information",
|
|
4582
|
+
checkedCount: (total, clean, issues) => `Checked: ${total} items (No issues: ${clean} | Issues found: ${issues})`,
|
|
4583
|
+
uncheckedCount: (n) => `Not checked: ${n} items (corresponding scan modules not run)`,
|
|
4584
|
+
cloudProviderCount: (n) => `Cloud provider responsible: ${n} items`,
|
|
4585
|
+
manualReviewCount: (n) => `Manual review required: ${n} items`,
|
|
4586
|
+
naCount: (n) => `Not applicable: ${n} items`,
|
|
4587
|
+
naNote: (n) => `Not applicable: ${n} items (IoT/wireless networks/mobile terminals/ICS/trusted verification, etc.)`,
|
|
4588
|
+
unknownNote: (n) => `(${n} items not checked \u2014 corresponding scan modules not run)`,
|
|
4589
|
+
cloudItemsNote: (n) => `The following ${n} items are the responsibility of the AWS cloud platform and are outside the scope of this report per the shared responsibility model.`,
|
|
4590
|
+
mlpsFooterGenerated: (version) => `Generated by AWS Security MCP Server v${version}`,
|
|
4591
|
+
mlpsFooterDisclaimer: "This report is for evidence collection reference and does not include compliance determination. A complete MLPS assessment must be conducted by a certified assessment institution.",
|
|
4592
|
+
andMore: (n) => `\u2026 and ${n} more`,
|
|
4593
|
+
remediationByPriority: "Remediation Items (by Priority)",
|
|
4594
|
+
affectedResources: (n) => `${n} resource${n === 1 ? "" : "s"} affected`,
|
|
4595
|
+
installWindowsPatches: (n, kbs) => `Install ${n} Windows patch${n === 1 ? "" : "es"} (${kbs})`,
|
|
4596
|
+
mlpsCategorySection: {
|
|
4597
|
+
"\u5B89\u5168\u7269\u7406\u73AF\u5883": "I. Physical Environment Security",
|
|
4598
|
+
"\u5B89\u5168\u901A\u4FE1\u7F51\u7EDC": "II. Communication Network Security",
|
|
4599
|
+
"\u5B89\u5168\u533A\u57DF\u8FB9\u754C": "III. Area Boundary Security",
|
|
4600
|
+
"\u5B89\u5168\u8BA1\u7B97\u73AF\u5883": "IV. Computing Environment Security",
|
|
4601
|
+
"\u5B89\u5168\u7BA1\u7406\u4E2D\u5FC3": "V. Security Management Center"
|
|
4602
|
+
},
|
|
4603
|
+
// Service Recommendations
|
|
4604
|
+
notEnabled: "Not Enabled",
|
|
4605
|
+
serviceRecommendations: {
|
|
4606
|
+
security_hub_findings: {
|
|
4607
|
+
icon: "\u{1F534}",
|
|
4608
|
+
service: "Security Hub",
|
|
4609
|
+
impact: "Cannot obtain 300+ automated security checks (FSBP/CIS/PCI DSS standards)",
|
|
4610
|
+
action: "Enable Security Hub for the most comprehensive security posture assessment"
|
|
4611
|
+
},
|
|
4612
|
+
guardduty_findings: {
|
|
4613
|
+
icon: "\u{1F534}",
|
|
4614
|
+
service: "GuardDuty",
|
|
4615
|
+
impact: "Cannot detect threat activity (malicious IPs, anomalous API calls, crypto mining, etc.)",
|
|
4616
|
+
action: "Enable GuardDuty for continuous threat detection"
|
|
4617
|
+
},
|
|
4618
|
+
inspector_findings: {
|
|
4619
|
+
icon: "\u{1F7E1}",
|
|
4620
|
+
service: "Inspector",
|
|
4621
|
+
impact: "Cannot scan EC2/Lambda/container software vulnerabilities (CVEs)",
|
|
4622
|
+
action: "Enable Inspector to discover known security vulnerabilities"
|
|
4623
|
+
},
|
|
4624
|
+
trusted_advisor_findings: {
|
|
4625
|
+
icon: "\u{1F7E1}",
|
|
4626
|
+
service: "Trusted Advisor",
|
|
4627
|
+
impact: "Cannot obtain AWS best practice security checks",
|
|
4628
|
+
action: "Upgrade to Business/Enterprise Support plan to use Trusted Advisor security checks"
|
|
4629
|
+
},
|
|
4630
|
+
config_rules_findings: {
|
|
4631
|
+
icon: "\u{1F7E1}",
|
|
4632
|
+
service: "AWS Config",
|
|
4633
|
+
impact: "Cannot check resource configuration compliance status",
|
|
4634
|
+
action: "Enable AWS Config and configure Config Rules"
|
|
4635
|
+
},
|
|
4636
|
+
access_analyzer_findings: {
|
|
4637
|
+
icon: "\u{1F7E1}",
|
|
4638
|
+
service: "IAM Access Analyzer",
|
|
4639
|
+
impact: "Cannot detect whether resources are accessed by external accounts or public networks",
|
|
4640
|
+
action: "Create IAM Access Analyzer (account-level or organization-level)"
|
|
4641
|
+
},
|
|
4642
|
+
patch_compliance_findings: {
|
|
4643
|
+
icon: "\u{1F7E1}",
|
|
4644
|
+
service: "SSM Patch Manager",
|
|
4645
|
+
impact: "Cannot check instance patch compliance status",
|
|
4646
|
+
action: "Install SSM Agent and configure Patch Manager"
|
|
4647
|
+
}
|
|
4648
|
+
},
|
|
4649
|
+
// HW Checklist (full composite)
|
|
4650
|
+
hwChecklist: `
|
|
4651
|
+
\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
|
|
4652
|
+
\u{1F4CB} Cyber Defense Drill Supplementary Reminders (Beyond Automated Scanning)
|
|
4653
|
+
\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
|
|
4654
|
+
|
|
4655
|
+
The following items require manual verification and execution:
|
|
4656
|
+
|
|
4657
|
+
\u26A0\uFE0F Emergency Isolation / Incident Response Plan
|
|
4658
|
+
\u25A1 Prepare dedicated isolation security groups (no Inbound/Outbound rules)
|
|
4659
|
+
\u25A1 Establish instance isolation SOP: Alert \u2192 Investigate \u2192 Block attacker IP \u2192 Network isolation \u2192 Security response \u2192 Log attack details
|
|
4660
|
+
\u25A1 Define emergency response procedures for each system (production core/non-core/test/dev)
|
|
4661
|
+
\u25A1 Identify responsible personnel and contacts for each project account and resource
|
|
4662
|
+
|
|
4663
|
+
\u26A0\uFE0F Test/Development Environment Handling
|
|
4664
|
+
\u25A1 Shut down non-critical systems during the drill period
|
|
4665
|
+
\u25A1 Shut down test/dev environments or maintain same security baseline as production
|
|
4666
|
+
\u25A1 Confirm which environments can be emergency-stopped to prevent attack propagation
|
|
4667
|
+
|
|
4668
|
+
\u26A0\uFE0F On-Duty Team Formation
|
|
4669
|
+
\u25A1 7\xD724 monitoring and rapid response team
|
|
4670
|
+
\u25A1 Technical and risk analysis team
|
|
4671
|
+
\u25A1 Security policy deployment team
|
|
4672
|
+
\u25A1 Business response team
|
|
4673
|
+
\u25A1 Confirm AWS TAM/Support contact information (ES/EOP customers)
|
|
4674
|
+
|
|
4675
|
+
\u26A0\uFE0F Ingress/Egress Path Architecture Diagram
|
|
4676
|
+
\u25A1 Ensure all Internet/DX dedicated line ingress/egress paths are clearly marked in architecture diagrams
|
|
4677
|
+
\u25A1 Clarify data flow for each ELB/Public EC2/S3/DX
|
|
4678
|
+
\u25A1 Identify all internet-facing data interaction interfaces
|
|
4679
|
+
|
|
4680
|
+
\u26A0\uFE0F Proactive Penetration Testing
|
|
4681
|
+
\u25A1 Contact security vendors for simulated attack drills before the exercise
|
|
4682
|
+
\u25A1 Conduct security hardening based on penetration test reports
|
|
4683
|
+
\u25A1 Monitor AWS security advisories (known vulnerabilities and patches)
|
|
4684
|
+
|
|
4685
|
+
\u26A0\uFE0F WAR-ROOM Real-Time Communication
|
|
4686
|
+
\u25A1 Create dedicated communication channels for the drill period (Teams/Slack/Chime)
|
|
4687
|
+
\u25A1 Establish WAR-ROOM connection with AWS TAM (Enterprise Support customers)
|
|
4688
|
+
\u25A1 Standardize case title format: "[CyberDrill] + Issue Description"
|
|
4689
|
+
|
|
4690
|
+
\u26A0\uFE0F Password & Credential Management
|
|
4691
|
+
\u25A1 All IAM users must have MFA enabled
|
|
4692
|
+
\u25A1 Access key rotation cycle \u2264 90 days
|
|
4693
|
+
\u25A1 Avoid shared account usage
|
|
4694
|
+
\u25A1 No plaintext passwords in S3/Lambda/application code
|
|
4695
|
+
|
|
4696
|
+
\u26A0\uFE0F Post-Drill Optimization
|
|
4697
|
+
\u25A1 Address and remediate each item from the attack report
|
|
4698
|
+
\u25A1 Establish periodic security maintenance processes with the security team
|
|
4699
|
+
\u25A1 Continuously fill security risk gaps
|
|
4700
|
+
|
|
4701
|
+
Reference: AWS Cyber Defense Drill Standard Operation Procedure (Compliance IEM)
|
|
4702
|
+
`
|
|
4703
|
+
};
|
|
4704
|
+
|
|
4705
|
+
// src/i18n/index.ts
|
|
4706
|
+
var translations = {
|
|
4707
|
+
zh: zhI18n,
|
|
4708
|
+
en: enI18n
|
|
4709
|
+
};
|
|
4710
|
+
function getI18n(lang = "zh") {
|
|
4711
|
+
return translations[lang] ?? translations.zh;
|
|
4712
|
+
}
|
|
4713
|
+
|
|
4200
4714
|
// src/tools/report-tool.ts
|
|
4201
4715
|
var SEVERITY_ICON = {
|
|
4202
4716
|
CRITICAL: "\u{1F534}",
|
|
@@ -4214,38 +4728,45 @@ function formatDuration(start, end) {
|
|
|
4214
4728
|
const remainSecs = secs % 60;
|
|
4215
4729
|
return `${mins}m ${remainSecs}s`;
|
|
4216
4730
|
}
|
|
4217
|
-
function
|
|
4218
|
-
const
|
|
4219
|
-
return [
|
|
4220
|
-
`#### ${f.title}`,
|
|
4221
|
-
`- **Resource:** ${f.resourceId} (\`${f.resourceArn}\`)`,
|
|
4222
|
-
`- **Description:** ${f.description}`,
|
|
4223
|
-
`- **Impact:** ${f.impact}`,
|
|
4224
|
-
`- **Risk Score:** ${f.riskScore}/10`,
|
|
4225
|
-
`- **Remediation:**`,
|
|
4226
|
-
steps,
|
|
4227
|
-
`- **Priority:** ${f.priority}`
|
|
4228
|
-
].join("\n");
|
|
4229
|
-
}
|
|
4230
|
-
function generateMarkdownReport(scanResults) {
|
|
4731
|
+
function generateMarkdownReport(scanResults, lang) {
|
|
4732
|
+
const t = getI18n(lang ?? "zh");
|
|
4231
4733
|
const { summary, modules, accountId, region, scanStart, scanEnd } = scanResults;
|
|
4232
4734
|
const date = scanStart.split("T")[0];
|
|
4233
4735
|
const duration = formatDuration(scanStart, scanEnd);
|
|
4736
|
+
const sevLabel = {
|
|
4737
|
+
CRITICAL: t.critical,
|
|
4738
|
+
HIGH: t.high,
|
|
4739
|
+
MEDIUM: t.medium,
|
|
4740
|
+
LOW: t.low
|
|
4741
|
+
};
|
|
4742
|
+
function renderFinding(f) {
|
|
4743
|
+
const steps = f.remediationSteps.map((s, i) => ` ${i + 1}. ${s}`).join("\n");
|
|
4744
|
+
return [
|
|
4745
|
+
`#### ${f.title}`,
|
|
4746
|
+
`- **${t.resource}:** ${f.resourceId} (\`${f.resourceArn}\`)`,
|
|
4747
|
+
`- **${t.description}:** ${f.description}`,
|
|
4748
|
+
`- **${t.impact}:** ${f.impact}`,
|
|
4749
|
+
`- **${t.riskScore}:** ${f.riskScore}/10`,
|
|
4750
|
+
`- **${t.remediation}:**`,
|
|
4751
|
+
steps,
|
|
4752
|
+
`- **${t.priority}:** ${f.priority}`
|
|
4753
|
+
].join("\n");
|
|
4754
|
+
}
|
|
4234
4755
|
const lines = [];
|
|
4235
|
-
lines.push(`#
|
|
4756
|
+
lines.push(`# ${t.securityReportTitle} \u2014 ${date}`);
|
|
4236
4757
|
lines.push("");
|
|
4237
|
-
lines.push(
|
|
4238
|
-
lines.push(`-
|
|
4239
|
-
lines.push(`-
|
|
4240
|
-
lines.push(`-
|
|
4758
|
+
lines.push(`## ${t.executiveSummary}`);
|
|
4759
|
+
lines.push(`- **${t.account}:** ${accountId}`);
|
|
4760
|
+
lines.push(`- **${t.region}:** ${region}`);
|
|
4761
|
+
lines.push(`- **${t.duration}:** ${duration}`);
|
|
4241
4762
|
lines.push(
|
|
4242
|
-
`-
|
|
4763
|
+
`- **${t.totalFindingsLabel}:** ${summary.totalFindings} (${SEVERITY_ICON.CRITICAL} ${summary.critical} ${t.critical} | ${SEVERITY_ICON.HIGH} ${summary.high} ${t.high} | ${SEVERITY_ICON.MEDIUM} ${summary.medium} ${t.medium} | ${SEVERITY_ICON.LOW} ${summary.low} ${t.low})`
|
|
4243
4764
|
);
|
|
4244
4765
|
lines.push("");
|
|
4245
4766
|
if (summary.totalFindings === 0) {
|
|
4246
|
-
lines.push(
|
|
4767
|
+
lines.push(`## ${t.findingsBySeverity}`);
|
|
4247
4768
|
lines.push("");
|
|
4248
|
-
lines.push(
|
|
4769
|
+
lines.push(`\u2705 ${t.noIssuesFound}`);
|
|
4249
4770
|
lines.push("");
|
|
4250
4771
|
} else {
|
|
4251
4772
|
const allFindings = modules.flatMap((m) => m.findings);
|
|
@@ -4256,15 +4777,15 @@ function generateMarkdownReport(scanResults) {
|
|
|
4256
4777
|
for (const f of allFindings) {
|
|
4257
4778
|
grouped.get(f.severity).push(f);
|
|
4258
4779
|
}
|
|
4259
|
-
lines.push(
|
|
4780
|
+
lines.push(`## ${t.findingsBySeverity}`);
|
|
4260
4781
|
lines.push("");
|
|
4261
4782
|
for (const sev of SEVERITY_ORDER) {
|
|
4262
4783
|
const findings = grouped.get(sev);
|
|
4263
4784
|
const icon = SEVERITY_ICON[sev];
|
|
4264
|
-
lines.push(`### ${icon} ${sev
|
|
4785
|
+
lines.push(`### ${icon} ${sevLabel[sev]}`);
|
|
4265
4786
|
lines.push("");
|
|
4266
4787
|
if (findings.length === 0) {
|
|
4267
|
-
lines.push(
|
|
4788
|
+
lines.push(t.noFindingsForSeverity(sevLabel[sev]));
|
|
4268
4789
|
lines.push("");
|
|
4269
4790
|
continue;
|
|
4270
4791
|
}
|
|
@@ -4275,9 +4796,9 @@ function generateMarkdownReport(scanResults) {
|
|
|
4275
4796
|
}
|
|
4276
4797
|
}
|
|
4277
4798
|
}
|
|
4278
|
-
lines.push(
|
|
4799
|
+
lines.push(`## ${t.scanStatistics}`);
|
|
4279
4800
|
lines.push(
|
|
4280
|
-
|
|
4801
|
+
`| ${t.module} | ${t.resources} | ${t.findings} | ${t.status} |`
|
|
4281
4802
|
);
|
|
4282
4803
|
lines.push("|--------|------------------|----------|--------|");
|
|
4283
4804
|
for (const m of modules) {
|
|
@@ -4290,7 +4811,7 @@ function generateMarkdownReport(scanResults) {
|
|
|
4290
4811
|
if (summary.totalFindings > 0) {
|
|
4291
4812
|
const allFindings = modules.flatMap((m) => m.findings);
|
|
4292
4813
|
allFindings.sort((a, b) => b.riskScore - a.riskScore);
|
|
4293
|
-
lines.push(
|
|
4814
|
+
lines.push(`## ${t.recommendations}`);
|
|
4294
4815
|
for (let i = 0; i < allFindings.length; i++) {
|
|
4295
4816
|
const f = allFindings[i];
|
|
4296
4817
|
lines.push(`${i + 1}. [${f.priority}] ${f.title}: ${f.remediationSteps[0] ?? "Review and remediate."}`);
|
|
@@ -6337,13 +6858,6 @@ var MLPS3_CATEGORY_ORDER = [
|
|
|
6337
6858
|
"\u5B89\u5168\u8BA1\u7B97\u73AF\u5883",
|
|
6338
6859
|
"\u5B89\u5168\u7BA1\u7406\u4E2D\u5FC3"
|
|
6339
6860
|
];
|
|
6340
|
-
var MLPS3_CATEGORY_SECTION = {
|
|
6341
|
-
"\u5B89\u5168\u7269\u7406\u73AF\u5883": "\u4E00\u3001\u5B89\u5168\u7269\u7406\u73AF\u5883",
|
|
6342
|
-
"\u5B89\u5168\u901A\u4FE1\u7F51\u7EDC": "\u4E8C\u3001\u5B89\u5168\u901A\u4FE1\u7F51\u7EDC",
|
|
6343
|
-
"\u5B89\u5168\u533A\u57DF\u8FB9\u754C": "\u4E09\u3001\u5B89\u5168\u533A\u57DF\u8FB9\u754C",
|
|
6344
|
-
"\u5B89\u5168\u8BA1\u7B97\u73AF\u5883": "\u56DB\u3001\u5B89\u5168\u8BA1\u7B97\u73AF\u5883",
|
|
6345
|
-
"\u5B89\u5168\u7BA1\u7406\u4E2D\u5FC3": "\u4E94\u3001\u5B89\u5168\u7BA1\u7406\u4E2D\u5FC3"
|
|
6346
|
-
};
|
|
6347
6861
|
var MLPS3_CHECK_MAPPING = [
|
|
6348
6862
|
// =========================================================================
|
|
6349
6863
|
// 安全物理环境 — L3-PES1-* (22 items) → cloud_provider
|
|
@@ -6908,9 +7422,8 @@ var MLPS3_CHECK_MAPPING = [
|
|
|
6908
7422
|
},
|
|
6909
7423
|
{
|
|
6910
7424
|
id: "L3-SMC1-09",
|
|
6911
|
-
type: "
|
|
6912
|
-
|
|
6913
|
-
findingPatterns: ["CloudWatch"]
|
|
7425
|
+
type: "manual",
|
|
7426
|
+
guidance: "\u9700\u914D\u7F6E CloudWatch \u96C6\u4E2D\u76D1\u63A7\u5E73\u53F0\uFF0C\u7ED3\u5408 SNS \u8FDB\u884C\u544A\u8B66\u901A\u77E5"
|
|
6914
7427
|
},
|
|
6915
7428
|
{
|
|
6916
7429
|
id: "L3-SMC1-10",
|
|
@@ -7004,232 +7517,68 @@ function evaluateAllFullChecks(scanResults) {
|
|
|
7004
7517
|
return evaluateFullCheck(item, mapping, allFindings, scanModules);
|
|
7005
7518
|
});
|
|
7006
7519
|
}
|
|
7007
|
-
|
|
7008
|
-
|
|
7009
|
-
|
|
7010
|
-
id: "8.1.4.1a",
|
|
7011
|
-
category: "\u8EAB\u4EFD\u9274\u522B",
|
|
7012
|
-
name: "\u5BC6\u7801\u7B56\u7565",
|
|
7013
|
-
modules: ["security_hub_findings"],
|
|
7014
|
-
findingPatterns: ["password policy", "password length", "complexity", "password expiry", "reuse prevention", "IAM.7", "IAM.10"]
|
|
7015
|
-
},
|
|
7016
|
-
{
|
|
7017
|
-
id: "8.1.4.1a",
|
|
7018
|
-
category: "\u8EAB\u4EFD\u9274\u522B",
|
|
7019
|
-
name: "\u5BC6\u94A5\u8F6E\u6362",
|
|
7020
|
-
modules: ["security_hub_findings"],
|
|
7021
|
-
findingPatterns: ["access key older", "access key rotated", "IAM.3", "IAM.4"]
|
|
7022
|
-
},
|
|
7023
|
-
{
|
|
7024
|
-
id: "8.1.4.1d",
|
|
7025
|
-
category: "\u8EAB\u4EFD\u9274\u522B",
|
|
7026
|
-
name: "\u53CC\u56E0\u7D20\u8BA4\u8BC1",
|
|
7027
|
-
modules: ["security_hub_findings"],
|
|
7028
|
-
findingPatterns: ["MFA", "IAM.5", "IAM.6"]
|
|
7029
|
-
},
|
|
7030
|
-
// 二、访问控制
|
|
7031
|
-
{
|
|
7032
|
-
id: "8.1.4.2c",
|
|
7033
|
-
category: "\u8BBF\u95EE\u63A7\u5236",
|
|
7034
|
-
name: "\u6700\u5C0F\u6743\u9650",
|
|
7035
|
-
modules: ["iam_privilege_escalation", "security_hub_findings"],
|
|
7036
|
-
findingPatterns: [
|
|
7037
|
-
"AdministratorAccess",
|
|
7038
|
-
"PowerUserAccess",
|
|
7039
|
-
"IAMFullAccess",
|
|
7040
|
-
"over-permissive",
|
|
7041
|
-
"privilege escalation",
|
|
7042
|
-
"self-grant",
|
|
7043
|
-
"iam:*",
|
|
7044
|
-
"create admin",
|
|
7045
|
-
"Lambda role passing",
|
|
7046
|
-
"CreateAccessKey",
|
|
7047
|
-
"AssumeRole"
|
|
7048
|
-
]
|
|
7049
|
-
},
|
|
7050
|
-
{
|
|
7051
|
-
id: "8.1.4.2",
|
|
7052
|
-
category: "\u8BBF\u95EE\u63A7\u5236",
|
|
7053
|
-
name: "\u5B89\u5168\u7EC4",
|
|
7054
|
-
modules: ["network_reachability", "security_hub_findings"],
|
|
7055
|
-
findingPatterns: ["allows all ports", "allows SSH", "allows RDP", "MySQL", "PostgreSQL", "MongoDB", "Redis", "high-risk port", "security group", "EC2.18", "EC2.19"]
|
|
7056
|
-
},
|
|
7057
|
-
// 三、安全审计
|
|
7058
|
-
{
|
|
7059
|
-
id: "8.1.4.3a",
|
|
7060
|
-
category: "\u5B89\u5168\u5BA1\u8BA1",
|
|
7061
|
-
name: "\u5BA1\u8BA1\u529F\u80FD",
|
|
7062
|
-
modules: ["security_hub_findings"],
|
|
7063
|
-
findingPatterns: ["CloudTrail", "not enabled", "multi-region", "not logging", "CloudTrail.1"]
|
|
7064
|
-
},
|
|
7065
|
-
{
|
|
7066
|
-
id: "8.1.4.3b",
|
|
7067
|
-
category: "\u5B89\u5168\u5BA1\u8BA1",
|
|
7068
|
-
name: "\u5BA1\u8BA1\u5B8C\u6574\u6027",
|
|
7069
|
-
modules: ["security_hub_findings"],
|
|
7070
|
-
findingPatterns: ["log file validation", "log integrity", "log validation", "CloudTrail.4", "CloudTrail.5"]
|
|
7071
|
-
},
|
|
7072
|
-
{
|
|
7073
|
-
id: "8.1.4.3c",
|
|
7074
|
-
category: "\u5B89\u5168\u5BA1\u8BA1",
|
|
7075
|
-
name: "\u5BA1\u8BA1\u4FDD\u62A4",
|
|
7076
|
-
modules: ["security_hub_findings"],
|
|
7077
|
-
findingPatterns: ["CloudTrail", "S3 bucket", "encryption", "versioning", "Block Public Access", "CloudTrail.6", "CloudTrail.7"]
|
|
7078
|
-
},
|
|
7079
|
-
// 四、入侵防范
|
|
7080
|
-
{
|
|
7081
|
-
id: "8.1.4.4a",
|
|
7082
|
-
category: "\u5165\u4FB5\u9632\u8303",
|
|
7083
|
-
name: "GuardDuty \u5A01\u80C1\u68C0\u6D4B",
|
|
7084
|
-
modules: ["service_detection", "guardduty_findings"],
|
|
7085
|
-
findingPatterns: ["GuardDuty"]
|
|
7086
|
-
},
|
|
7087
|
-
{
|
|
7088
|
-
id: "8.1.4.4a",
|
|
7089
|
-
category: "\u5165\u4FB5\u9632\u8303",
|
|
7090
|
-
name: "Inspector \u6F0F\u6D1E\u626B\u63CF",
|
|
7091
|
-
modules: ["service_detection", "inspector_findings"],
|
|
7092
|
-
findingPatterns: ["Inspector", "CVE-"]
|
|
7093
|
-
},
|
|
7094
|
-
// 五、数据安全
|
|
7095
|
-
{
|
|
7096
|
-
id: "8.1.4.5a",
|
|
7097
|
-
category: "\u6570\u636E\u5B89\u5168",
|
|
7098
|
-
name: "\u4F20\u8F93\u52A0\u5BC6",
|
|
7099
|
-
modules: ["ssl_certificate", "security_hub_findings"],
|
|
7100
|
-
findingPatterns: ["HTTPS", "TLS", "HTTP listener", "certificate", "ELB.1"]
|
|
7101
|
-
},
|
|
7102
|
-
{
|
|
7103
|
-
id: "8.1.4.5b",
|
|
7104
|
-
category: "\u6570\u636E\u5B89\u5168",
|
|
7105
|
-
name: "S3 \u5B58\u50A8\u52A0\u5BC6",
|
|
7106
|
-
modules: ["security_hub_findings"],
|
|
7107
|
-
findingPatterns: ["no default encryption", "not encrypted", "S3.4"]
|
|
7108
|
-
},
|
|
7109
|
-
{
|
|
7110
|
-
id: "8.1.4.5b",
|
|
7111
|
-
category: "\u6570\u636E\u5B89\u5168",
|
|
7112
|
-
name: "EBS \u9ED8\u8BA4\u52A0\u5BC6",
|
|
7113
|
-
modules: ["security_hub_findings"],
|
|
7114
|
-
findingPatterns: ["EBS default encryption", "EC2.7"]
|
|
7115
|
-
},
|
|
7116
|
-
{
|
|
7117
|
-
id: "8.1.4.5b",
|
|
7118
|
-
category: "\u6570\u636E\u5B89\u5168",
|
|
7119
|
-
name: "RDS \u5B58\u50A8\u52A0\u5BC6",
|
|
7120
|
-
modules: ["security_hub_findings"],
|
|
7121
|
-
findingPatterns: ["storage is not encrypted", "RDS.3"]
|
|
7122
|
-
},
|
|
7123
|
-
// 六、网络安全
|
|
7124
|
-
{
|
|
7125
|
-
id: "8.1.3.1a",
|
|
7126
|
-
category: "\u7F51\u7EDC\u5B89\u5168",
|
|
7127
|
-
name: "\u7F51\u7EDC\u67B6\u6784",
|
|
7128
|
-
modules: ["security_hub_findings"],
|
|
7129
|
-
findingPatterns: ["default VPC", "EC2.2"]
|
|
7130
|
-
},
|
|
7131
|
-
{
|
|
7132
|
-
id: "8.1.3.2a",
|
|
7133
|
-
category: "\u7F51\u7EDC\u5B89\u5168",
|
|
7134
|
-
name: "\u8FB9\u754C\u9632\u62A4",
|
|
7135
|
-
modules: ["network_reachability", "security_hub_findings"],
|
|
7136
|
-
findingPatterns: ["allows all ports", "allows SSH", "allows RDP", "security group", "EC2.18", "EC2.19"]
|
|
7137
|
-
}
|
|
7138
|
-
];
|
|
7139
|
-
var CATEGORY_ORDER = [
|
|
7140
|
-
"\u8EAB\u4EFD\u9274\u522B",
|
|
7141
|
-
"\u8BBF\u95EE\u63A7\u5236",
|
|
7142
|
-
"\u5B89\u5168\u5BA1\u8BA1",
|
|
7143
|
-
"\u5165\u4FB5\u9632\u8303",
|
|
7144
|
-
"\u6570\u636E\u5B89\u5168",
|
|
7145
|
-
"\u7F51\u7EDC\u5B89\u5168"
|
|
7146
|
-
];
|
|
7147
|
-
var CATEGORY_SECTION = {
|
|
7148
|
-
"\u8EAB\u4EFD\u9274\u522B": "\u4E00\u3001\u8EAB\u4EFD\u9274\u522B",
|
|
7149
|
-
"\u8BBF\u95EE\u63A7\u5236": "\u4E8C\u3001\u8BBF\u95EE\u63A7\u5236",
|
|
7150
|
-
"\u5B89\u5168\u5BA1\u8BA1": "\u4E09\u3001\u5B89\u5168\u5BA1\u8BA1",
|
|
7151
|
-
"\u5165\u4FB5\u9632\u8303": "\u56DB\u3001\u5165\u4FB5\u9632\u8303",
|
|
7152
|
-
"\u6570\u636E\u5B89\u5168": "\u4E94\u3001\u6570\u636E\u5B89\u5168",
|
|
7153
|
-
"\u7F51\u7EDC\u5B89\u5168": "\u516D\u3001\u7F51\u7EDC\u5B89\u5168"
|
|
7154
|
-
};
|
|
7155
|
-
function evaluateCheck(check, allFindings, scanModules) {
|
|
7156
|
-
const allModulesPresent = check.modules.every(
|
|
7157
|
-
(mod) => scanModules.some((m) => m.module === mod && m.status === "success")
|
|
7158
|
-
);
|
|
7159
|
-
if (!allModulesPresent) {
|
|
7160
|
-
return { check, status: "unknown", relatedFindings: [] };
|
|
7161
|
-
}
|
|
7162
|
-
const relatedFindings = allFindings.filter((f) => {
|
|
7163
|
-
const moduleMatch = check.modules.some((mod) => f.module === mod);
|
|
7164
|
-
if (!moduleMatch) return false;
|
|
7165
|
-
const text = `${f.title} ${f.description}`.toLowerCase();
|
|
7166
|
-
return check.findingPatterns.some(
|
|
7167
|
-
(pattern) => text.includes(pattern.toLowerCase())
|
|
7168
|
-
);
|
|
7169
|
-
});
|
|
7170
|
-
return {
|
|
7171
|
-
check,
|
|
7172
|
-
status: relatedFindings.length === 0 ? "clean" : "issues",
|
|
7173
|
-
relatedFindings
|
|
7174
|
-
};
|
|
7175
|
-
}
|
|
7176
|
-
function generateMlps3Report(scanResults) {
|
|
7520
|
+
function generateMlps3Report(scanResults, lang) {
|
|
7521
|
+
const t = getI18n(lang ?? "zh");
|
|
7522
|
+
const isEn = (lang ?? "zh") === "en";
|
|
7177
7523
|
const { accountId, region, scanStart } = scanResults;
|
|
7178
7524
|
const scanTime = scanStart.replace("T", " ").replace(/\.\d+Z$/, " UTC");
|
|
7179
|
-
const
|
|
7180
|
-
|
|
7181
|
-
);
|
|
7182
|
-
const
|
|
7183
|
-
|
|
7184
|
-
|
|
7185
|
-
|
|
7186
|
-
const
|
|
7187
|
-
|
|
7188
|
-
);
|
|
7189
|
-
const
|
|
7190
|
-
const issuesCount = results.filter((r) => r.status === "issues").length;
|
|
7191
|
-
const unknownCount = results.filter((r) => r.status === "unknown").length;
|
|
7192
|
-
const checkedTotal = cleanCount + issuesCount;
|
|
7193
|
-
const total = results.length;
|
|
7525
|
+
const results = evaluateAllFullChecks(scanResults);
|
|
7526
|
+
const autoResults = results.filter((r) => r.mapping.type === "auto");
|
|
7527
|
+
const autoClean = autoResults.filter((r) => r.status === "clean").length;
|
|
7528
|
+
const autoIssues = autoResults.filter((r) => r.status === "issues").length;
|
|
7529
|
+
const autoUnknown = autoResults.filter((r) => r.status === "unknown").length;
|
|
7530
|
+
const checkedTotal = autoClean + autoIssues;
|
|
7531
|
+
const cloudCount = results.filter((r) => r.status === "cloud_provider").length;
|
|
7532
|
+
const manualCount = results.filter((r) => r.status === "manual").length;
|
|
7533
|
+
const naCount = results.filter((r) => r.status === "not_applicable").length;
|
|
7534
|
+
const itemControl = (r) => isEn ? r.item.controlEn : r.item.controlCn;
|
|
7535
|
+
const itemReq = (r) => isEn ? r.item.requirementEn : r.item.requirementCn;
|
|
7194
7536
|
const lines = [];
|
|
7195
|
-
lines.push(
|
|
7196
|
-
lines.push(
|
|
7537
|
+
lines.push(`# ${t.mlpsTitle}`);
|
|
7538
|
+
lines.push(`> **${t.mlpsDisclaimer}**`);
|
|
7197
7539
|
lines.push("");
|
|
7198
|
-
lines.push(
|
|
7199
|
-
lines.push(`-
|
|
7540
|
+
lines.push(`## ${t.accountInfo}`);
|
|
7541
|
+
lines.push(`- ${t.account}: ${accountId} | ${t.region}: ${region} | ${t.scanTime}: ${scanTime}`);
|
|
7200
7542
|
lines.push("");
|
|
7201
|
-
lines.push(
|
|
7202
|
-
lines.push(`-
|
|
7203
|
-
|
|
7204
|
-
|
|
7205
|
-
|
|
7206
|
-
|
|
7543
|
+
lines.push(`## ${t.preCheckOverview}`);
|
|
7544
|
+
lines.push(`- ${t.checkedCount(checkedTotal, autoClean, autoIssues)}`);
|
|
7545
|
+
if (autoUnknown > 0) {
|
|
7546
|
+
lines.push(`- ${t.uncheckedCount(autoUnknown)}`);
|
|
7547
|
+
}
|
|
7548
|
+
lines.push(`- ${t.cloudProviderCount(cloudCount)}`);
|
|
7549
|
+
lines.push(`- ${t.manualReviewCount(manualCount)}`);
|
|
7550
|
+
if (naCount > 0) {
|
|
7551
|
+
lines.push(`- ${t.naCount(naCount)}`);
|
|
7207
7552
|
}
|
|
7208
7553
|
lines.push("");
|
|
7209
|
-
for (const category of
|
|
7210
|
-
const sectionTitle =
|
|
7211
|
-
const
|
|
7212
|
-
|
|
7554
|
+
for (const category of MLPS3_CATEGORY_ORDER) {
|
|
7555
|
+
const sectionTitle = t.mlpsCategorySection[category] ?? category;
|
|
7556
|
+
const catResults = results.filter(
|
|
7557
|
+
(r) => r.item.categoryCn === category && r.status !== "not_applicable"
|
|
7558
|
+
);
|
|
7559
|
+
if (catResults.length === 0) continue;
|
|
7213
7560
|
lines.push(`## ${sectionTitle}`);
|
|
7214
7561
|
lines.push("");
|
|
7215
|
-
const
|
|
7216
|
-
for (const r of
|
|
7217
|
-
const
|
|
7218
|
-
|
|
7219
|
-
|
|
7562
|
+
const controlMap = /* @__PURE__ */ new Map();
|
|
7563
|
+
for (const r of catResults) {
|
|
7564
|
+
const key = r.item.controlCn;
|
|
7565
|
+
if (!controlMap.has(key)) controlMap.set(key, []);
|
|
7566
|
+
controlMap.get(key).push(r);
|
|
7220
7567
|
}
|
|
7221
|
-
for (const [
|
|
7222
|
-
|
|
7223
|
-
|
|
7224
|
-
|
|
7225
|
-
const
|
|
7226
|
-
|
|
7568
|
+
for (const [_controlKey, controlResults] of controlMap) {
|
|
7569
|
+
const controlName = itemControl(controlResults[0]);
|
|
7570
|
+
lines.push(`### ${controlName}`);
|
|
7571
|
+
for (const r of controlResults) {
|
|
7572
|
+
const icon = r.status === "clean" ? "\u2705" : r.status === "issues" ? "\u274C" : r.status === "unknown" ? "\u26A0\uFE0F" : r.status === "manual" ? "\u{1F4CB}" : "\u{1F3E2}";
|
|
7573
|
+
const suffix = r.status === "unknown" ? ` \u2014 ${t.notChecked}` : r.status === "manual" ? ` \u2014 ${r.mapping.guidance ?? t.manualReview}` : r.status === "cloud_provider" ? ` \u2014 ${r.mapping.note ?? t.cloudProvider}` : r.status === "clean" ? ` ${t.noIssues}` : ` ${t.issuesFound}`;
|
|
7574
|
+
const reqText = itemReq(r);
|
|
7575
|
+
lines.push(`- [${icon}] ${r.item.id} ${reqText.slice(0, 60)}${reqText.length > 60 ? "\u2026" : ""}${suffix}`);
|
|
7227
7576
|
if (r.status === "issues" && r.relatedFindings.length > 0) {
|
|
7228
7577
|
for (const f of r.relatedFindings.slice(0, 3)) {
|
|
7229
7578
|
lines.push(` - ${f.severity}: ${f.title}`);
|
|
7230
7579
|
}
|
|
7231
7580
|
if (r.relatedFindings.length > 3) {
|
|
7232
|
-
lines.push(` -
|
|
7581
|
+
lines.push(` - ${t.andMore(r.relatedFindings.length - 3)}`);
|
|
7233
7582
|
}
|
|
7234
7583
|
}
|
|
7235
7584
|
}
|
|
@@ -7238,7 +7587,7 @@ function generateMlps3Report(scanResults) {
|
|
|
7238
7587
|
}
|
|
7239
7588
|
const failedResults = results.filter((r) => r.status === "issues");
|
|
7240
7589
|
if (failedResults.length > 0) {
|
|
7241
|
-
lines.push(
|
|
7590
|
+
lines.push(`## ${t.remediationByPriority}`);
|
|
7242
7591
|
lines.push("");
|
|
7243
7592
|
const allFailedFindings = /* @__PURE__ */ new Map();
|
|
7244
7593
|
for (const r of failedResults) {
|
|
@@ -7260,6 +7609,10 @@ function generateMlps3Report(scanResults) {
|
|
|
7260
7609
|
}
|
|
7261
7610
|
lines.push("");
|
|
7262
7611
|
}
|
|
7612
|
+
if (naCount > 0) {
|
|
7613
|
+
lines.push(`> ${t.naNote(naCount)}`);
|
|
7614
|
+
lines.push("");
|
|
7615
|
+
}
|
|
7263
7616
|
return lines.join("\n");
|
|
7264
7617
|
}
|
|
7265
7618
|
|
|
@@ -7267,6 +7620,15 @@ function generateMlps3Report(scanResults) {
|
|
|
7267
7620
|
function esc(s) {
|
|
7268
7621
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
7269
7622
|
}
|
|
7623
|
+
function escWithLinks(s) {
|
|
7624
|
+
const parts = s.split(/(https?:\/\/\S+)/);
|
|
7625
|
+
return parts.map((part, i) => {
|
|
7626
|
+
if (i % 2 === 1) {
|
|
7627
|
+
return `<a href="${esc(part)}" style="color:#60a5fa" target="_blank" rel="noopener">${esc(part)}</a>`;
|
|
7628
|
+
}
|
|
7629
|
+
return esc(part);
|
|
7630
|
+
}).join("");
|
|
7631
|
+
}
|
|
7270
7632
|
function calcScore(summary) {
|
|
7271
7633
|
const raw = 100 - summary.critical * 15 - summary.high * 5 - summary.medium * 2 - summary.low * 0.5;
|
|
7272
7634
|
return Math.max(0, Math.min(100, Math.round(raw)));
|
|
@@ -7290,50 +7652,6 @@ function scoreColor(score) {
|
|
|
7290
7652
|
if (score >= 50) return "#eab308";
|
|
7291
7653
|
return "#ef4444";
|
|
7292
7654
|
}
|
|
7293
|
-
var SERVICE_RECOMMENDATIONS = {
|
|
7294
|
-
security_hub_findings: {
|
|
7295
|
-
icon: "\u{1F534}",
|
|
7296
|
-
service: "Security Hub",
|
|
7297
|
-
impact: "\u65E0\u6CD5\u83B7\u53D6 300+ \u9879\u81EA\u52A8\u5316\u5B89\u5168\u68C0\u67E5\uFF08FSBP/CIS/PCI DSS \u6807\u51C6\uFF09",
|
|
7298
|
-
action: "\u542F\u7528 Security Hub \u83B7\u5F97\u6700\u5168\u9762\u7684\u5B89\u5168\u6001\u52BF\u8BC4\u4F30"
|
|
7299
|
-
},
|
|
7300
|
-
guardduty_findings: {
|
|
7301
|
-
icon: "\u{1F534}",
|
|
7302
|
-
service: "GuardDuty",
|
|
7303
|
-
impact: "\u65E0\u6CD5\u68C0\u6D4B\u5A01\u80C1\u6D3B\u52A8\uFF08\u6076\u610F IP\u3001\u5F02\u5E38 API \u8C03\u7528\u3001\u52A0\u5BC6\u8D27\u5E01\u6316\u77FF\u7B49\uFF09",
|
|
7304
|
-
action: "\u542F\u7528 GuardDuty \u83B7\u5F97\u6301\u7EED\u5A01\u80C1\u68C0\u6D4B\u80FD\u529B"
|
|
7305
|
-
},
|
|
7306
|
-
inspector_findings: {
|
|
7307
|
-
icon: "\u{1F7E1}",
|
|
7308
|
-
service: "Inspector",
|
|
7309
|
-
impact: "\u65E0\u6CD5\u626B\u63CF EC2/Lambda/\u5BB9\u5668\u7684\u8F6F\u4EF6\u6F0F\u6D1E\uFF08CVE\uFF09",
|
|
7310
|
-
action: "\u542F\u7528 Inspector \u53D1\u73B0\u5DF2\u77E5\u5B89\u5168\u6F0F\u6D1E"
|
|
7311
|
-
},
|
|
7312
|
-
trusted_advisor_findings: {
|
|
7313
|
-
icon: "\u{1F7E1}",
|
|
7314
|
-
service: "Trusted Advisor",
|
|
7315
|
-
impact: "\u65E0\u6CD5\u83B7\u53D6 AWS \u6700\u4F73\u5B9E\u8DF5\u5B89\u5168\u68C0\u67E5",
|
|
7316
|
-
action: "\u5347\u7EA7\u81F3 Business/Enterprise Support \u8BA1\u5212\u4EE5\u4F7F\u7528 Trusted Advisor \u5B89\u5168\u68C0\u67E5"
|
|
7317
|
-
},
|
|
7318
|
-
config_rules_findings: {
|
|
7319
|
-
icon: "\u{1F7E1}",
|
|
7320
|
-
service: "AWS Config",
|
|
7321
|
-
impact: "\u65E0\u6CD5\u68C0\u67E5\u8D44\u6E90\u914D\u7F6E\u5408\u89C4\u72B6\u6001",
|
|
7322
|
-
action: "\u542F\u7528 AWS Config \u5E76\u914D\u7F6E Config Rules"
|
|
7323
|
-
},
|
|
7324
|
-
access_analyzer_findings: {
|
|
7325
|
-
icon: "\u{1F7E1}",
|
|
7326
|
-
service: "IAM Access Analyzer",
|
|
7327
|
-
impact: "\u65E0\u6CD5\u68C0\u6D4B\u8D44\u6E90\u662F\u5426\u88AB\u5916\u90E8\u8D26\u53F7\u6216\u516C\u7F51\u8BBF\u95EE",
|
|
7328
|
-
action: "\u521B\u5EFA IAM Access Analyzer\uFF08\u8D26\u6237\u7EA7\u6216\u7EC4\u7EC7\u7EA7\uFF09"
|
|
7329
|
-
},
|
|
7330
|
-
patch_compliance_findings: {
|
|
7331
|
-
icon: "\u{1F7E1}",
|
|
7332
|
-
service: "SSM Patch Manager",
|
|
7333
|
-
impact: "\u65E0\u6CD5\u68C0\u67E5\u5B9E\u4F8B\u8865\u4E01\u5408\u89C4\u72B6\u6001",
|
|
7334
|
-
action: "\u5B89\u88C5 SSM Agent \u5E76\u914D\u7F6E Patch Manager"
|
|
7335
|
-
}
|
|
7336
|
-
};
|
|
7337
7655
|
var SERVICE_NOT_ENABLED_PATTERNS = [
|
|
7338
7656
|
"not enabled",
|
|
7339
7657
|
"not found",
|
|
@@ -7343,10 +7661,11 @@ var SERVICE_NOT_ENABLED_PATTERNS = [
|
|
|
7343
7661
|
"not available",
|
|
7344
7662
|
"is not enabled"
|
|
7345
7663
|
];
|
|
7346
|
-
function getDisabledServices(modules) {
|
|
7664
|
+
function getDisabledServices(modules, lang) {
|
|
7665
|
+
const t = getI18n(lang ?? "zh");
|
|
7347
7666
|
const disabled = [];
|
|
7348
7667
|
for (const mod of modules) {
|
|
7349
|
-
const rec =
|
|
7668
|
+
const rec = t.serviceRecommendations[mod.module];
|
|
7350
7669
|
if (!rec) continue;
|
|
7351
7670
|
if (!mod.warnings?.length) continue;
|
|
7352
7671
|
const hasNotEnabled = mod.warnings.some(
|
|
@@ -7358,21 +7677,22 @@ function getDisabledServices(modules) {
|
|
|
7358
7677
|
}
|
|
7359
7678
|
return disabled;
|
|
7360
7679
|
}
|
|
7361
|
-
function buildServiceReminderHtml(modules) {
|
|
7362
|
-
const
|
|
7680
|
+
function buildServiceReminderHtml(modules, lang) {
|
|
7681
|
+
const t = getI18n(lang ?? "zh");
|
|
7682
|
+
const disabled = getDisabledServices(modules, lang);
|
|
7363
7683
|
if (disabled.length === 0) return "";
|
|
7364
7684
|
const items = disabled.map((svc) => `
|
|
7365
7685
|
<div style="margin-bottom:12px">
|
|
7366
|
-
<div style="font-weight:600;font-size:15px">${esc(svc.icon)} ${esc(svc.service)}
|
|
7367
|
-
<div style="margin-left:28px;color:#cbd5e1;font-size:13px"
|
|
7368
|
-
<div style="margin-left:28px;color:#cbd5e1;font-size:13px"
|
|
7686
|
+
<div style="font-weight:600;font-size:15px">${esc(svc.icon)} ${esc(svc.service)} ${esc(t.notEnabled)}</div>
|
|
7687
|
+
<div style="margin-left:28px;color:#cbd5e1;font-size:13px">${esc(t.serviceImpact)}\uFF1A${esc(svc.impact)}</div>
|
|
7688
|
+
<div style="margin-left:28px;color:#cbd5e1;font-size:13px">${esc(t.serviceAction)}\uFF1A${esc(svc.action)}</div>
|
|
7369
7689
|
</div>`).join("\n");
|
|
7370
7690
|
return `
|
|
7371
7691
|
<section>
|
|
7372
7692
|
<div style="background:#2d1f00;border:1px solid #b45309;border-radius:8px;padding:20px;margin-bottom:32px">
|
|
7373
|
-
<div style="font-size:17px;font-weight:700;margin-bottom:12px"
|
|
7693
|
+
<div style="font-size:17px;font-weight:700;margin-bottom:12px">${esc(t.serviceReminderTitle)}</div>
|
|
7374
7694
|
${items}
|
|
7375
|
-
<div style="margin-top:12px;font-size:13px;color:#fbbf24;font-weight:500"
|
|
7695
|
+
<div style="margin-top:12px;font-size:13px;color:#fbbf24;font-weight:500">${esc(t.serviceReminderFooter)}</div>
|
|
7376
7696
|
</div>
|
|
7377
7697
|
</section>`;
|
|
7378
7698
|
}
|
|
@@ -7574,12 +7894,12 @@ function donutChart(summary) {
|
|
|
7574
7894
|
"</svg>"
|
|
7575
7895
|
].join("\n");
|
|
7576
7896
|
}
|
|
7577
|
-
function barChart(modules) {
|
|
7897
|
+
function barChart(modules, allCleanLabel = "All modules clean") {
|
|
7578
7898
|
const withFindings = modules.filter((m) => m.findingsCount > 0).sort((a, b) => b.findingsCount - a.findingsCount).slice(0, 12);
|
|
7579
7899
|
if (withFindings.length === 0) {
|
|
7580
7900
|
return [
|
|
7581
7901
|
'<svg viewBox="0 0 400 50" width="100%">',
|
|
7582
|
-
|
|
7902
|
+
` <text x="200" y="30" text-anchor="middle" fill="#22c55e" font-size="14" font-weight="600">${esc(allCleanLabel)}</text>`,
|
|
7583
7903
|
"</svg>"
|
|
7584
7904
|
].join("\n");
|
|
7585
7905
|
}
|
|
@@ -7694,7 +8014,9 @@ function scoreTrendChart(history) {
|
|
|
7694
8014
|
"</svg>"
|
|
7695
8015
|
].join("\n");
|
|
7696
8016
|
}
|
|
7697
|
-
function generateHtmlReport(scanResults, history) {
|
|
8017
|
+
function generateHtmlReport(scanResults, history, lang) {
|
|
8018
|
+
const t = getI18n(lang ?? "zh");
|
|
8019
|
+
const htmlLang = (lang ?? "zh") === "zh" ? "zh-CN" : "en";
|
|
7698
8020
|
const { summary, modules, accountId, region, scanStart, scanEnd } = scanResults;
|
|
7699
8021
|
const date = scanStart.split("T")[0];
|
|
7700
8022
|
const duration = formatDuration2(scanStart, scanEnd);
|
|
@@ -7712,23 +8034,23 @@ function generateHtmlReport(scanResults, history) {
|
|
|
7712
8034
|
<div class="top5-content">
|
|
7713
8035
|
<span class="badge badge-${esc(f.severity.toLowerCase())}">${esc(f.severity)}</span>
|
|
7714
8036
|
<div class="top5-title">${esc(f.title)}</div>
|
|
7715
|
-
<div class="top5-detail"><strong
|
|
7716
|
-
<div class="top5-detail"><strong
|
|
7717
|
-
<div class="top5-detail"><strong
|
|
7718
|
-
<h4
|
|
7719
|
-
<ol class="top5-remediation">${f.remediationSteps.map((s) => `<li>${
|
|
8037
|
+
<div class="top5-detail"><strong>${t.resource}:</strong> ${esc(f.resourceId)}</div>
|
|
8038
|
+
<div class="top5-detail"><strong>${t.impact}:</strong> ${esc(f.impact)}</div>
|
|
8039
|
+
<div class="top5-detail"><strong>${t.riskScore}:</strong> ${f.riskScore}/10</div>
|
|
8040
|
+
<h4>${t.remediation}</h4>
|
|
8041
|
+
<ol class="top5-remediation">${f.remediationSteps.map((s) => `<li>${escWithLinks(s)}</li>`).join("")}</ol>
|
|
7720
8042
|
</div>
|
|
7721
8043
|
</div>`
|
|
7722
8044
|
).join("\n");
|
|
7723
8045
|
top5Html = `
|
|
7724
8046
|
<section>
|
|
7725
|
-
<h2
|
|
8047
|
+
<h2>${esc(t.topHighestRiskFindings(top5.length))}</h2>
|
|
7726
8048
|
${cards}
|
|
7727
8049
|
</section>`;
|
|
7728
8050
|
}
|
|
7729
8051
|
let findingsHtml;
|
|
7730
8052
|
if (summary.totalFindings === 0) {
|
|
7731
|
-
findingsHtml =
|
|
8053
|
+
findingsHtml = `<div class="no-findings">${esc(t.noIssuesFound)}</div>`;
|
|
7732
8054
|
} else {
|
|
7733
8055
|
const FOLD_THRESHOLD = 20;
|
|
7734
8056
|
const renderCard = (f) => {
|
|
@@ -7737,10 +8059,10 @@ function generateHtmlReport(scanResults, history) {
|
|
|
7737
8059
|
<span class="badge badge-${esc(sev)}">${esc(f.severity)}</span>
|
|
7738
8060
|
<span class="finding-title-text">${esc(f.title)}</span>
|
|
7739
8061
|
<span class="finding-resource">${esc(f.resourceArn || f.resourceId)}</span>
|
|
7740
|
-
<details><summary
|
|
8062
|
+
<details><summary>${t.details}</summary><div class="finding-card-body">
|
|
7741
8063
|
<p>${esc(f.description)}</p>
|
|
7742
|
-
<p><strong
|
|
7743
|
-
<ol>${f.remediationSteps.map((s) => `<li>${
|
|
8064
|
+
<p><strong>${t.remediation}:</strong></p>
|
|
8065
|
+
<ol>${f.remediationSteps.map((s) => `<li>${escWithLinks(s)}</li>`).join("")}</ol>
|
|
7744
8066
|
</div></details>
|
|
7745
8067
|
</div>`;
|
|
7746
8068
|
};
|
|
@@ -7751,7 +8073,7 @@ function generateHtmlReport(scanResults, history) {
|
|
|
7751
8073
|
const first = findings.slice(0, FOLD_THRESHOLD).map(renderCard).join("\n");
|
|
7752
8074
|
const rest = findings.slice(FOLD_THRESHOLD).map(renderCard).join("\n");
|
|
7753
8075
|
return `${first}
|
|
7754
|
-
<details><summary
|
|
8076
|
+
<details><summary>${t.showRemainingFindings(findings.length - FOLD_THRESHOLD)}</summary>
|
|
7755
8077
|
${rest}
|
|
7756
8078
|
</details>`;
|
|
7757
8079
|
};
|
|
@@ -7803,13 +8125,13 @@ ${rest}
|
|
|
7803
8125
|
if (history && history.length >= 2) {
|
|
7804
8126
|
trendHtml = `
|
|
7805
8127
|
<section class="trend-section">
|
|
7806
|
-
<h2
|
|
8128
|
+
<h2>${esc(t.trendTitle)}</h2>
|
|
7807
8129
|
<div class="trend-chart">
|
|
7808
|
-
<div class="trend-title"
|
|
8130
|
+
<div class="trend-title">${esc(t.findingsBySeverity)}</div>
|
|
7809
8131
|
${findingsTrendChart(history)}
|
|
7810
8132
|
</div>
|
|
7811
8133
|
<div class="trend-chart">
|
|
7812
|
-
<div class="trend-title"
|
|
8134
|
+
<div class="trend-title">${esc(t.securityScore)}</div>
|
|
7813
8135
|
${scoreTrendChart(history)}
|
|
7814
8136
|
</div>
|
|
7815
8137
|
</section>`;
|
|
@@ -7820,16 +8142,57 @@ ${rest}
|
|
|
7820
8142
|
let recsHtml = "";
|
|
7821
8143
|
if (summary.totalFindings > 0) {
|
|
7822
8144
|
const recMap = /* @__PURE__ */ new Map();
|
|
8145
|
+
const kbPatches = [];
|
|
8146
|
+
let kbSeverity = "LOW";
|
|
8147
|
+
let kbUrl;
|
|
8148
|
+
const genericPatterns = ["See References", "None Provided", "Review the finding", "Review and remediate."];
|
|
7823
8149
|
for (const f of allFindings) {
|
|
7824
8150
|
const rem = f.remediationSteps[0] ?? "Review and remediate.";
|
|
8151
|
+
const url = f.remediationSteps.find((s) => s.startsWith("Documentation:"))?.replace("Documentation: ", "");
|
|
8152
|
+
if (genericPatterns.some((p) => rem.startsWith(p))) continue;
|
|
8153
|
+
const kbMatch = f.title.match(/KB\d+/);
|
|
8154
|
+
if (kbMatch && (f.module === "security_hub_findings" || f.module === "inspector_findings")) {
|
|
8155
|
+
kbPatches.push(kbMatch[0]);
|
|
8156
|
+
if (SEVERITY_ORDER2.indexOf(f.severity) < SEVERITY_ORDER2.indexOf(kbSeverity)) kbSeverity = f.severity;
|
|
8157
|
+
if (!kbUrl && url) kbUrl = url;
|
|
8158
|
+
continue;
|
|
8159
|
+
}
|
|
8160
|
+
if (f.module === "security_hub_findings") {
|
|
8161
|
+
const controlMatch = f.title.match(/^([A-Z][A-Za-z0-9]*\.\d+)\s/);
|
|
8162
|
+
if (controlMatch) {
|
|
8163
|
+
const controlId = controlMatch[1];
|
|
8164
|
+
const key = `ctrl:${controlId}`;
|
|
8165
|
+
const existing2 = recMap.get(key);
|
|
8166
|
+
if (existing2) {
|
|
8167
|
+
existing2.count++;
|
|
8168
|
+
if (!existing2.url && url) existing2.url = url;
|
|
8169
|
+
if (SEVERITY_ORDER2.indexOf(f.severity) < SEVERITY_ORDER2.indexOf(existing2.severity)) existing2.severity = f.severity;
|
|
8170
|
+
} else {
|
|
8171
|
+
recMap.set(key, { text: `[${controlId}] ${rem}`, severity: f.severity, count: 1, url });
|
|
8172
|
+
}
|
|
8173
|
+
continue;
|
|
8174
|
+
}
|
|
8175
|
+
}
|
|
7825
8176
|
const existing = recMap.get(rem);
|
|
7826
8177
|
if (existing) {
|
|
7827
8178
|
existing.count++;
|
|
8179
|
+
if (!existing.url && url) existing.url = url;
|
|
7828
8180
|
if (SEVERITY_ORDER2.indexOf(f.severity) < SEVERITY_ORDER2.indexOf(existing.severity)) {
|
|
7829
8181
|
existing.severity = f.severity;
|
|
7830
8182
|
}
|
|
7831
8183
|
} else {
|
|
7832
|
-
recMap.set(rem, { text: rem, severity: f.severity, count: 1 });
|
|
8184
|
+
recMap.set(rem, { text: rem, severity: f.severity, count: 1, url });
|
|
8185
|
+
}
|
|
8186
|
+
}
|
|
8187
|
+
if (kbPatches.length > 0) {
|
|
8188
|
+
const unique = [...new Set(kbPatches)];
|
|
8189
|
+
const kbList = unique.slice(0, 5).join(", ") + (unique.length > 5 ? ", \u2026" : "");
|
|
8190
|
+
recMap.set("__kb__", { text: t.installWindowsPatches(unique.length, kbList), severity: kbSeverity, count: 1, url: kbUrl });
|
|
8191
|
+
}
|
|
8192
|
+
for (const [key, rec] of recMap) {
|
|
8193
|
+
if (key.startsWith("ctrl:") && rec.count > 1) {
|
|
8194
|
+
rec.text += ` \u2014 ${t.affectedResources(rec.count)}`;
|
|
8195
|
+
rec.count = 1;
|
|
7833
8196
|
}
|
|
7834
8197
|
}
|
|
7835
8198
|
const uniqueRecs = [...recMap.values()].sort((a, b) => {
|
|
@@ -7840,60 +8203,61 @@ ${rest}
|
|
|
7840
8203
|
const renderRec = (r) => {
|
|
7841
8204
|
const sev = r.severity.toLowerCase();
|
|
7842
8205
|
const countLabel = r.count > 1 ? ` (× ${r.count})` : "";
|
|
7843
|
-
|
|
8206
|
+
const linkHtml = r.url ? ` <a href="${esc(r.url)}" style="color:#60a5fa" target="_blank" rel="noopener">📖</a>` : "";
|
|
8207
|
+
return `<li><span class="badge badge-${esc(sev)}">${esc(r.severity)}</span> ${esc(r.text)}${countLabel}${linkHtml}</li>`;
|
|
7844
8208
|
};
|
|
7845
8209
|
const TOP_N = 10;
|
|
7846
8210
|
const topItems = uniqueRecs.slice(0, TOP_N).map(renderRec).join("\n");
|
|
7847
8211
|
const remaining = uniqueRecs.slice(TOP_N);
|
|
7848
8212
|
const moreHtml = remaining.length > 0 ? `
|
|
7849
|
-
<details><summary
|
|
8213
|
+
<details><summary>${t.showMoreCount(remaining.length)}</summary>
|
|
7850
8214
|
${remaining.map(renderRec).join("\n")}
|
|
7851
8215
|
</details>` : "";
|
|
7852
8216
|
recsHtml = `
|
|
7853
8217
|
<details class="rec-fold">
|
|
7854
|
-
<summary><h2 style="margin:0;border:0;display:inline"
|
|
8218
|
+
<summary><h2 style="margin:0;border:0;display:inline">${esc(t.recommendations)} (${uniqueRecs.length} ${esc(t.unique)})</h2></summary>
|
|
7855
8219
|
<div class="rec-body">
|
|
7856
8220
|
<ol>${topItems}${moreHtml}</ol>
|
|
7857
8221
|
</div>
|
|
7858
8222
|
</details>`;
|
|
7859
8223
|
}
|
|
7860
8224
|
return `<!DOCTYPE html>
|
|
7861
|
-
<html lang="
|
|
8225
|
+
<html lang="${htmlLang}">
|
|
7862
8226
|
<head>
|
|
7863
8227
|
<meta charset="UTF-8">
|
|
7864
8228
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
7865
|
-
<title
|
|
8229
|
+
<title>${esc(t.securityReportTitle)} — ${esc(date)}</title>
|
|
7866
8230
|
<style>${sharedCss()}</style>
|
|
7867
8231
|
</head>
|
|
7868
8232
|
<body>
|
|
7869
8233
|
<div class="container">
|
|
7870
8234
|
|
|
7871
8235
|
<header>
|
|
7872
|
-
<h1>🛡️
|
|
7873
|
-
<div class="meta"
|
|
8236
|
+
<h1>🛡️ ${esc(t.securityReportTitle)}</h1>
|
|
8237
|
+
<div class="meta">${esc(t.account)}: ${esc(accountId)} | ${esc(t.region)}: ${esc(region)} | ${esc(date)} | ${esc(t.duration)}: ${esc(duration)}</div>
|
|
7874
8238
|
</header>
|
|
7875
8239
|
|
|
7876
8240
|
<section class="summary">
|
|
7877
8241
|
<div class="score-card">
|
|
7878
8242
|
<div class="score-value" style="color:${scoreColor(score)}">${score}</div>
|
|
7879
|
-
<div class="score-label"
|
|
8243
|
+
<div class="score-label">${esc(t.securityScore)}</div>
|
|
7880
8244
|
</div>
|
|
7881
8245
|
<div class="severity-stats">
|
|
7882
|
-
<div class="stat-card stat-critical"><div class="stat-count">${summary.critical}</div><div class="stat-label"
|
|
7883
|
-
<div class="stat-card stat-high"><div class="stat-count">${summary.high}</div><div class="stat-label"
|
|
7884
|
-
<div class="stat-card stat-medium"><div class="stat-count">${summary.medium}</div><div class="stat-label"
|
|
7885
|
-
<div class="stat-card stat-low"><div class="stat-count">${summary.low}</div><div class="stat-label"
|
|
8246
|
+
<div class="stat-card stat-critical"><div class="stat-count">${summary.critical}</div><div class="stat-label">${esc(t.critical)}</div></div>
|
|
8247
|
+
<div class="stat-card stat-high"><div class="stat-count">${summary.high}</div><div class="stat-label">${esc(t.high)}</div></div>
|
|
8248
|
+
<div class="stat-card stat-medium"><div class="stat-count">${summary.medium}</div><div class="stat-label">${esc(t.medium)}</div></div>
|
|
8249
|
+
<div class="stat-card stat-low"><div class="stat-count">${summary.low}</div><div class="stat-label">${esc(t.low)}</div></div>
|
|
7886
8250
|
</div>
|
|
7887
8251
|
</section>
|
|
7888
8252
|
|
|
7889
8253
|
<section class="charts">
|
|
7890
8254
|
<div class="chart-box">
|
|
7891
|
-
<div class="chart-title"
|
|
8255
|
+
<div class="chart-title">${esc(t.severityDistribution)}</div>
|
|
7892
8256
|
<div style="text-align:center">${donutChart(summary)}</div>
|
|
7893
8257
|
</div>
|
|
7894
8258
|
<div class="chart-box">
|
|
7895
|
-
<div class="chart-title"
|
|
7896
|
-
${barChart(modules)}
|
|
8259
|
+
<div class="chart-title">${esc(t.findingsByModule)}</div>
|
|
8260
|
+
${barChart(modules, t.allModulesClean)}
|
|
7897
8261
|
</div>
|
|
7898
8262
|
</section>
|
|
7899
8263
|
|
|
@@ -7901,33 +8265,35 @@ ${trendHtml}
|
|
|
7901
8265
|
|
|
7902
8266
|
${top5Html}
|
|
7903
8267
|
|
|
7904
|
-
${buildServiceReminderHtml(modules)}
|
|
8268
|
+
${buildServiceReminderHtml(modules, lang)}
|
|
7905
8269
|
|
|
7906
8270
|
<section>
|
|
7907
|
-
<h2
|
|
8271
|
+
<h2>${esc(t.scanStatistics)}</h2>
|
|
7908
8272
|
<table>
|
|
7909
|
-
<thead><tr><th
|
|
8273
|
+
<thead><tr><th>${esc(t.module)}</th><th>${esc(t.resources)}</th><th>${esc(t.findings)}</th><th>${esc(t.status)}</th></tr></thead>
|
|
7910
8274
|
<tbody>${statsRows}</tbody>
|
|
7911
8275
|
</table>
|
|
7912
8276
|
</section>
|
|
7913
8277
|
|
|
7914
8278
|
<section>
|
|
7915
|
-
<h2
|
|
8279
|
+
<h2>${esc(t.allFindings)}</h2>
|
|
7916
8280
|
${findingsHtml}
|
|
7917
8281
|
</section>
|
|
7918
8282
|
|
|
7919
8283
|
${recsHtml}
|
|
7920
8284
|
|
|
7921
8285
|
<footer>
|
|
7922
|
-
<p
|
|
7923
|
-
<p
|
|
8286
|
+
<p>${esc(t.generatedBy)} v${VERSION}</p>
|
|
8287
|
+
<p>${esc(t.informationalOnly)}</p>
|
|
7924
8288
|
</footer>
|
|
7925
8289
|
|
|
7926
8290
|
</div>
|
|
7927
8291
|
</body>
|
|
7928
8292
|
</html>`;
|
|
7929
8293
|
}
|
|
7930
|
-
function generateMlps3HtmlReport(scanResults, history) {
|
|
8294
|
+
function generateMlps3HtmlReport(scanResults, history, lang) {
|
|
8295
|
+
const t = getI18n(lang ?? "zh");
|
|
8296
|
+
const htmlLang = (lang ?? "zh") === "zh" ? "zh-CN" : "en";
|
|
7931
8297
|
const { accountId, region, scanStart } = scanResults;
|
|
7932
8298
|
const date = scanStart.split("T")[0];
|
|
7933
8299
|
const scanTime = scanStart.replace("T", " ").replace(/\.\d+Z$/, " UTC");
|
|
@@ -7944,17 +8310,21 @@ function generateMlps3HtmlReport(scanResults, history) {
|
|
|
7944
8310
|
if (history && history.length >= 2) {
|
|
7945
8311
|
trendHtml = `
|
|
7946
8312
|
<section class="trend-section">
|
|
7947
|
-
<h2
|
|
8313
|
+
<h2>${esc(t.trendTitle)}</h2>
|
|
7948
8314
|
<div class="trend-chart">
|
|
7949
|
-
<div class="trend-title"
|
|
8315
|
+
<div class="trend-title">${esc(t.findingsBySeverity)}</div>
|
|
7950
8316
|
${findingsTrendChart(history)}
|
|
7951
8317
|
</div>
|
|
7952
8318
|
<div class="trend-chart">
|
|
7953
|
-
<div class="trend-title"
|
|
8319
|
+
<div class="trend-title">${esc(t.securityScore)}</div>
|
|
7954
8320
|
${scoreTrendChart(history)}
|
|
7955
8321
|
</div>
|
|
7956
8322
|
</section>`;
|
|
7957
8323
|
}
|
|
8324
|
+
const isEn = (lang ?? "zh") === "en";
|
|
8325
|
+
const itemCat = (r) => isEn ? r.item.categoryEn : r.item.categoryCn;
|
|
8326
|
+
const itemControl = (r) => isEn ? r.item.controlEn : r.item.controlCn;
|
|
8327
|
+
const itemReq = (r) => isEn ? r.item.requirementEn : r.item.requirementCn;
|
|
7958
8328
|
const categoryMap = /* @__PURE__ */ new Map();
|
|
7959
8329
|
for (const r of results) {
|
|
7960
8330
|
if (r.status === "not_applicable") continue;
|
|
@@ -7963,7 +8333,7 @@ function generateMlps3HtmlReport(scanResults, history) {
|
|
|
7963
8333
|
categoryMap.get(cat).push(r);
|
|
7964
8334
|
}
|
|
7965
8335
|
const categorySections = MLPS3_CATEGORY_ORDER.map((category) => {
|
|
7966
|
-
const sectionTitle =
|
|
8336
|
+
const sectionTitle = t.mlpsCategorySection[category] ?? category;
|
|
7967
8337
|
const catResults = categoryMap.get(category);
|
|
7968
8338
|
if (!catResults || catResults.length === 0) return "";
|
|
7969
8339
|
const allCloud = catResults.every((r) => r.status === "cloud_provider");
|
|
@@ -7971,11 +8341,11 @@ function generateMlps3HtmlReport(scanResults, history) {
|
|
|
7971
8341
|
return `<details class="category-fold mlps-cloud-section">
|
|
7972
8342
|
<summary>
|
|
7973
8343
|
<span class="category-title">${esc(sectionTitle)}</span>
|
|
7974
|
-
<span class="category-stats"><span class="category-stat-cloud">\u{1F3E2} ${catResults.length}
|
|
8344
|
+
<span class="category-stats"><span class="category-stat-cloud">\u{1F3E2} ${catResults.length} ${esc(t.cloudProvider)}</span></span>
|
|
7975
8345
|
</summary>
|
|
7976
8346
|
<div class="category-body">
|
|
7977
|
-
<div class="mlps-cloud-note"
|
|
7978
|
-
${catResults.map((r) => `<div class="check-item check-cloud"><span class="check-icon">\u{1F3E2}</span><span class="check-name">${esc(r.item.id)} ${esc(r
|
|
8347
|
+
<div class="mlps-cloud-note">${esc(t.cloudItemsNote(catResults.length))}</div>
|
|
8348
|
+
${catResults.map((r) => `<div class="check-item check-cloud"><span class="check-icon">\u{1F3E2}</span><span class="check-name">${esc(r.item.id)} ${esc(itemControl(r))}</span><span class="check-note">${esc(r.mapping.note ?? "")}</span></div>`).join("\n")}
|
|
7979
8349
|
</div>
|
|
7980
8350
|
</details>`;
|
|
7981
8351
|
}
|
|
@@ -7997,31 +8367,34 @@ function generateMlps3HtmlReport(scanResults, history) {
|
|
|
7997
8367
|
if (!controlMap.has(key)) controlMap.set(key, []);
|
|
7998
8368
|
controlMap.get(key).push(r);
|
|
7999
8369
|
}
|
|
8000
|
-
const controlGroups = [...controlMap.entries()].map(([
|
|
8370
|
+
const controlGroups = [...controlMap.entries()].map(([_controlKey, controlResults]) => {
|
|
8371
|
+
const controlName = itemControl(controlResults[0]);
|
|
8001
8372
|
const cloudItems = controlResults.filter((r) => r.status === "cloud_provider");
|
|
8002
8373
|
const nonCloudItems = controlResults.filter((r) => r.status !== "cloud_provider");
|
|
8003
8374
|
let itemsHtml = "";
|
|
8004
8375
|
for (const r of nonCloudItems) {
|
|
8005
8376
|
const icon = r.status === "clean" ? "\u{1F7E2}" : r.status === "issues" ? "\u{1F534}" : r.status === "unknown" ? "\u2B1C" : r.status === "manual" ? "\u{1F4CB}" : "\u{1F3E2}";
|
|
8006
8377
|
const cls = `check-${r.status === "cloud_provider" ? "cloud" : r.status}`;
|
|
8007
|
-
const suffix = r.status === "unknown" ?
|
|
8378
|
+
const suffix = r.status === "unknown" ? ` \u2014 ${esc(t.notChecked)}` : r.status === "manual" ? ` \u2014 ${esc(r.mapping.guidance ?? t.manualReview)}` : "";
|
|
8008
8379
|
let findingsDetail = "";
|
|
8009
8380
|
if (r.status === "clean") {
|
|
8010
|
-
findingsDetail = `<div class="check-detail"
|
|
8381
|
+
findingsDetail = `<div class="check-detail">${esc(t.noRelatedIssues)}</div>`;
|
|
8011
8382
|
} else if (r.status === "issues" && r.relatedFindings.length > 0) {
|
|
8012
8383
|
const fItems = r.relatedFindings.slice(0, 5).map((f) => `<li>${esc(f.severity)}: ${esc(f.title)}</li>`);
|
|
8013
8384
|
if (r.relatedFindings.length > 5) {
|
|
8014
|
-
fItems.push(`<li
|
|
8385
|
+
fItems.push(`<li>${esc(t.andMore(r.relatedFindings.length - 5))}</li>`);
|
|
8015
8386
|
}
|
|
8016
|
-
const remediationHint = r.relatedFindings[0]?.remediationSteps?.[0] ? `<p style="color:#fbbf24;font-size:12px;margin-top:4px"
|
|
8017
|
-
findingsDetail = `<div class="check-findings-wrap"><details><summary
|
|
8387
|
+
const remediationHint = r.relatedFindings[0]?.remediationSteps?.[0] ? `<p style="color:#fbbf24;font-size:12px;margin-top:4px">${esc(t.remediation)}\uFF1A${escWithLinks(r.relatedFindings[0].remediationSteps[0])}</p>` : "";
|
|
8388
|
+
findingsDetail = `<div class="check-findings-wrap"><details><summary>${esc(t.issuesFoundCount(r.relatedFindings.length))}</summary><ul class="check-findings">${fItems.join("")}</ul>${remediationHint}</details></div>`;
|
|
8018
8389
|
}
|
|
8019
|
-
|
|
8390
|
+
const reqText = itemReq(r);
|
|
8391
|
+
itemsHtml += `<div class="check-item ${cls}"><span class="check-icon">${icon}</span><span class="check-name">${esc(r.item.id)} ${esc(reqText.slice(0, 60))}${reqText.length > 60 ? "\u2026" : ""}${suffix}</span></div>
|
|
8020
8392
|
${findingsDetail}`;
|
|
8021
8393
|
}
|
|
8022
8394
|
if (cloudItems.length > 0) {
|
|
8023
8395
|
for (const r of cloudItems) {
|
|
8024
|
-
|
|
8396
|
+
const reqText = itemReq(r);
|
|
8397
|
+
itemsHtml += `<div class="check-item check-cloud"><span class="check-icon">\u{1F3E2}</span><span class="check-name">${esc(r.item.id)} ${esc(reqText.slice(0, 50))}${reqText.length > 50 ? "\u2026" : ""}</span><span class="check-note">${esc(t.cloudProvider)}</span></div>
|
|
8025
8398
|
`;
|
|
8026
8399
|
}
|
|
8027
8400
|
}
|
|
@@ -8054,20 +8427,61 @@ ${itemsHtml}
|
|
|
8054
8427
|
let remediationHtml = "";
|
|
8055
8428
|
if (failedResults.length > 0) {
|
|
8056
8429
|
const mlpsRecMap = /* @__PURE__ */ new Map();
|
|
8430
|
+
const mlpsKbPatches = [];
|
|
8431
|
+
let mlpsKbSeverity = "LOW";
|
|
8432
|
+
let mlpsKbUrl;
|
|
8433
|
+
const mlpsGenericPatterns = ["See References", "None Provided", "Review the finding", "Review and remediate."];
|
|
8057
8434
|
for (const r of failedResults) {
|
|
8058
8435
|
for (const f of r.relatedFindings) {
|
|
8059
8436
|
const rem = f.remediationSteps[0] ?? "Review and remediate.";
|
|
8437
|
+
const url = f.remediationSteps.find((s) => s.startsWith("Documentation:"))?.replace("Documentation: ", "");
|
|
8438
|
+
if (mlpsGenericPatterns.some((p) => rem.startsWith(p))) continue;
|
|
8439
|
+
const kbMatch = f.title.match(/KB\d+/);
|
|
8440
|
+
if (kbMatch && (f.module === "security_hub_findings" || f.module === "inspector_findings")) {
|
|
8441
|
+
mlpsKbPatches.push(kbMatch[0]);
|
|
8442
|
+
if (SEVERITY_ORDER2.indexOf(f.severity) < SEVERITY_ORDER2.indexOf(mlpsKbSeverity)) mlpsKbSeverity = f.severity;
|
|
8443
|
+
if (!mlpsKbUrl && url) mlpsKbUrl = url;
|
|
8444
|
+
continue;
|
|
8445
|
+
}
|
|
8446
|
+
if (f.module === "security_hub_findings") {
|
|
8447
|
+
const controlMatch = f.title.match(/^([A-Z][A-Za-z0-9]*\.\d+)\s/);
|
|
8448
|
+
if (controlMatch) {
|
|
8449
|
+
const controlId = controlMatch[1];
|
|
8450
|
+
const key = `ctrl:${controlId}`;
|
|
8451
|
+
const existing2 = mlpsRecMap.get(key);
|
|
8452
|
+
if (existing2) {
|
|
8453
|
+
existing2.count++;
|
|
8454
|
+
if (!existing2.url && url) existing2.url = url;
|
|
8455
|
+
if (SEVERITY_ORDER2.indexOf(f.severity) < SEVERITY_ORDER2.indexOf(existing2.severity)) existing2.severity = f.severity;
|
|
8456
|
+
} else {
|
|
8457
|
+
mlpsRecMap.set(key, { text: `[${controlId}] ${rem}`, severity: f.severity, count: 1, url });
|
|
8458
|
+
}
|
|
8459
|
+
continue;
|
|
8460
|
+
}
|
|
8461
|
+
}
|
|
8060
8462
|
const existing = mlpsRecMap.get(rem);
|
|
8061
8463
|
if (existing) {
|
|
8062
8464
|
existing.count++;
|
|
8465
|
+
if (!existing.url && url) existing.url = url;
|
|
8063
8466
|
if (SEVERITY_ORDER2.indexOf(f.severity) < SEVERITY_ORDER2.indexOf(existing.severity)) {
|
|
8064
8467
|
existing.severity = f.severity;
|
|
8065
8468
|
}
|
|
8066
8469
|
} else {
|
|
8067
|
-
mlpsRecMap.set(rem, { text: rem, severity: f.severity, count: 1 });
|
|
8470
|
+
mlpsRecMap.set(rem, { text: rem, severity: f.severity, count: 1, url });
|
|
8068
8471
|
}
|
|
8069
8472
|
}
|
|
8070
8473
|
}
|
|
8474
|
+
if (mlpsKbPatches.length > 0) {
|
|
8475
|
+
const unique = [...new Set(mlpsKbPatches)];
|
|
8476
|
+
const kbList = unique.slice(0, 5).join(", ") + (unique.length > 5 ? ", \u2026" : "");
|
|
8477
|
+
mlpsRecMap.set("__kb__", { text: t.installWindowsPatches(unique.length, kbList), severity: mlpsKbSeverity, count: 1, url: mlpsKbUrl });
|
|
8478
|
+
}
|
|
8479
|
+
for (const [key, rec] of mlpsRecMap) {
|
|
8480
|
+
if (key.startsWith("ctrl:") && rec.count > 1) {
|
|
8481
|
+
rec.text += ` \u2014 ${t.affectedResources(rec.count)}`;
|
|
8482
|
+
rec.count = 1;
|
|
8483
|
+
}
|
|
8484
|
+
}
|
|
8071
8485
|
const mlpsUniqueRecs = [...mlpsRecMap.values()].sort((a, b) => {
|
|
8072
8486
|
const sevDiff = SEVERITY_ORDER2.indexOf(a.severity) - SEVERITY_ORDER2.indexOf(b.severity);
|
|
8073
8487
|
if (sevDiff !== 0) return sevDiff;
|
|
@@ -8077,26 +8491,27 @@ ${itemsHtml}
|
|
|
8077
8491
|
const renderMlpsRec = (r) => {
|
|
8078
8492
|
const sev = r.severity.toLowerCase();
|
|
8079
8493
|
const countLabel = r.count > 1 ? ` (× ${r.count})` : "";
|
|
8080
|
-
|
|
8494
|
+
const linkHtml = r.url ? ` <a href="${esc(r.url)}" style="color:#60a5fa" target="_blank" rel="noopener">📖</a>` : "";
|
|
8495
|
+
return `<li><span class="badge badge-${esc(sev)}">${esc(r.severity)}</span> ${esc(r.text)}${countLabel}${linkHtml}</li>`;
|
|
8081
8496
|
};
|
|
8082
8497
|
const MLPS_TOP_N = 10;
|
|
8083
8498
|
const mlpsTopItems = mlpsUniqueRecs.slice(0, MLPS_TOP_N).map(renderMlpsRec).join("\n");
|
|
8084
8499
|
const mlpsRemaining = mlpsUniqueRecs.slice(MLPS_TOP_N);
|
|
8085
8500
|
const mlpsMoreHtml = mlpsRemaining.length > 0 ? `
|
|
8086
|
-
<details><summary
|
|
8501
|
+
<details><summary>${esc(t.showRemaining(mlpsRemaining.length))}…</summary>
|
|
8087
8502
|
${mlpsRemaining.map(renderMlpsRec).join("\n")}
|
|
8088
8503
|
</details>` : "";
|
|
8089
8504
|
remediationHtml = `
|
|
8090
8505
|
<details class="rec-fold" open>
|
|
8091
|
-
<summary><h2 style="margin:0;border:0;display:inline"
|
|
8506
|
+
<summary><h2 style="margin:0;border:0;display:inline">${esc(t.remediationItems(mlpsUniqueRecs.length))}</h2></summary>
|
|
8092
8507
|
<div class="rec-body">
|
|
8093
8508
|
<ol>${mlpsTopItems}${mlpsMoreHtml}</ol>
|
|
8094
8509
|
</div>
|
|
8095
8510
|
</details>`;
|
|
8096
8511
|
}
|
|
8097
8512
|
}
|
|
8098
|
-
const naNote = naCount > 0 ? `<p style="color:#64748b;font-size:13px;margin-top:24px"
|
|
8099
|
-
const unknownNote = autoUnknown > 0 ? `<div style="color:#94a3b8;font-size:12px;margin-top:8px"
|
|
8513
|
+
const naNote = naCount > 0 ? `<p style="color:#64748b;font-size:13px;margin-top:24px">${esc(t.naNote(naCount))}</p>` : "";
|
|
8514
|
+
const unknownNote = autoUnknown > 0 ? `<div style="color:#94a3b8;font-size:12px;margin-top:8px">${esc(t.unknownNote(autoUnknown))}</div>` : "";
|
|
8100
8515
|
const mlpsCss = `
|
|
8101
8516
|
.mlps-cloud-section>summary{color:#94a3b8}
|
|
8102
8517
|
.mlps-cloud-note{color:#94a3b8;font-size:13px;margin-bottom:12px;font-style:italic}
|
|
@@ -8118,40 +8533,40 @@ ${mlpsRemaining.map(renderMlpsRec).join("\n")}
|
|
|
8118
8533
|
.mlps-summary-card .stat-label{font-size:12px;color:#94a3b8;margin-top:2px}
|
|
8119
8534
|
`;
|
|
8120
8535
|
return `<!DOCTYPE html>
|
|
8121
|
-
<html lang="
|
|
8536
|
+
<html lang="${htmlLang}">
|
|
8122
8537
|
<head>
|
|
8123
8538
|
<meta charset="UTF-8">
|
|
8124
8539
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
8125
|
-
<title
|
|
8540
|
+
<title>${esc(t.mlpsTitle)} — ${esc(date)}</title>
|
|
8126
8541
|
<style>${sharedCss()}${mlpsCss}</style>
|
|
8127
8542
|
</head>
|
|
8128
8543
|
<body>
|
|
8129
8544
|
<div class="container">
|
|
8130
8545
|
|
|
8131
8546
|
<header>
|
|
8132
|
-
<h1>🛡️
|
|
8133
|
-
<div class="disclaimer"
|
|
8134
|
-
<div class="meta"
|
|
8547
|
+
<h1>🛡️ ${esc(t.mlpsTitle)}</h1>
|
|
8548
|
+
<div class="disclaimer">${esc(t.mlpsDisclaimer)}</div>
|
|
8549
|
+
<div class="meta">${esc(t.account)}: ${esc(accountId)} | ${esc(t.region)}: ${esc(region)} | ${esc(t.scanTime)}: ${esc(scanTime)}</div>
|
|
8135
8550
|
</header>
|
|
8136
8551
|
|
|
8137
8552
|
<section class="summary" style="display:block;text-align:center">
|
|
8138
8553
|
<div style="font-size:36px;font-weight:700;margin-bottom:12px">
|
|
8139
|
-
<span style="color:#22c55e">${autoClean}</span> <span style="color:#94a3b8;font-size:18px"
|
|
8554
|
+
<span style="color:#22c55e">${autoClean}</span> <span style="color:#94a3b8;font-size:18px">${esc(t.noIssues)}</span>
|
|
8140
8555
|
<span style="color:#475569;margin:0 16px">/</span>
|
|
8141
|
-
<span style="color:#ef4444">${autoIssues}</span> <span style="color:#94a3b8;font-size:18px"
|
|
8556
|
+
<span style="color:#ef4444">${autoIssues}</span> <span style="color:#94a3b8;font-size:18px">${esc(t.issuesFound)}</span>
|
|
8142
8557
|
</div>
|
|
8143
8558
|
<div class="mlps-summary-cards" style="justify-content:center">
|
|
8144
|
-
<div class="mlps-summary-card"><div class="stat-count" style="color:#60a5fa">${checkedTotal}</div><div class="stat-label"
|
|
8145
|
-
<div class="mlps-summary-card"><div class="stat-count" style="color:#94a3b8">${cloudCount}</div><div class="stat-label">\u{1F3E2}
|
|
8146
|
-
<div class="mlps-summary-card"><div class="stat-count" style="color:#eab308">${manualCount}</div><div class="stat-label">\u{1F4CB}
|
|
8147
|
-
${naCount > 0 ? `<div class="mlps-summary-card"><div class="stat-count" style="color:#64748b">${naCount}</div><div class="stat-label">\u2796
|
|
8559
|
+
<div class="mlps-summary-card"><div class="stat-count" style="color:#60a5fa">${checkedTotal}</div><div class="stat-label">${esc(t.checkedItems)}</div></div>
|
|
8560
|
+
<div class="mlps-summary-card"><div class="stat-count" style="color:#94a3b8">${cloudCount}</div><div class="stat-label">\u{1F3E2} ${esc(t.cloudProvider)}</div></div>
|
|
8561
|
+
<div class="mlps-summary-card"><div class="stat-count" style="color:#eab308">${manualCount}</div><div class="stat-label">\u{1F4CB} ${esc(t.manualReview)}</div></div>
|
|
8562
|
+
${naCount > 0 ? `<div class="mlps-summary-card"><div class="stat-count" style="color:#64748b">${naCount}</div><div class="stat-label">\u2796 ${esc(t.notApplicable)}</div></div>` : ""}
|
|
8148
8563
|
</div>
|
|
8149
8564
|
</section>
|
|
8150
8565
|
${unknownNote}
|
|
8151
8566
|
|
|
8152
8567
|
${trendHtml}
|
|
8153
8568
|
|
|
8154
|
-
${buildServiceReminderHtml(scanResults.modules)}
|
|
8569
|
+
${buildServiceReminderHtml(scanResults.modules, lang)}
|
|
8155
8570
|
|
|
8156
8571
|
${categorySections}
|
|
8157
8572
|
|
|
@@ -8160,8 +8575,8 @@ ${remediationHtml}
|
|
|
8160
8575
|
${naNote}
|
|
8161
8576
|
|
|
8162
8577
|
<footer>
|
|
8163
|
-
<p
|
|
8164
|
-
<p
|
|
8578
|
+
<p>${esc(t.mlpsFooterGenerated(VERSION))}</p>
|
|
8579
|
+
<p>${esc(t.mlpsFooterDisclaimer)}</p>
|
|
8165
8580
|
</footer>
|
|
8166
8581
|
|
|
8167
8582
|
</div>
|
|
@@ -8376,16 +8791,14 @@ Aggregates active findings from AWS Security Hub. Replaces individual config sca
|
|
|
8376
8791
|
- INFORMATIONAL findings are skipped.
|
|
8377
8792
|
|
|
8378
8793
|
## 3. GuardDuty Findings (guardduty_findings)
|
|
8379
|
-
|
|
8380
|
-
-
|
|
8381
|
-
-
|
|
8382
|
-
- Only non-archived findings are included.
|
|
8794
|
+
Detection-only: checks if GuardDuty is enabled in the region.
|
|
8795
|
+
- GuardDuty findings are aggregated via Security Hub (security_hub_findings module).
|
|
8796
|
+
- Reports whether GuardDuty detectors are active.
|
|
8383
8797
|
|
|
8384
8798
|
## 4. Inspector Findings (inspector_findings)
|
|
8385
|
-
|
|
8386
|
-
-
|
|
8387
|
-
-
|
|
8388
|
-
- CVE IDs are included in finding titles when available.
|
|
8799
|
+
Detection-only: checks if Inspector is enabled in the region.
|
|
8800
|
+
- Inspector findings are aggregated via Security Hub (security_hub_findings module).
|
|
8801
|
+
- Reports whether Inspector scanning (EC2/Lambda) is active.
|
|
8389
8802
|
|
|
8390
8803
|
## 5. Trusted Advisor Findings (trusted_advisor_findings)
|
|
8391
8804
|
Aggregates security checks from AWS Trusted Advisor.
|
|
@@ -8421,19 +8834,15 @@ Finds unused/idle AWS resources (unattached EBS volumes, unused EIPs, stopped in
|
|
|
8421
8834
|
Assesses disaster recovery readiness \u2014 RDS Multi-AZ & backups, EBS snapshot coverage, S3 versioning & cross-region replication.
|
|
8422
8835
|
|
|
8423
8836
|
## 15. Config Rules Findings (config_rules_findings)
|
|
8424
|
-
|
|
8425
|
-
-
|
|
8426
|
-
-
|
|
8427
|
-
- Security-related rules (encryption, IAM, public access, etc.) mapped to HIGH severity (7.5).
|
|
8428
|
-
- Other non-compliant rules mapped to MEDIUM severity (5.5).
|
|
8837
|
+
Detection-only: checks if AWS Config Rules are configured.
|
|
8838
|
+
- Config Rule compliance findings are aggregated via Security Hub (security_hub_findings module).
|
|
8839
|
+
- Reports whether Config is enabled and counts active rules.
|
|
8429
8840
|
- Gracefully handles regions where AWS Config is not enabled.
|
|
8430
8841
|
|
|
8431
8842
|
## 16. IAM Access Analyzer Findings (access_analyzer_findings)
|
|
8432
|
-
|
|
8433
|
-
-
|
|
8434
|
-
-
|
|
8435
|
-
- Covers S3 buckets, IAM roles, SQS queues, Lambda functions, KMS keys, and more.
|
|
8436
|
-
- Severity mapped: CRITICAL \u2192 9.5, HIGH \u2192 8.0, MEDIUM \u2192 5.5, LOW \u2192 3.0.
|
|
8843
|
+
Detection-only: checks if IAM Access Analyzer is configured.
|
|
8844
|
+
- Access Analyzer findings are aggregated via Security Hub (security_hub_findings module).
|
|
8845
|
+
- Reports whether active analyzers exist.
|
|
8437
8846
|
- Returns warning if no analyzer is configured.
|
|
8438
8847
|
|
|
8439
8848
|
## 17. SSM Patch Compliance (patch_compliance_findings)
|
|
@@ -8518,112 +8927,18 @@ var MODULE_DESCRIPTIONS = {
|
|
|
8518
8927
|
idle_resources: "Finds unused/idle AWS resources (unattached EBS volumes, unused EIPs, stopped instances, unused security groups) that waste money and increase attack surface.",
|
|
8519
8928
|
disaster_recovery: "Assesses disaster recovery readiness \u2014 RDS Multi-AZ & backups, EBS snapshot coverage, S3 versioning & cross-region replication.",
|
|
8520
8929
|
security_hub_findings: "Aggregates active findings from AWS Security Hub \u2014 replaces individual config scanners with centralized compliance checks.",
|
|
8521
|
-
guardduty_findings: "
|
|
8522
|
-
inspector_findings: "
|
|
8930
|
+
guardduty_findings: "Checks if GuardDuty is enabled. Findings are aggregated via Security Hub.",
|
|
8931
|
+
inspector_findings: "Checks if Inspector is enabled. Findings are aggregated via Security Hub.",
|
|
8523
8932
|
trusted_advisor_findings: "Aggregates security checks from AWS Trusted Advisor \u2014 requires Business or Enterprise Support plan.",
|
|
8524
|
-
config_rules_findings: "
|
|
8525
|
-
access_analyzer_findings: "
|
|
8933
|
+
config_rules_findings: "Checks if AWS Config Rules are configured. Findings are aggregated via Security Hub.",
|
|
8934
|
+
access_analyzer_findings: "Checks if IAM Access Analyzer is configured. Findings are aggregated via Security Hub.",
|
|
8526
8935
|
patch_compliance_findings: "Checks SSM Patch Manager compliance \u2014 managed instances with missing or failed security and system patches.",
|
|
8527
8936
|
imdsv2_enforcement: "Checks if EC2 instances enforce IMDSv2 (HttpTokens: required) \u2014 IMDSv1 allows credential theft via SSRF.",
|
|
8528
8937
|
waf_coverage: "Checks if internet-facing ALBs have WAF Web ACL associated for protection against common web exploits."
|
|
8529
8938
|
};
|
|
8530
|
-
|
|
8531
|
-
|
|
8532
|
-
|
|
8533
|
-
\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
|
|
8534
|
-
|
|
8535
|
-
\u4EE5\u4E0B\u4E8B\u9879\u9700\u8981\u4EBA\u5DE5\u786E\u8BA4\u548C\u6267\u884C\uFF1A
|
|
8536
|
-
|
|
8537
|
-
\u26A0\uFE0F \u5E94\u6025\u9694\u79BB/\u6B62\u8840\u65B9\u6848
|
|
8538
|
-
\u25A1 \u51C6\u5907\u4E13\u7528\u9694\u79BB\u5B89\u5168\u7EC4\uFF08\u65E0 Inbound/Outbound \u89C4\u5219\uFF09
|
|
8539
|
-
\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
|
|
8540
|
-
\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
|
|
8541
|
-
\u25A1 \u660E\u786E\u5404\u9879\u76EE\u8D26\u6237\u53CA\u8D44\u6E90\u7684\u8D1F\u8D23\u4EBA\u4E0E\u8054\u7CFB\u65B9\u5F0F
|
|
8542
|
-
|
|
8543
|
-
\u26A0\uFE0F \u6D4B\u8BD5/\u5F00\u53D1\u73AF\u5883\u5904\u7F6E
|
|
8544
|
-
\u25A1 \u975E\u6838\u5FC3\u7CFB\u7EDF\u5728\u62A4\u7F51\u671F\u95F4\u5173\u95ED
|
|
8545
|
-
\u25A1 \u6D4B\u8BD5/\u5F00\u53D1\u73AF\u5883\u5173\u95ED\u6216\u4E0E\u751F\u4EA7\u4FDD\u6301\u540C\u7B49\u5B89\u5168\u57FA\u7EBF
|
|
8546
|
-
\u25A1 \u786E\u8BA4\u54EA\u4E9B\u73AF\u5883\u53EF\u4EE5\u7D27\u6025\u5173\u505C\uFF0C\u907F\u514D\u653B\u51FB\u6269\u6563
|
|
8547
|
-
|
|
8548
|
-
\u26A0\uFE0F \u503C\u5B88\u56E2\u961F\u7EC4\u5EFA
|
|
8549
|
-
\u25A1 7\xD724 \u76D1\u63A7\u5FEB\u901F\u54CD\u5E94\u56E2\u961F
|
|
8550
|
-
\u25A1 \u6280\u672F\u4E0E\u98CE\u9669\u5206\u6790\u7EC4
|
|
8551
|
-
\u25A1 \u5B89\u5168\u7B56\u7565\u4E0B\u53D1\u7EC4
|
|
8552
|
-
\u25A1 \u4E1A\u52A1\u54CD\u5E94\u7EC4
|
|
8553
|
-
\u25A1 \u660E\u786E AWS TAM/Support \u8054\u7CFB\u65B9\u5F0F\uFF08ES/EOP \u5BA2\u6237\uFF09
|
|
8554
|
-
|
|
8555
|
-
\u26A0\uFE0F \u51FA\u5165\u7AD9\u8DEF\u5F84\u67B6\u6784\u56FE
|
|
8556
|
-
\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
|
|
8557
|
-
\u25A1 \u660E\u786E\u5404 ELB/Public EC2/S3/DX \u7684\u6570\u636E\u6D41\u5411
|
|
8558
|
-
\u25A1 \u8BC6\u522B\u6240\u6709\u9762\u5411\u4E92\u8054\u7F51\u7684\u6570\u636E\u4EA4\u4E92\u63A5\u53E3
|
|
8559
|
-
|
|
8560
|
-
\u26A0\uFE0F \u4E3B\u52A8\u5F0F\u6E17\u900F\u6D4B\u8BD5
|
|
8561
|
-
\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
|
|
8562
|
-
\u25A1 \u57FA\u4E8E\u6E17\u900F\u6D4B\u8BD5\u62A5\u544A\u8FDB\u884C\u6B63\u5F0F\u62A4\u7F51\u524D\u7684\u5B89\u5168\u52A0\u56FA
|
|
8563
|
-
\u25A1 \u5173\u6CE8 AWS \u5B89\u5168\u516C\u544A\uFF08\u5DF2\u77E5\u6F0F\u6D1E\u4E0E\u8865\u4E01\uFF09
|
|
8564
|
-
|
|
8565
|
-
\u26A0\uFE0F WAR-ROOM \u5B9E\u65F6\u6C9F\u901A
|
|
8566
|
-
\u25A1 \u521B\u5EFA\u62A4\u7F51\u671F\u95F4\u4E13\u7528\u6C9F\u901A\u6E20\u9053\uFF08\u4F01\u5FAE/\u9489\u9489/\u98DE\u4E66/Chime\uFF09
|
|
8567
|
-
\u25A1 \u4E0E AWS TAM \u5EFA\u7ACB WAR-ROOM \u8054\u7CFB\uFF08\u4F01\u4E1A\u7EA7\u652F\u6301\u5BA2\u6237\uFF09
|
|
8568
|
-
\u25A1 \u7EDF\u4E00\u6848\u4F8B\u6807\u9898\u683C\u5F0F\uFF1A"\u3010\u62A4\u7F51\u3011+ \u95EE\u9898\u63CF\u8FF0"
|
|
8569
|
-
|
|
8570
|
-
\u26A0\uFE0F \u5BC6\u7801\u4E0E\u51ED\u8BC1\u7BA1\u7406
|
|
8571
|
-
\u25A1 \u6240\u6709 IAM \u7528\u6237\u7ED1\u5B9A MFA
|
|
8572
|
-
\u25A1 AKSK \u8F6E\u8F6C\u5468\u671F \u2264 90 \u5929
|
|
8573
|
-
\u25A1 \u907F\u514D\u5171\u4EAB\u8D26\u6237\u4F7F\u7528
|
|
8574
|
-
\u25A1 S3/Lambda/\u5E94\u7528\u4EE3\u7801\u4E2D\u65E0\u660E\u6587\u5BC6\u7801
|
|
8575
|
-
|
|
8576
|
-
\u26A0\uFE0F \u62A4\u7F51\u540E\u4F18\u5316
|
|
8577
|
-
\u25A1 \u9488\u5BF9\u653B\u51FB\u62A5\u544A\u9010\u9879\u5E94\u7B54\u4E0E\u4FEE\u590D
|
|
8578
|
-
\u25A1 \u4E0E\u5B89\u5168\u56E2\u961F\u5EFA\u7ACB\u5468\u671F\u6027\u5B89\u5168\u7EF4\u62A4\u6D41\u7A0B
|
|
8579
|
-
\u25A1 \u6301\u7EED\u8865\u5168\u5B89\u5168\u98CE\u9669
|
|
8580
|
-
|
|
8581
|
-
\u53C2\u8003\uFF1AAWS \u62A4\u7F51\u884C\u52A8 Standard Operation Procedure (Compliance IEM)
|
|
8582
|
-
`;
|
|
8583
|
-
var SERVICE_RECOMMENDATIONS2 = {
|
|
8584
|
-
security_hub_findings: {
|
|
8585
|
-
icon: "\u{1F534}",
|
|
8586
|
-
service: "Security Hub",
|
|
8587
|
-
impact: "\u65E0\u6CD5\u83B7\u53D6 300+ \u9879\u81EA\u52A8\u5316\u5B89\u5168\u68C0\u67E5\uFF08FSBP/CIS/PCI DSS \u6807\u51C6\uFF09",
|
|
8588
|
-
action: "\u542F\u7528 Security Hub \u83B7\u5F97\u6700\u5168\u9762\u7684\u5B89\u5168\u6001\u52BF\u8BC4\u4F30"
|
|
8589
|
-
},
|
|
8590
|
-
guardduty_findings: {
|
|
8591
|
-
icon: "\u{1F534}",
|
|
8592
|
-
service: "GuardDuty",
|
|
8593
|
-
impact: "\u65E0\u6CD5\u68C0\u6D4B\u5A01\u80C1\u6D3B\u52A8\uFF08\u6076\u610F IP\u3001\u5F02\u5E38 API \u8C03\u7528\u3001\u52A0\u5BC6\u8D27\u5E01\u6316\u77FF\u7B49\uFF09",
|
|
8594
|
-
action: "\u542F\u7528 GuardDuty \u83B7\u5F97\u6301\u7EED\u5A01\u80C1\u68C0\u6D4B\u80FD\u529B"
|
|
8595
|
-
},
|
|
8596
|
-
inspector_findings: {
|
|
8597
|
-
icon: "\u{1F7E1}",
|
|
8598
|
-
service: "Inspector",
|
|
8599
|
-
impact: "\u65E0\u6CD5\u626B\u63CF EC2/Lambda/\u5BB9\u5668\u7684\u8F6F\u4EF6\u6F0F\u6D1E\uFF08CVE\uFF09",
|
|
8600
|
-
action: "\u542F\u7528 Inspector \u53D1\u73B0\u5DF2\u77E5\u5B89\u5168\u6F0F\u6D1E"
|
|
8601
|
-
},
|
|
8602
|
-
trusted_advisor_findings: {
|
|
8603
|
-
icon: "\u{1F7E1}",
|
|
8604
|
-
service: "Trusted Advisor",
|
|
8605
|
-
impact: "\u65E0\u6CD5\u83B7\u53D6 AWS \u6700\u4F73\u5B9E\u8DF5\u5B89\u5168\u68C0\u67E5",
|
|
8606
|
-
action: "\u5347\u7EA7\u81F3 Business/Enterprise Support \u8BA1\u5212\u4EE5\u4F7F\u7528 Trusted Advisor \u5B89\u5168\u68C0\u67E5"
|
|
8607
|
-
},
|
|
8608
|
-
config_rules_findings: {
|
|
8609
|
-
icon: "\u{1F7E1}",
|
|
8610
|
-
service: "AWS Config",
|
|
8611
|
-
impact: "\u65E0\u6CD5\u68C0\u67E5\u8D44\u6E90\u914D\u7F6E\u5408\u89C4\u72B6\u6001",
|
|
8612
|
-
action: "\u542F\u7528 AWS Config \u5E76\u914D\u7F6E Config Rules"
|
|
8613
|
-
},
|
|
8614
|
-
access_analyzer_findings: {
|
|
8615
|
-
icon: "\u{1F7E1}",
|
|
8616
|
-
service: "IAM Access Analyzer",
|
|
8617
|
-
impact: "\u65E0\u6CD5\u68C0\u6D4B\u8D44\u6E90\u662F\u5426\u88AB\u5916\u90E8\u8D26\u53F7\u6216\u516C\u7F51\u8BBF\u95EE",
|
|
8618
|
-
action: "\u521B\u5EFA IAM Access Analyzer\uFF08\u8D26\u6237\u7EA7\u6216\u7EC4\u7EC7\u7EA7\uFF09"
|
|
8619
|
-
},
|
|
8620
|
-
patch_compliance_findings: {
|
|
8621
|
-
icon: "\u{1F7E1}",
|
|
8622
|
-
service: "SSM Patch Manager",
|
|
8623
|
-
impact: "\u65E0\u6CD5\u68C0\u67E5\u5B9E\u4F8B\u8865\u4E01\u5408\u89C4\u72B6\u6001",
|
|
8624
|
-
action: "\u5B89\u88C5 SSM Agent \u5E76\u914D\u7F6E Patch Manager"
|
|
8625
|
-
}
|
|
8626
|
-
};
|
|
8939
|
+
function getHwDefenseChecklist(lang) {
|
|
8940
|
+
return getI18n(lang ?? "zh").hwChecklist;
|
|
8941
|
+
}
|
|
8627
8942
|
var SERVICE_NOT_ENABLED_PATTERNS2 = [
|
|
8628
8943
|
"not enabled",
|
|
8629
8944
|
"not found",
|
|
@@ -8633,10 +8948,11 @@ var SERVICE_NOT_ENABLED_PATTERNS2 = [
|
|
|
8633
8948
|
"not available",
|
|
8634
8949
|
"is not enabled"
|
|
8635
8950
|
];
|
|
8636
|
-
function buildServiceReminder(modules) {
|
|
8951
|
+
function buildServiceReminder(modules, lang) {
|
|
8952
|
+
const t = getI18n(lang ?? "zh");
|
|
8637
8953
|
const disabledServices = [];
|
|
8638
8954
|
for (const mod of modules) {
|
|
8639
|
-
const rec =
|
|
8955
|
+
const rec = t.serviceRecommendations[mod.module];
|
|
8640
8956
|
if (!rec) continue;
|
|
8641
8957
|
if (!mod.warnings?.length) continue;
|
|
8642
8958
|
const hasNotEnabled = mod.warnings.some(
|
|
@@ -8649,26 +8965,26 @@ function buildServiceReminder(modules) {
|
|
|
8649
8965
|
if (disabledServices.length === 0) return "";
|
|
8650
8966
|
const lines = [
|
|
8651
8967
|
"",
|
|
8652
|
-
|
|
8968
|
+
t.serviceReminderTitle,
|
|
8653
8969
|
""
|
|
8654
8970
|
];
|
|
8655
8971
|
for (const svc of disabledServices) {
|
|
8656
|
-
lines.push(`${svc.icon} ${svc.service}
|
|
8657
|
-
lines.push(`
|
|
8658
|
-
lines.push(`
|
|
8972
|
+
lines.push(`${svc.icon} ${svc.service} ${t.notEnabled}`);
|
|
8973
|
+
lines.push(` ${t.serviceImpact}: ${svc.impact}`);
|
|
8974
|
+
lines.push(` ${t.serviceAction}: ${svc.action}`);
|
|
8659
8975
|
lines.push("");
|
|
8660
8976
|
}
|
|
8661
|
-
lines.push(
|
|
8977
|
+
lines.push(t.serviceReminderFooter);
|
|
8662
8978
|
return lines.join("\n");
|
|
8663
8979
|
}
|
|
8664
|
-
function summarizeResult(result) {
|
|
8980
|
+
function summarizeResult(result, lang) {
|
|
8665
8981
|
const { summary } = result;
|
|
8666
8982
|
const lines = [
|
|
8667
8983
|
`Scan complete for account ${result.accountId} in ${result.region}.`,
|
|
8668
8984
|
`Total findings: ${summary.totalFindings} (${summary.critical} Critical, ${summary.high} High, ${summary.medium} Medium, ${summary.low} Low)`,
|
|
8669
8985
|
`Modules: ${summary.modulesSuccess} succeeded, ${summary.modulesError} errored`
|
|
8670
8986
|
];
|
|
8671
|
-
const reminder = buildServiceReminder(result.modules);
|
|
8987
|
+
const reminder = buildServiceReminder(result.modules, lang);
|
|
8672
8988
|
if (reminder) {
|
|
8673
8989
|
lines.push(reminder);
|
|
8674
8990
|
}
|
|
@@ -8730,9 +9046,10 @@ function createServer(defaultRegion) {
|
|
|
8730
9046
|
region: z.string().optional().describe("AWS region to scan (default: server region)"),
|
|
8731
9047
|
org_mode: z.boolean().optional().describe("Enable multi-account scanning via AWS Organizations"),
|
|
8732
9048
|
role_name: z.string().optional().describe("IAM role name to assume in child accounts (default: AWSSecurityMCPAudit)"),
|
|
8733
|
-
account_ids: z.array(z.string()).optional().describe("Specific account IDs to scan (default: all org accounts)")
|
|
9049
|
+
account_ids: z.array(z.string()).optional().describe("Specific account IDs to scan (default: all org accounts)"),
|
|
9050
|
+
lang: z.enum(["zh", "en"]).optional().describe("Report language (default: zh)")
|
|
8734
9051
|
},
|
|
8735
|
-
async ({ region, org_mode, role_name, account_ids }) => {
|
|
9052
|
+
async ({ region, org_mode, role_name, account_ids, lang }) => {
|
|
8736
9053
|
try {
|
|
8737
9054
|
const r = region ?? defaultRegion;
|
|
8738
9055
|
let result;
|
|
@@ -8747,7 +9064,7 @@ function createServer(defaultRegion) {
|
|
|
8747
9064
|
}
|
|
8748
9065
|
return {
|
|
8749
9066
|
content: [
|
|
8750
|
-
{ type: "text", text: summarizeResult(result) },
|
|
9067
|
+
{ type: "text", text: summarizeResult(result, lang ?? "zh") },
|
|
8751
9068
|
{ type: "text", text: JSON.stringify(result, null, 2) }
|
|
8752
9069
|
]
|
|
8753
9070
|
};
|
|
@@ -8808,9 +9125,10 @@ function createServer(defaultRegion) {
|
|
|
8808
9125
|
region: z.string().optional().describe("AWS region to scan (default: server region)"),
|
|
8809
9126
|
org_mode: z.boolean().optional().describe("Enable multi-account scanning via AWS Organizations"),
|
|
8810
9127
|
role_name: z.string().optional().describe("IAM role name to assume in child accounts (default: AWSSecurityMCPAudit)"),
|
|
8811
|
-
account_ids: z.array(z.string()).optional().describe("Specific account IDs to scan (default: all org accounts)")
|
|
9128
|
+
account_ids: z.array(z.string()).optional().describe("Specific account IDs to scan (default: all org accounts)"),
|
|
9129
|
+
lang: z.enum(["zh", "en"]).optional().describe("Report language (default: zh)")
|
|
8812
9130
|
},
|
|
8813
|
-
async ({ group, region, org_mode, role_name, account_ids }) => {
|
|
9131
|
+
async ({ group, region, org_mode, role_name, account_ids, lang }) => {
|
|
8814
9132
|
try {
|
|
8815
9133
|
const groupDef = SCAN_GROUPS[group];
|
|
8816
9134
|
if (!groupDef) {
|
|
@@ -8892,7 +9210,7 @@ function createServer(defaultRegion) {
|
|
|
8892
9210
|
`Scan group: ${groupDef.name} (${group})`,
|
|
8893
9211
|
groupDef.description,
|
|
8894
9212
|
"",
|
|
8895
|
-
summarizeResult(result)
|
|
9213
|
+
summarizeResult(result, lang ?? "zh")
|
|
8896
9214
|
];
|
|
8897
9215
|
if (missingModules.length > 0) {
|
|
8898
9216
|
lines.push("");
|
|
@@ -8905,7 +9223,7 @@ function createServer(defaultRegion) {
|
|
|
8905
9223
|
if (group === "hw_defense") {
|
|
8906
9224
|
const summaryContent = content[0];
|
|
8907
9225
|
if (summaryContent && summaryContent.type === "text") {
|
|
8908
|
-
summaryContent.text += "\n\n" +
|
|
9226
|
+
summaryContent.text += "\n\n" + getHwDefenseChecklist(lang ?? "zh");
|
|
8909
9227
|
}
|
|
8910
9228
|
}
|
|
8911
9229
|
return { content };
|
|
@@ -8935,11 +9253,14 @@ function createServer(defaultRegion) {
|
|
|
8935
9253
|
server.tool(
|
|
8936
9254
|
"generate_report",
|
|
8937
9255
|
"Generate a Markdown security report from scan results. Read-only. Does not modify any AWS resources.",
|
|
8938
|
-
{
|
|
8939
|
-
|
|
9256
|
+
{
|
|
9257
|
+
scan_results: z.string().describe("JSON string of FullScanResult from scan_all"),
|
|
9258
|
+
lang: z.enum(["zh", "en"]).optional().describe("Report language (default: zh)")
|
|
9259
|
+
},
|
|
9260
|
+
async ({ scan_results, lang }) => {
|
|
8940
9261
|
try {
|
|
8941
9262
|
const parsed = JSON.parse(scan_results);
|
|
8942
|
-
const report = generateMarkdownReport(parsed);
|
|
9263
|
+
const report = generateMarkdownReport(parsed, lang ?? "zh");
|
|
8943
9264
|
return { content: [{ type: "text", text: report }] };
|
|
8944
9265
|
} catch (err) {
|
|
8945
9266
|
return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
@@ -8949,11 +9270,14 @@ function createServer(defaultRegion) {
|
|
|
8949
9270
|
server.tool(
|
|
8950
9271
|
"generate_mlps3_report",
|
|
8951
9272
|
"Generate a GB/T 22239-2019 \u7B49\u4FDD\u4E09\u7EA7 compliance pre-check report from scan results. Best used with scan_group mlps3_precheck results. Read-only.",
|
|
8952
|
-
{
|
|
8953
|
-
|
|
9273
|
+
{
|
|
9274
|
+
scan_results: z.string().describe("JSON string of FullScanResult from scan_group mlps3_precheck or scan_all"),
|
|
9275
|
+
lang: z.enum(["zh", "en"]).optional().describe("Report language (default: zh)")
|
|
9276
|
+
},
|
|
9277
|
+
async ({ scan_results, lang }) => {
|
|
8954
9278
|
try {
|
|
8955
9279
|
const parsed = JSON.parse(scan_results);
|
|
8956
|
-
const report = generateMlps3Report(parsed);
|
|
9280
|
+
const report = generateMlps3Report(parsed, lang ?? "zh");
|
|
8957
9281
|
return { content: [{ type: "text", text: report }] };
|
|
8958
9282
|
} catch (err) {
|
|
8959
9283
|
return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
@@ -8965,13 +9289,14 @@ function createServer(defaultRegion) {
|
|
|
8965
9289
|
"Generate a professional HTML security report. Save the output as an .html file.",
|
|
8966
9290
|
{
|
|
8967
9291
|
scan_results: z.string().describe("JSON string of FullScanResult from scan_all"),
|
|
8968
|
-
history: z.string().optional().describe("JSON string of DashboardHistoryEntry[] from dashboard data.json for 30-day trend charts")
|
|
9292
|
+
history: z.string().optional().describe("JSON string of DashboardHistoryEntry[] from dashboard data.json for 30-day trend charts"),
|
|
9293
|
+
lang: z.enum(["zh", "en"]).optional().describe("Report language (default: zh)")
|
|
8969
9294
|
},
|
|
8970
|
-
async ({ scan_results, history }) => {
|
|
9295
|
+
async ({ scan_results, history, lang }) => {
|
|
8971
9296
|
try {
|
|
8972
9297
|
const parsed = JSON.parse(scan_results);
|
|
8973
9298
|
const historyData = history ? JSON.parse(history) : void 0;
|
|
8974
|
-
const report = generateHtmlReport(parsed, historyData);
|
|
9299
|
+
const report = generateHtmlReport(parsed, historyData, lang ?? "zh");
|
|
8975
9300
|
return { content: [{ type: "text", text: report }] };
|
|
8976
9301
|
} catch (err) {
|
|
8977
9302
|
return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
@@ -8983,13 +9308,14 @@ function createServer(defaultRegion) {
|
|
|
8983
9308
|
"Generate a professional HTML MLPS Level 3 compliance report (\u7B49\u4FDD\u4E09\u7EA7). Save as .html file.",
|
|
8984
9309
|
{
|
|
8985
9310
|
scan_results: z.string().describe("JSON string of FullScanResult from scan_group mlps3_precheck or scan_all"),
|
|
8986
|
-
history: z.string().optional().describe("JSON string of DashboardHistoryEntry[] from dashboard data.json for 30-day trend charts")
|
|
9311
|
+
history: z.string().optional().describe("JSON string of DashboardHistoryEntry[] from dashboard data.json for 30-day trend charts"),
|
|
9312
|
+
lang: z.enum(["zh", "en"]).optional().describe("Report language (default: zh)")
|
|
8987
9313
|
},
|
|
8988
|
-
async ({ scan_results, history }) => {
|
|
9314
|
+
async ({ scan_results, history, lang }) => {
|
|
8989
9315
|
try {
|
|
8990
9316
|
const parsed = JSON.parse(scan_results);
|
|
8991
9317
|
const historyData = history ? JSON.parse(history) : void 0;
|
|
8992
|
-
const report = generateMlps3HtmlReport(parsed, historyData);
|
|
9318
|
+
const report = generateMlps3HtmlReport(parsed, historyData, lang ?? "zh");
|
|
8993
9319
|
return { content: [{ type: "text", text: report }] };
|
|
8994
9320
|
} catch (err) {
|
|
8995
9321
|
return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
@@ -8999,7 +9325,10 @@ function createServer(defaultRegion) {
|
|
|
8999
9325
|
server.tool(
|
|
9000
9326
|
"generate_maturity_report",
|
|
9001
9327
|
"Generate a security maturity assessment report from scan_all results. Requires service_detection module output. Read-only.",
|
|
9002
|
-
{
|
|
9328
|
+
{
|
|
9329
|
+
scan_results: z.string().describe("JSON string of FullScanResult from scan_all"),
|
|
9330
|
+
lang: z.enum(["zh", "en"]).optional().describe("Report language (default: zh)")
|
|
9331
|
+
},
|
|
9003
9332
|
async ({ scan_results }) => {
|
|
9004
9333
|
try {
|
|
9005
9334
|
const parsed = JSON.parse(scan_results);
|
|
@@ -9270,7 +9599,7 @@ ${finding}`
|
|
|
9270
9599
|
type: "text",
|
|
9271
9600
|
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
|
|
9272
9601
|
|
|
9273
|
-
${
|
|
9602
|
+
${getHwDefenseChecklist("zh")}
|
|
9274
9603
|
|
|
9275
9604
|
\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`
|
|
9276
9605
|
}
|