aws-security-mcp 0.5.3 → 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/dist/bin/aws-security-mcp.js +856 -360
- package/dist/bin/aws-security-mcp.js.map +1 -1
- package/dist/src/index.d.ts +6 -4
- package/dist/src/index.js +856 -360
- 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",
|
|
@@ -2963,14 +2971,21 @@ var SecurityHubFindingsScanner = class {
|
|
|
2963
2971
|
const resourceType = f.Resources?.[0]?.Type ?? "AWS::Unknown";
|
|
2964
2972
|
const resourceArn = resourceId.startsWith("arn:") ? resourceId : `arn:${partition}:securityhub:${region}:${accountId}:finding/${f.Id ?? "unknown"}`;
|
|
2965
2973
|
const remediationSteps = [];
|
|
2966
|
-
|
|
2967
|
-
|
|
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);
|
|
2968
2982
|
}
|
|
2969
2983
|
if (f.Remediation?.Recommendation?.Url) {
|
|
2970
|
-
remediationSteps.push(`
|
|
2984
|
+
remediationSteps.push(`Documentation: ${f.Remediation.Recommendation.Url}`);
|
|
2971
2985
|
}
|
|
2972
|
-
|
|
2973
|
-
|
|
2986
|
+
const recText = f.Remediation?.Recommendation?.Text ?? "";
|
|
2987
|
+
if (recText && !["See References", "None Provided", ""].includes(recText.trim())) {
|
|
2988
|
+
remediationSteps.push(recText);
|
|
2974
2989
|
}
|
|
2975
2990
|
findings.push({
|
|
2976
2991
|
severity,
|
|
@@ -3123,10 +3138,11 @@ var GuardDutyFindingsScanner = class {
|
|
|
3123
3138
|
impact: `GuardDuty threat type: ${gdf.Type ?? "unknown"} (severity ${gdSeverity})`,
|
|
3124
3139
|
riskScore: score,
|
|
3125
3140
|
remediationSteps: [
|
|
3126
|
-
|
|
3127
|
-
`
|
|
3128
|
-
"
|
|
3129
|
-
|
|
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),
|
|
3130
3146
|
priority: priorityFromSeverity(severity),
|
|
3131
3147
|
module: this.moduleName,
|
|
3132
3148
|
accountId: gdf.AccountId ?? accountId
|
|
@@ -3217,18 +3233,44 @@ var InspectorFindingsScanner = class {
|
|
|
3217
3233
|
const resourceType = f.resources?.[0]?.type ?? "AWS::Unknown";
|
|
3218
3234
|
const resourceArn = resourceId.startsWith("arn:") ? resourceId : `arn:${partition}:inspector2:${region}:${accountId}:finding/${f.findingArn ?? "unknown"}`;
|
|
3219
3235
|
const remediationSteps = [];
|
|
3220
|
-
|
|
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) {
|
|
3221
3246
|
remediationSteps.push(f.remediation.recommendation.text);
|
|
3222
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
|
+
}
|
|
3223
3268
|
if (f.remediation?.recommendation?.Url) {
|
|
3224
|
-
remediationSteps.push(`
|
|
3269
|
+
remediationSteps.push(`Documentation: ${f.remediation.recommendation.Url}`);
|
|
3225
3270
|
}
|
|
3226
3271
|
if (f.packageVulnerabilityDetails?.referenceUrls?.length) {
|
|
3227
3272
|
remediationSteps.push(`CVE references: ${f.packageVulnerabilityDetails.referenceUrls.slice(0, 3).join(", ")}`);
|
|
3228
3273
|
}
|
|
3229
|
-
if (remediationSteps.length === 0) {
|
|
3230
|
-
remediationSteps.push("Review the finding in the Amazon Inspector console and apply the recommended patch or update.");
|
|
3231
|
-
}
|
|
3232
3274
|
const description = f.description ?? titleBase;
|
|
3233
3275
|
const impact = cveId ? `Vulnerability ${cveId} \u2014 CVSS: ${f.packageVulnerabilityDetails?.cvss?.[0]?.baseScore ?? "N/A"}` : `Inspector finding type: ${f.type ?? "unknown"}`;
|
|
3234
3276
|
findings.push({
|
|
@@ -3563,10 +3605,10 @@ var ConfigRulesFindingsScanner = class {
|
|
|
3563
3605
|
impact: `Resource is non-compliant with Config Rule: ${ruleName}`,
|
|
3564
3606
|
riskScore,
|
|
3565
3607
|
remediationSteps: [
|
|
3566
|
-
`
|
|
3567
|
-
`
|
|
3568
|
-
|
|
3569
|
-
],
|
|
3608
|
+
`Fix Config Rule violation: ${ruleName}`,
|
|
3609
|
+
annotation ? `Details: ${annotation}` : "",
|
|
3610
|
+
`Resource: ${resourceType}/${resourceId}`
|
|
3611
|
+
].filter(Boolean),
|
|
3570
3612
|
priority: priorityFromSeverity(severity),
|
|
3571
3613
|
module: this.moduleName,
|
|
3572
3614
|
accountId
|
|
@@ -3715,13 +3757,13 @@ var AccessAnalyzerFindingsScanner = class {
|
|
|
3715
3757
|
const title = buildFindingTitle(aaf);
|
|
3716
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"}`;
|
|
3717
3759
|
const remediationSteps = external ? [
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3760
|
+
`Restrict external access on ${resourceType} ${resourceId}`,
|
|
3761
|
+
"Remove or narrow the resource policy to eliminate unintended external access.",
|
|
3762
|
+
`Resource ARN: ${resourceArn}`
|
|
3721
3763
|
] : [
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3764
|
+
`Remove unused access on ${resourceType} ${resourceId}`,
|
|
3765
|
+
"Remove unused permissions, roles, or credentials to follow least-privilege.",
|
|
3766
|
+
`Resource ARN: ${resourceArn}`
|
|
3725
3767
|
];
|
|
3726
3768
|
findings.push({
|
|
3727
3769
|
severity,
|
|
@@ -4196,6 +4238,479 @@ var WafCoverageScanner = class {
|
|
|
4196
4238
|
}
|
|
4197
4239
|
};
|
|
4198
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
|
+
|
|
4199
4714
|
// src/tools/report-tool.ts
|
|
4200
4715
|
var SEVERITY_ICON = {
|
|
4201
4716
|
CRITICAL: "\u{1F534}",
|
|
@@ -4213,38 +4728,45 @@ function formatDuration(start, end) {
|
|
|
4213
4728
|
const remainSecs = secs % 60;
|
|
4214
4729
|
return `${mins}m ${remainSecs}s`;
|
|
4215
4730
|
}
|
|
4216
|
-
function
|
|
4217
|
-
const
|
|
4218
|
-
return [
|
|
4219
|
-
`#### ${f.title}`,
|
|
4220
|
-
`- **Resource:** ${f.resourceId} (\`${f.resourceArn}\`)`,
|
|
4221
|
-
`- **Description:** ${f.description}`,
|
|
4222
|
-
`- **Impact:** ${f.impact}`,
|
|
4223
|
-
`- **Risk Score:** ${f.riskScore}/10`,
|
|
4224
|
-
`- **Remediation:**`,
|
|
4225
|
-
steps,
|
|
4226
|
-
`- **Priority:** ${f.priority}`
|
|
4227
|
-
].join("\n");
|
|
4228
|
-
}
|
|
4229
|
-
function generateMarkdownReport(scanResults) {
|
|
4731
|
+
function generateMarkdownReport(scanResults, lang) {
|
|
4732
|
+
const t = getI18n(lang ?? "zh");
|
|
4230
4733
|
const { summary, modules, accountId, region, scanStart, scanEnd } = scanResults;
|
|
4231
4734
|
const date = scanStart.split("T")[0];
|
|
4232
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
|
+
}
|
|
4233
4755
|
const lines = [];
|
|
4234
|
-
lines.push(`#
|
|
4756
|
+
lines.push(`# ${t.securityReportTitle} \u2014 ${date}`);
|
|
4235
4757
|
lines.push("");
|
|
4236
|
-
lines.push(
|
|
4237
|
-
lines.push(`-
|
|
4238
|
-
lines.push(`-
|
|
4239
|
-
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}`);
|
|
4240
4762
|
lines.push(
|
|
4241
|
-
`-
|
|
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})`
|
|
4242
4764
|
);
|
|
4243
4765
|
lines.push("");
|
|
4244
4766
|
if (summary.totalFindings === 0) {
|
|
4245
|
-
lines.push(
|
|
4767
|
+
lines.push(`## ${t.findingsBySeverity}`);
|
|
4246
4768
|
lines.push("");
|
|
4247
|
-
lines.push(
|
|
4769
|
+
lines.push(`\u2705 ${t.noIssuesFound}`);
|
|
4248
4770
|
lines.push("");
|
|
4249
4771
|
} else {
|
|
4250
4772
|
const allFindings = modules.flatMap((m) => m.findings);
|
|
@@ -4255,15 +4777,15 @@ function generateMarkdownReport(scanResults) {
|
|
|
4255
4777
|
for (const f of allFindings) {
|
|
4256
4778
|
grouped.get(f.severity).push(f);
|
|
4257
4779
|
}
|
|
4258
|
-
lines.push(
|
|
4780
|
+
lines.push(`## ${t.findingsBySeverity}`);
|
|
4259
4781
|
lines.push("");
|
|
4260
4782
|
for (const sev of SEVERITY_ORDER) {
|
|
4261
4783
|
const findings = grouped.get(sev);
|
|
4262
4784
|
const icon = SEVERITY_ICON[sev];
|
|
4263
|
-
lines.push(`### ${icon} ${sev
|
|
4785
|
+
lines.push(`### ${icon} ${sevLabel[sev]}`);
|
|
4264
4786
|
lines.push("");
|
|
4265
4787
|
if (findings.length === 0) {
|
|
4266
|
-
lines.push(
|
|
4788
|
+
lines.push(t.noFindingsForSeverity(sevLabel[sev]));
|
|
4267
4789
|
lines.push("");
|
|
4268
4790
|
continue;
|
|
4269
4791
|
}
|
|
@@ -4274,9 +4796,9 @@ function generateMarkdownReport(scanResults) {
|
|
|
4274
4796
|
}
|
|
4275
4797
|
}
|
|
4276
4798
|
}
|
|
4277
|
-
lines.push(
|
|
4799
|
+
lines.push(`## ${t.scanStatistics}`);
|
|
4278
4800
|
lines.push(
|
|
4279
|
-
|
|
4801
|
+
`| ${t.module} | ${t.resources} | ${t.findings} | ${t.status} |`
|
|
4280
4802
|
);
|
|
4281
4803
|
lines.push("|--------|------------------|----------|--------|");
|
|
4282
4804
|
for (const m of modules) {
|
|
@@ -4289,7 +4811,7 @@ function generateMarkdownReport(scanResults) {
|
|
|
4289
4811
|
if (summary.totalFindings > 0) {
|
|
4290
4812
|
const allFindings = modules.flatMap((m) => m.findings);
|
|
4291
4813
|
allFindings.sort((a, b) => b.riskScore - a.riskScore);
|
|
4292
|
-
lines.push(
|
|
4814
|
+
lines.push(`## ${t.recommendations}`);
|
|
4293
4815
|
for (let i = 0; i < allFindings.length; i++) {
|
|
4294
4816
|
const f = allFindings[i];
|
|
4295
4817
|
lines.push(`${i + 1}. [${f.priority}] ${f.title}: ${f.remediationSteps[0] ?? "Review and remediate."}`);
|
|
@@ -6336,13 +6858,6 @@ var MLPS3_CATEGORY_ORDER = [
|
|
|
6336
6858
|
"\u5B89\u5168\u8BA1\u7B97\u73AF\u5883",
|
|
6337
6859
|
"\u5B89\u5168\u7BA1\u7406\u4E2D\u5FC3"
|
|
6338
6860
|
];
|
|
6339
|
-
var MLPS3_CATEGORY_SECTION = {
|
|
6340
|
-
"\u5B89\u5168\u7269\u7406\u73AF\u5883": "\u4E00\u3001\u5B89\u5168\u7269\u7406\u73AF\u5883",
|
|
6341
|
-
"\u5B89\u5168\u901A\u4FE1\u7F51\u7EDC": "\u4E8C\u3001\u5B89\u5168\u901A\u4FE1\u7F51\u7EDC",
|
|
6342
|
-
"\u5B89\u5168\u533A\u57DF\u8FB9\u754C": "\u4E09\u3001\u5B89\u5168\u533A\u57DF\u8FB9\u754C",
|
|
6343
|
-
"\u5B89\u5168\u8BA1\u7B97\u73AF\u5883": "\u56DB\u3001\u5B89\u5168\u8BA1\u7B97\u73AF\u5883",
|
|
6344
|
-
"\u5B89\u5168\u7BA1\u7406\u4E2D\u5FC3": "\u4E94\u3001\u5B89\u5168\u7BA1\u7406\u4E2D\u5FC3"
|
|
6345
|
-
};
|
|
6346
6861
|
var MLPS3_CHECK_MAPPING = [
|
|
6347
6862
|
// =========================================================================
|
|
6348
6863
|
// 安全物理环境 — L3-PES1-* (22 items) → cloud_provider
|
|
@@ -7002,7 +7517,9 @@ function evaluateAllFullChecks(scanResults) {
|
|
|
7002
7517
|
return evaluateFullCheck(item, mapping, allFindings, scanModules);
|
|
7003
7518
|
});
|
|
7004
7519
|
}
|
|
7005
|
-
function generateMlps3Report(scanResults) {
|
|
7520
|
+
function generateMlps3Report(scanResults, lang) {
|
|
7521
|
+
const t = getI18n(lang ?? "zh");
|
|
7522
|
+
const isEn = (lang ?? "zh") === "en";
|
|
7006
7523
|
const { accountId, region, scanStart } = scanResults;
|
|
7007
7524
|
const scanTime = scanStart.replace("T", " ").replace(/\.\d+Z$/, " UTC");
|
|
7008
7525
|
const results = evaluateAllFullChecks(scanResults);
|
|
@@ -7014,27 +7531,28 @@ function generateMlps3Report(scanResults) {
|
|
|
7014
7531
|
const cloudCount = results.filter((r) => r.status === "cloud_provider").length;
|
|
7015
7532
|
const manualCount = results.filter((r) => r.status === "manual").length;
|
|
7016
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;
|
|
7017
7536
|
const lines = [];
|
|
7018
|
-
lines.push(
|
|
7019
|
-
lines.push(
|
|
7020
|
-
lines.push("> **\uFF08GB/T 22239-2019 \u5B8C\u6574\u68C0\u67E5\u6E05\u5355 184 \u9879\uFF09**");
|
|
7537
|
+
lines.push(`# ${t.mlpsTitle}`);
|
|
7538
|
+
lines.push(`> **${t.mlpsDisclaimer}**`);
|
|
7021
7539
|
lines.push("");
|
|
7022
|
-
lines.push(
|
|
7023
|
-
lines.push(`-
|
|
7540
|
+
lines.push(`## ${t.accountInfo}`);
|
|
7541
|
+
lines.push(`- ${t.account}: ${accountId} | ${t.region}: ${region} | ${t.scanTime}: ${scanTime}`);
|
|
7024
7542
|
lines.push("");
|
|
7025
|
-
lines.push(
|
|
7026
|
-
lines.push(`-
|
|
7543
|
+
lines.push(`## ${t.preCheckOverview}`);
|
|
7544
|
+
lines.push(`- ${t.checkedCount(checkedTotal, autoClean, autoIssues)}`);
|
|
7027
7545
|
if (autoUnknown > 0) {
|
|
7028
|
-
lines.push(`-
|
|
7546
|
+
lines.push(`- ${t.uncheckedCount(autoUnknown)}`);
|
|
7029
7547
|
}
|
|
7030
|
-
lines.push(`-
|
|
7031
|
-
lines.push(`-
|
|
7548
|
+
lines.push(`- ${t.cloudProviderCount(cloudCount)}`);
|
|
7549
|
+
lines.push(`- ${t.manualReviewCount(manualCount)}`);
|
|
7032
7550
|
if (naCount > 0) {
|
|
7033
|
-
lines.push(`-
|
|
7551
|
+
lines.push(`- ${t.naCount(naCount)}`);
|
|
7034
7552
|
}
|
|
7035
7553
|
lines.push("");
|
|
7036
7554
|
for (const category of MLPS3_CATEGORY_ORDER) {
|
|
7037
|
-
const sectionTitle =
|
|
7555
|
+
const sectionTitle = t.mlpsCategorySection[category] ?? category;
|
|
7038
7556
|
const catResults = results.filter(
|
|
7039
7557
|
(r) => r.item.categoryCn === category && r.status !== "not_applicable"
|
|
7040
7558
|
);
|
|
@@ -7047,18 +7565,20 @@ function generateMlps3Report(scanResults) {
|
|
|
7047
7565
|
if (!controlMap.has(key)) controlMap.set(key, []);
|
|
7048
7566
|
controlMap.get(key).push(r);
|
|
7049
7567
|
}
|
|
7050
|
-
for (const [
|
|
7568
|
+
for (const [_controlKey, controlResults] of controlMap) {
|
|
7569
|
+
const controlName = itemControl(controlResults[0]);
|
|
7051
7570
|
lines.push(`### ${controlName}`);
|
|
7052
7571
|
for (const r of controlResults) {
|
|
7053
7572
|
const icon = r.status === "clean" ? "\u2705" : r.status === "issues" ? "\u274C" : r.status === "unknown" ? "\u26A0\uFE0F" : r.status === "manual" ? "\u{1F4CB}" : "\u{1F3E2}";
|
|
7054
|
-
const suffix = r.status === "unknown" ?
|
|
7055
|
-
|
|
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}`);
|
|
7056
7576
|
if (r.status === "issues" && r.relatedFindings.length > 0) {
|
|
7057
7577
|
for (const f of r.relatedFindings.slice(0, 3)) {
|
|
7058
7578
|
lines.push(` - ${f.severity}: ${f.title}`);
|
|
7059
7579
|
}
|
|
7060
7580
|
if (r.relatedFindings.length > 3) {
|
|
7061
|
-
lines.push(` -
|
|
7581
|
+
lines.push(` - ${t.andMore(r.relatedFindings.length - 3)}`);
|
|
7062
7582
|
}
|
|
7063
7583
|
}
|
|
7064
7584
|
}
|
|
@@ -7067,7 +7587,7 @@ function generateMlps3Report(scanResults) {
|
|
|
7067
7587
|
}
|
|
7068
7588
|
const failedResults = results.filter((r) => r.status === "issues");
|
|
7069
7589
|
if (failedResults.length > 0) {
|
|
7070
|
-
lines.push(
|
|
7590
|
+
lines.push(`## ${t.remediationByPriority}`);
|
|
7071
7591
|
lines.push("");
|
|
7072
7592
|
const allFailedFindings = /* @__PURE__ */ new Map();
|
|
7073
7593
|
for (const r of failedResults) {
|
|
@@ -7090,7 +7610,7 @@ function generateMlps3Report(scanResults) {
|
|
|
7090
7610
|
lines.push("");
|
|
7091
7611
|
}
|
|
7092
7612
|
if (naCount > 0) {
|
|
7093
|
-
lines.push(`>
|
|
7613
|
+
lines.push(`> ${t.naNote(naCount)}`);
|
|
7094
7614
|
lines.push("");
|
|
7095
7615
|
}
|
|
7096
7616
|
return lines.join("\n");
|
|
@@ -7100,6 +7620,15 @@ function generateMlps3Report(scanResults) {
|
|
|
7100
7620
|
function esc(s) {
|
|
7101
7621
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
7102
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
|
+
}
|
|
7103
7632
|
function calcScore(summary) {
|
|
7104
7633
|
const raw = 100 - summary.critical * 15 - summary.high * 5 - summary.medium * 2 - summary.low * 0.5;
|
|
7105
7634
|
return Math.max(0, Math.min(100, Math.round(raw)));
|
|
@@ -7123,50 +7652,6 @@ function scoreColor(score) {
|
|
|
7123
7652
|
if (score >= 50) return "#eab308";
|
|
7124
7653
|
return "#ef4444";
|
|
7125
7654
|
}
|
|
7126
|
-
var SERVICE_RECOMMENDATIONS = {
|
|
7127
|
-
security_hub_findings: {
|
|
7128
|
-
icon: "\u{1F534}",
|
|
7129
|
-
service: "Security Hub",
|
|
7130
|
-
impact: "\u65E0\u6CD5\u83B7\u53D6 300+ \u9879\u81EA\u52A8\u5316\u5B89\u5168\u68C0\u67E5\uFF08FSBP/CIS/PCI DSS \u6807\u51C6\uFF09",
|
|
7131
|
-
action: "\u542F\u7528 Security Hub \u83B7\u5F97\u6700\u5168\u9762\u7684\u5B89\u5168\u6001\u52BF\u8BC4\u4F30"
|
|
7132
|
-
},
|
|
7133
|
-
guardduty_findings: {
|
|
7134
|
-
icon: "\u{1F534}",
|
|
7135
|
-
service: "GuardDuty",
|
|
7136
|
-
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",
|
|
7137
|
-
action: "\u542F\u7528 GuardDuty \u83B7\u5F97\u6301\u7EED\u5A01\u80C1\u68C0\u6D4B\u80FD\u529B"
|
|
7138
|
-
},
|
|
7139
|
-
inspector_findings: {
|
|
7140
|
-
icon: "\u{1F7E1}",
|
|
7141
|
-
service: "Inspector",
|
|
7142
|
-
impact: "\u65E0\u6CD5\u626B\u63CF EC2/Lambda/\u5BB9\u5668\u7684\u8F6F\u4EF6\u6F0F\u6D1E\uFF08CVE\uFF09",
|
|
7143
|
-
action: "\u542F\u7528 Inspector \u53D1\u73B0\u5DF2\u77E5\u5B89\u5168\u6F0F\u6D1E"
|
|
7144
|
-
},
|
|
7145
|
-
trusted_advisor_findings: {
|
|
7146
|
-
icon: "\u{1F7E1}",
|
|
7147
|
-
service: "Trusted Advisor",
|
|
7148
|
-
impact: "\u65E0\u6CD5\u83B7\u53D6 AWS \u6700\u4F73\u5B9E\u8DF5\u5B89\u5168\u68C0\u67E5",
|
|
7149
|
-
action: "\u5347\u7EA7\u81F3 Business/Enterprise Support \u8BA1\u5212\u4EE5\u4F7F\u7528 Trusted Advisor \u5B89\u5168\u68C0\u67E5"
|
|
7150
|
-
},
|
|
7151
|
-
config_rules_findings: {
|
|
7152
|
-
icon: "\u{1F7E1}",
|
|
7153
|
-
service: "AWS Config",
|
|
7154
|
-
impact: "\u65E0\u6CD5\u68C0\u67E5\u8D44\u6E90\u914D\u7F6E\u5408\u89C4\u72B6\u6001",
|
|
7155
|
-
action: "\u542F\u7528 AWS Config \u5E76\u914D\u7F6E Config Rules"
|
|
7156
|
-
},
|
|
7157
|
-
access_analyzer_findings: {
|
|
7158
|
-
icon: "\u{1F7E1}",
|
|
7159
|
-
service: "IAM Access Analyzer",
|
|
7160
|
-
impact: "\u65E0\u6CD5\u68C0\u6D4B\u8D44\u6E90\u662F\u5426\u88AB\u5916\u90E8\u8D26\u53F7\u6216\u516C\u7F51\u8BBF\u95EE",
|
|
7161
|
-
action: "\u521B\u5EFA IAM Access Analyzer\uFF08\u8D26\u6237\u7EA7\u6216\u7EC4\u7EC7\u7EA7\uFF09"
|
|
7162
|
-
},
|
|
7163
|
-
patch_compliance_findings: {
|
|
7164
|
-
icon: "\u{1F7E1}",
|
|
7165
|
-
service: "SSM Patch Manager",
|
|
7166
|
-
impact: "\u65E0\u6CD5\u68C0\u67E5\u5B9E\u4F8B\u8865\u4E01\u5408\u89C4\u72B6\u6001",
|
|
7167
|
-
action: "\u5B89\u88C5 SSM Agent \u5E76\u914D\u7F6E Patch Manager"
|
|
7168
|
-
}
|
|
7169
|
-
};
|
|
7170
7655
|
var SERVICE_NOT_ENABLED_PATTERNS = [
|
|
7171
7656
|
"not enabled",
|
|
7172
7657
|
"not found",
|
|
@@ -7176,10 +7661,11 @@ var SERVICE_NOT_ENABLED_PATTERNS = [
|
|
|
7176
7661
|
"not available",
|
|
7177
7662
|
"is not enabled"
|
|
7178
7663
|
];
|
|
7179
|
-
function getDisabledServices(modules) {
|
|
7664
|
+
function getDisabledServices(modules, lang) {
|
|
7665
|
+
const t = getI18n(lang ?? "zh");
|
|
7180
7666
|
const disabled = [];
|
|
7181
7667
|
for (const mod of modules) {
|
|
7182
|
-
const rec =
|
|
7668
|
+
const rec = t.serviceRecommendations[mod.module];
|
|
7183
7669
|
if (!rec) continue;
|
|
7184
7670
|
if (!mod.warnings?.length) continue;
|
|
7185
7671
|
const hasNotEnabled = mod.warnings.some(
|
|
@@ -7191,21 +7677,22 @@ function getDisabledServices(modules) {
|
|
|
7191
7677
|
}
|
|
7192
7678
|
return disabled;
|
|
7193
7679
|
}
|
|
7194
|
-
function buildServiceReminderHtml(modules) {
|
|
7195
|
-
const
|
|
7680
|
+
function buildServiceReminderHtml(modules, lang) {
|
|
7681
|
+
const t = getI18n(lang ?? "zh");
|
|
7682
|
+
const disabled = getDisabledServices(modules, lang);
|
|
7196
7683
|
if (disabled.length === 0) return "";
|
|
7197
7684
|
const items = disabled.map((svc) => `
|
|
7198
7685
|
<div style="margin-bottom:12px">
|
|
7199
|
-
<div style="font-weight:600;font-size:15px">${esc(svc.icon)} ${esc(svc.service)}
|
|
7200
|
-
<div style="margin-left:28px;color:#cbd5e1;font-size:13px"
|
|
7201
|
-
<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>
|
|
7202
7689
|
</div>`).join("\n");
|
|
7203
7690
|
return `
|
|
7204
7691
|
<section>
|
|
7205
7692
|
<div style="background:#2d1f00;border:1px solid #b45309;border-radius:8px;padding:20px;margin-bottom:32px">
|
|
7206
|
-
<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>
|
|
7207
7694
|
${items}
|
|
7208
|
-
<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>
|
|
7209
7696
|
</div>
|
|
7210
7697
|
</section>`;
|
|
7211
7698
|
}
|
|
@@ -7407,12 +7894,12 @@ function donutChart(summary) {
|
|
|
7407
7894
|
"</svg>"
|
|
7408
7895
|
].join("\n");
|
|
7409
7896
|
}
|
|
7410
|
-
function barChart(modules) {
|
|
7897
|
+
function barChart(modules, allCleanLabel = "All modules clean") {
|
|
7411
7898
|
const withFindings = modules.filter((m) => m.findingsCount > 0).sort((a, b) => b.findingsCount - a.findingsCount).slice(0, 12);
|
|
7412
7899
|
if (withFindings.length === 0) {
|
|
7413
7900
|
return [
|
|
7414
7901
|
'<svg viewBox="0 0 400 50" width="100%">',
|
|
7415
|
-
|
|
7902
|
+
` <text x="200" y="30" text-anchor="middle" fill="#22c55e" font-size="14" font-weight="600">${esc(allCleanLabel)}</text>`,
|
|
7416
7903
|
"</svg>"
|
|
7417
7904
|
].join("\n");
|
|
7418
7905
|
}
|
|
@@ -7527,7 +8014,9 @@ function scoreTrendChart(history) {
|
|
|
7527
8014
|
"</svg>"
|
|
7528
8015
|
].join("\n");
|
|
7529
8016
|
}
|
|
7530
|
-
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";
|
|
7531
8020
|
const { summary, modules, accountId, region, scanStart, scanEnd } = scanResults;
|
|
7532
8021
|
const date = scanStart.split("T")[0];
|
|
7533
8022
|
const duration = formatDuration2(scanStart, scanEnd);
|
|
@@ -7545,23 +8034,23 @@ function generateHtmlReport(scanResults, history) {
|
|
|
7545
8034
|
<div class="top5-content">
|
|
7546
8035
|
<span class="badge badge-${esc(f.severity.toLowerCase())}">${esc(f.severity)}</span>
|
|
7547
8036
|
<div class="top5-title">${esc(f.title)}</div>
|
|
7548
|
-
<div class="top5-detail"><strong
|
|
7549
|
-
<div class="top5-detail"><strong
|
|
7550
|
-
<div class="top5-detail"><strong
|
|
7551
|
-
<h4
|
|
7552
|
-
<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>
|
|
7553
8042
|
</div>
|
|
7554
8043
|
</div>`
|
|
7555
8044
|
).join("\n");
|
|
7556
8045
|
top5Html = `
|
|
7557
8046
|
<section>
|
|
7558
|
-
<h2
|
|
8047
|
+
<h2>${esc(t.topHighestRiskFindings(top5.length))}</h2>
|
|
7559
8048
|
${cards}
|
|
7560
8049
|
</section>`;
|
|
7561
8050
|
}
|
|
7562
8051
|
let findingsHtml;
|
|
7563
8052
|
if (summary.totalFindings === 0) {
|
|
7564
|
-
findingsHtml =
|
|
8053
|
+
findingsHtml = `<div class="no-findings">${esc(t.noIssuesFound)}</div>`;
|
|
7565
8054
|
} else {
|
|
7566
8055
|
const FOLD_THRESHOLD = 20;
|
|
7567
8056
|
const renderCard = (f) => {
|
|
@@ -7570,10 +8059,10 @@ function generateHtmlReport(scanResults, history) {
|
|
|
7570
8059
|
<span class="badge badge-${esc(sev)}">${esc(f.severity)}</span>
|
|
7571
8060
|
<span class="finding-title-text">${esc(f.title)}</span>
|
|
7572
8061
|
<span class="finding-resource">${esc(f.resourceArn || f.resourceId)}</span>
|
|
7573
|
-
<details><summary
|
|
8062
|
+
<details><summary>${t.details}</summary><div class="finding-card-body">
|
|
7574
8063
|
<p>${esc(f.description)}</p>
|
|
7575
|
-
<p><strong
|
|
7576
|
-
<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>
|
|
7577
8066
|
</div></details>
|
|
7578
8067
|
</div>`;
|
|
7579
8068
|
};
|
|
@@ -7584,7 +8073,7 @@ function generateHtmlReport(scanResults, history) {
|
|
|
7584
8073
|
const first = findings.slice(0, FOLD_THRESHOLD).map(renderCard).join("\n");
|
|
7585
8074
|
const rest = findings.slice(FOLD_THRESHOLD).map(renderCard).join("\n");
|
|
7586
8075
|
return `${first}
|
|
7587
|
-
<details><summary
|
|
8076
|
+
<details><summary>${t.showRemainingFindings(findings.length - FOLD_THRESHOLD)}</summary>
|
|
7588
8077
|
${rest}
|
|
7589
8078
|
</details>`;
|
|
7590
8079
|
};
|
|
@@ -7636,13 +8125,13 @@ ${rest}
|
|
|
7636
8125
|
if (history && history.length >= 2) {
|
|
7637
8126
|
trendHtml = `
|
|
7638
8127
|
<section class="trend-section">
|
|
7639
|
-
<h2
|
|
8128
|
+
<h2>${esc(t.trendTitle)}</h2>
|
|
7640
8129
|
<div class="trend-chart">
|
|
7641
|
-
<div class="trend-title"
|
|
8130
|
+
<div class="trend-title">${esc(t.findingsBySeverity)}</div>
|
|
7642
8131
|
${findingsTrendChart(history)}
|
|
7643
8132
|
</div>
|
|
7644
8133
|
<div class="trend-chart">
|
|
7645
|
-
<div class="trend-title"
|
|
8134
|
+
<div class="trend-title">${esc(t.securityScore)}</div>
|
|
7646
8135
|
${scoreTrendChart(history)}
|
|
7647
8136
|
</div>
|
|
7648
8137
|
</section>`;
|
|
@@ -7653,16 +8142,57 @@ ${rest}
|
|
|
7653
8142
|
let recsHtml = "";
|
|
7654
8143
|
if (summary.totalFindings > 0) {
|
|
7655
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."];
|
|
7656
8149
|
for (const f of allFindings) {
|
|
7657
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
|
+
}
|
|
7658
8176
|
const existing = recMap.get(rem);
|
|
7659
8177
|
if (existing) {
|
|
7660
8178
|
existing.count++;
|
|
8179
|
+
if (!existing.url && url) existing.url = url;
|
|
7661
8180
|
if (SEVERITY_ORDER2.indexOf(f.severity) < SEVERITY_ORDER2.indexOf(existing.severity)) {
|
|
7662
8181
|
existing.severity = f.severity;
|
|
7663
8182
|
}
|
|
7664
8183
|
} else {
|
|
7665
|
-
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;
|
|
7666
8196
|
}
|
|
7667
8197
|
}
|
|
7668
8198
|
const uniqueRecs = [...recMap.values()].sort((a, b) => {
|
|
@@ -7673,60 +8203,61 @@ ${rest}
|
|
|
7673
8203
|
const renderRec = (r) => {
|
|
7674
8204
|
const sev = r.severity.toLowerCase();
|
|
7675
8205
|
const countLabel = r.count > 1 ? ` (× ${r.count})` : "";
|
|
7676
|
-
|
|
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>`;
|
|
7677
8208
|
};
|
|
7678
8209
|
const TOP_N = 10;
|
|
7679
8210
|
const topItems = uniqueRecs.slice(0, TOP_N).map(renderRec).join("\n");
|
|
7680
8211
|
const remaining = uniqueRecs.slice(TOP_N);
|
|
7681
8212
|
const moreHtml = remaining.length > 0 ? `
|
|
7682
|
-
<details><summary
|
|
8213
|
+
<details><summary>${t.showMoreCount(remaining.length)}</summary>
|
|
7683
8214
|
${remaining.map(renderRec).join("\n")}
|
|
7684
8215
|
</details>` : "";
|
|
7685
8216
|
recsHtml = `
|
|
7686
8217
|
<details class="rec-fold">
|
|
7687
|
-
<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>
|
|
7688
8219
|
<div class="rec-body">
|
|
7689
8220
|
<ol>${topItems}${moreHtml}</ol>
|
|
7690
8221
|
</div>
|
|
7691
8222
|
</details>`;
|
|
7692
8223
|
}
|
|
7693
8224
|
return `<!DOCTYPE html>
|
|
7694
|
-
<html lang="
|
|
8225
|
+
<html lang="${htmlLang}">
|
|
7695
8226
|
<head>
|
|
7696
8227
|
<meta charset="UTF-8">
|
|
7697
8228
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
7698
|
-
<title
|
|
8229
|
+
<title>${esc(t.securityReportTitle)} — ${esc(date)}</title>
|
|
7699
8230
|
<style>${sharedCss()}</style>
|
|
7700
8231
|
</head>
|
|
7701
8232
|
<body>
|
|
7702
8233
|
<div class="container">
|
|
7703
8234
|
|
|
7704
8235
|
<header>
|
|
7705
|
-
<h1>🛡️
|
|
7706
|
-
<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>
|
|
7707
8238
|
</header>
|
|
7708
8239
|
|
|
7709
8240
|
<section class="summary">
|
|
7710
8241
|
<div class="score-card">
|
|
7711
8242
|
<div class="score-value" style="color:${scoreColor(score)}">${score}</div>
|
|
7712
|
-
<div class="score-label"
|
|
8243
|
+
<div class="score-label">${esc(t.securityScore)}</div>
|
|
7713
8244
|
</div>
|
|
7714
8245
|
<div class="severity-stats">
|
|
7715
|
-
<div class="stat-card stat-critical"><div class="stat-count">${summary.critical}</div><div class="stat-label"
|
|
7716
|
-
<div class="stat-card stat-high"><div class="stat-count">${summary.high}</div><div class="stat-label"
|
|
7717
|
-
<div class="stat-card stat-medium"><div class="stat-count">${summary.medium}</div><div class="stat-label"
|
|
7718
|
-
<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>
|
|
7719
8250
|
</div>
|
|
7720
8251
|
</section>
|
|
7721
8252
|
|
|
7722
8253
|
<section class="charts">
|
|
7723
8254
|
<div class="chart-box">
|
|
7724
|
-
<div class="chart-title"
|
|
8255
|
+
<div class="chart-title">${esc(t.severityDistribution)}</div>
|
|
7725
8256
|
<div style="text-align:center">${donutChart(summary)}</div>
|
|
7726
8257
|
</div>
|
|
7727
8258
|
<div class="chart-box">
|
|
7728
|
-
<div class="chart-title"
|
|
7729
|
-
${barChart(modules)}
|
|
8259
|
+
<div class="chart-title">${esc(t.findingsByModule)}</div>
|
|
8260
|
+
${barChart(modules, t.allModulesClean)}
|
|
7730
8261
|
</div>
|
|
7731
8262
|
</section>
|
|
7732
8263
|
|
|
@@ -7734,33 +8265,35 @@ ${trendHtml}
|
|
|
7734
8265
|
|
|
7735
8266
|
${top5Html}
|
|
7736
8267
|
|
|
7737
|
-
${buildServiceReminderHtml(modules)}
|
|
8268
|
+
${buildServiceReminderHtml(modules, lang)}
|
|
7738
8269
|
|
|
7739
8270
|
<section>
|
|
7740
|
-
<h2
|
|
8271
|
+
<h2>${esc(t.scanStatistics)}</h2>
|
|
7741
8272
|
<table>
|
|
7742
|
-
<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>
|
|
7743
8274
|
<tbody>${statsRows}</tbody>
|
|
7744
8275
|
</table>
|
|
7745
8276
|
</section>
|
|
7746
8277
|
|
|
7747
8278
|
<section>
|
|
7748
|
-
<h2
|
|
8279
|
+
<h2>${esc(t.allFindings)}</h2>
|
|
7749
8280
|
${findingsHtml}
|
|
7750
8281
|
</section>
|
|
7751
8282
|
|
|
7752
8283
|
${recsHtml}
|
|
7753
8284
|
|
|
7754
8285
|
<footer>
|
|
7755
|
-
<p
|
|
7756
|
-
<p
|
|
8286
|
+
<p>${esc(t.generatedBy)} v${VERSION}</p>
|
|
8287
|
+
<p>${esc(t.informationalOnly)}</p>
|
|
7757
8288
|
</footer>
|
|
7758
8289
|
|
|
7759
8290
|
</div>
|
|
7760
8291
|
</body>
|
|
7761
8292
|
</html>`;
|
|
7762
8293
|
}
|
|
7763
|
-
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";
|
|
7764
8297
|
const { accountId, region, scanStart } = scanResults;
|
|
7765
8298
|
const date = scanStart.split("T")[0];
|
|
7766
8299
|
const scanTime = scanStart.replace("T", " ").replace(/\.\d+Z$/, " UTC");
|
|
@@ -7777,17 +8310,21 @@ function generateMlps3HtmlReport(scanResults, history) {
|
|
|
7777
8310
|
if (history && history.length >= 2) {
|
|
7778
8311
|
trendHtml = `
|
|
7779
8312
|
<section class="trend-section">
|
|
7780
|
-
<h2
|
|
8313
|
+
<h2>${esc(t.trendTitle)}</h2>
|
|
7781
8314
|
<div class="trend-chart">
|
|
7782
|
-
<div class="trend-title"
|
|
8315
|
+
<div class="trend-title">${esc(t.findingsBySeverity)}</div>
|
|
7783
8316
|
${findingsTrendChart(history)}
|
|
7784
8317
|
</div>
|
|
7785
8318
|
<div class="trend-chart">
|
|
7786
|
-
<div class="trend-title"
|
|
8319
|
+
<div class="trend-title">${esc(t.securityScore)}</div>
|
|
7787
8320
|
${scoreTrendChart(history)}
|
|
7788
8321
|
</div>
|
|
7789
8322
|
</section>`;
|
|
7790
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;
|
|
7791
8328
|
const categoryMap = /* @__PURE__ */ new Map();
|
|
7792
8329
|
for (const r of results) {
|
|
7793
8330
|
if (r.status === "not_applicable") continue;
|
|
@@ -7796,7 +8333,7 @@ function generateMlps3HtmlReport(scanResults, history) {
|
|
|
7796
8333
|
categoryMap.get(cat).push(r);
|
|
7797
8334
|
}
|
|
7798
8335
|
const categorySections = MLPS3_CATEGORY_ORDER.map((category) => {
|
|
7799
|
-
const sectionTitle =
|
|
8336
|
+
const sectionTitle = t.mlpsCategorySection[category] ?? category;
|
|
7800
8337
|
const catResults = categoryMap.get(category);
|
|
7801
8338
|
if (!catResults || catResults.length === 0) return "";
|
|
7802
8339
|
const allCloud = catResults.every((r) => r.status === "cloud_provider");
|
|
@@ -7804,11 +8341,11 @@ function generateMlps3HtmlReport(scanResults, history) {
|
|
|
7804
8341
|
return `<details class="category-fold mlps-cloud-section">
|
|
7805
8342
|
<summary>
|
|
7806
8343
|
<span class="category-title">${esc(sectionTitle)}</span>
|
|
7807
|
-
<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>
|
|
7808
8345
|
</summary>
|
|
7809
8346
|
<div class="category-body">
|
|
7810
|
-
<div class="mlps-cloud-note"
|
|
7811
|
-
${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")}
|
|
7812
8349
|
</div>
|
|
7813
8350
|
</details>`;
|
|
7814
8351
|
}
|
|
@@ -7830,31 +8367,34 @@ function generateMlps3HtmlReport(scanResults, history) {
|
|
|
7830
8367
|
if (!controlMap.has(key)) controlMap.set(key, []);
|
|
7831
8368
|
controlMap.get(key).push(r);
|
|
7832
8369
|
}
|
|
7833
|
-
const controlGroups = [...controlMap.entries()].map(([
|
|
8370
|
+
const controlGroups = [...controlMap.entries()].map(([_controlKey, controlResults]) => {
|
|
8371
|
+
const controlName = itemControl(controlResults[0]);
|
|
7834
8372
|
const cloudItems = controlResults.filter((r) => r.status === "cloud_provider");
|
|
7835
8373
|
const nonCloudItems = controlResults.filter((r) => r.status !== "cloud_provider");
|
|
7836
8374
|
let itemsHtml = "";
|
|
7837
8375
|
for (const r of nonCloudItems) {
|
|
7838
8376
|
const icon = r.status === "clean" ? "\u{1F7E2}" : r.status === "issues" ? "\u{1F534}" : r.status === "unknown" ? "\u2B1C" : r.status === "manual" ? "\u{1F4CB}" : "\u{1F3E2}";
|
|
7839
8377
|
const cls = `check-${r.status === "cloud_provider" ? "cloud" : r.status}`;
|
|
7840
|
-
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)}` : "";
|
|
7841
8379
|
let findingsDetail = "";
|
|
7842
8380
|
if (r.status === "clean") {
|
|
7843
|
-
findingsDetail = `<div class="check-detail"
|
|
8381
|
+
findingsDetail = `<div class="check-detail">${esc(t.noRelatedIssues)}</div>`;
|
|
7844
8382
|
} else if (r.status === "issues" && r.relatedFindings.length > 0) {
|
|
7845
8383
|
const fItems = r.relatedFindings.slice(0, 5).map((f) => `<li>${esc(f.severity)}: ${esc(f.title)}</li>`);
|
|
7846
8384
|
if (r.relatedFindings.length > 5) {
|
|
7847
|
-
fItems.push(`<li
|
|
8385
|
+
fItems.push(`<li>${esc(t.andMore(r.relatedFindings.length - 5))}</li>`);
|
|
7848
8386
|
}
|
|
7849
|
-
const remediationHint = r.relatedFindings[0]?.remediationSteps?.[0] ? `<p style="color:#fbbf24;font-size:12px;margin-top:4px"
|
|
7850
|
-
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>`;
|
|
7851
8389
|
}
|
|
7852
|
-
|
|
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>
|
|
7853
8392
|
${findingsDetail}`;
|
|
7854
8393
|
}
|
|
7855
8394
|
if (cloudItems.length > 0) {
|
|
7856
8395
|
for (const r of cloudItems) {
|
|
7857
|
-
|
|
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>
|
|
7858
8398
|
`;
|
|
7859
8399
|
}
|
|
7860
8400
|
}
|
|
@@ -7887,20 +8427,61 @@ ${itemsHtml}
|
|
|
7887
8427
|
let remediationHtml = "";
|
|
7888
8428
|
if (failedResults.length > 0) {
|
|
7889
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."];
|
|
7890
8434
|
for (const r of failedResults) {
|
|
7891
8435
|
for (const f of r.relatedFindings) {
|
|
7892
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
|
+
}
|
|
7893
8462
|
const existing = mlpsRecMap.get(rem);
|
|
7894
8463
|
if (existing) {
|
|
7895
8464
|
existing.count++;
|
|
8465
|
+
if (!existing.url && url) existing.url = url;
|
|
7896
8466
|
if (SEVERITY_ORDER2.indexOf(f.severity) < SEVERITY_ORDER2.indexOf(existing.severity)) {
|
|
7897
8467
|
existing.severity = f.severity;
|
|
7898
8468
|
}
|
|
7899
8469
|
} else {
|
|
7900
|
-
mlpsRecMap.set(rem, { text: rem, severity: f.severity, count: 1 });
|
|
8470
|
+
mlpsRecMap.set(rem, { text: rem, severity: f.severity, count: 1, url });
|
|
7901
8471
|
}
|
|
7902
8472
|
}
|
|
7903
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
|
+
}
|
|
7904
8485
|
const mlpsUniqueRecs = [...mlpsRecMap.values()].sort((a, b) => {
|
|
7905
8486
|
const sevDiff = SEVERITY_ORDER2.indexOf(a.severity) - SEVERITY_ORDER2.indexOf(b.severity);
|
|
7906
8487
|
if (sevDiff !== 0) return sevDiff;
|
|
@@ -7910,26 +8491,27 @@ ${itemsHtml}
|
|
|
7910
8491
|
const renderMlpsRec = (r) => {
|
|
7911
8492
|
const sev = r.severity.toLowerCase();
|
|
7912
8493
|
const countLabel = r.count > 1 ? ` (× ${r.count})` : "";
|
|
7913
|
-
|
|
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>`;
|
|
7914
8496
|
};
|
|
7915
8497
|
const MLPS_TOP_N = 10;
|
|
7916
8498
|
const mlpsTopItems = mlpsUniqueRecs.slice(0, MLPS_TOP_N).map(renderMlpsRec).join("\n");
|
|
7917
8499
|
const mlpsRemaining = mlpsUniqueRecs.slice(MLPS_TOP_N);
|
|
7918
8500
|
const mlpsMoreHtml = mlpsRemaining.length > 0 ? `
|
|
7919
|
-
<details><summary
|
|
8501
|
+
<details><summary>${esc(t.showRemaining(mlpsRemaining.length))}…</summary>
|
|
7920
8502
|
${mlpsRemaining.map(renderMlpsRec).join("\n")}
|
|
7921
8503
|
</details>` : "";
|
|
7922
8504
|
remediationHtml = `
|
|
7923
8505
|
<details class="rec-fold" open>
|
|
7924
|
-
<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>
|
|
7925
8507
|
<div class="rec-body">
|
|
7926
8508
|
<ol>${mlpsTopItems}${mlpsMoreHtml}</ol>
|
|
7927
8509
|
</div>
|
|
7928
8510
|
</details>`;
|
|
7929
8511
|
}
|
|
7930
8512
|
}
|
|
7931
|
-
const naNote = naCount > 0 ? `<p style="color:#64748b;font-size:13px;margin-top:24px"
|
|
7932
|
-
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>` : "";
|
|
7933
8515
|
const mlpsCss = `
|
|
7934
8516
|
.mlps-cloud-section>summary{color:#94a3b8}
|
|
7935
8517
|
.mlps-cloud-note{color:#94a3b8;font-size:13px;margin-bottom:12px;font-style:italic}
|
|
@@ -7951,40 +8533,40 @@ ${mlpsRemaining.map(renderMlpsRec).join("\n")}
|
|
|
7951
8533
|
.mlps-summary-card .stat-label{font-size:12px;color:#94a3b8;margin-top:2px}
|
|
7952
8534
|
`;
|
|
7953
8535
|
return `<!DOCTYPE html>
|
|
7954
|
-
<html lang="
|
|
8536
|
+
<html lang="${htmlLang}">
|
|
7955
8537
|
<head>
|
|
7956
8538
|
<meta charset="UTF-8">
|
|
7957
8539
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
7958
|
-
<title
|
|
8540
|
+
<title>${esc(t.mlpsTitle)} — ${esc(date)}</title>
|
|
7959
8541
|
<style>${sharedCss()}${mlpsCss}</style>
|
|
7960
8542
|
</head>
|
|
7961
8543
|
<body>
|
|
7962
8544
|
<div class="container">
|
|
7963
8545
|
|
|
7964
8546
|
<header>
|
|
7965
|
-
<h1>🛡️
|
|
7966
|
-
<div class="disclaimer"
|
|
7967
|
-
<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>
|
|
7968
8550
|
</header>
|
|
7969
8551
|
|
|
7970
8552
|
<section class="summary" style="display:block;text-align:center">
|
|
7971
8553
|
<div style="font-size:36px;font-weight:700;margin-bottom:12px">
|
|
7972
|
-
<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>
|
|
7973
8555
|
<span style="color:#475569;margin:0 16px">/</span>
|
|
7974
|
-
<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>
|
|
7975
8557
|
</div>
|
|
7976
8558
|
<div class="mlps-summary-cards" style="justify-content:center">
|
|
7977
|
-
<div class="mlps-summary-card"><div class="stat-count" style="color:#60a5fa">${checkedTotal}</div><div class="stat-label"
|
|
7978
|
-
<div class="mlps-summary-card"><div class="stat-count" style="color:#94a3b8">${cloudCount}</div><div class="stat-label">\u{1F3E2}
|
|
7979
|
-
<div class="mlps-summary-card"><div class="stat-count" style="color:#eab308">${manualCount}</div><div class="stat-label">\u{1F4CB}
|
|
7980
|
-
${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>` : ""}
|
|
7981
8563
|
</div>
|
|
7982
8564
|
</section>
|
|
7983
8565
|
${unknownNote}
|
|
7984
8566
|
|
|
7985
8567
|
${trendHtml}
|
|
7986
8568
|
|
|
7987
|
-
${buildServiceReminderHtml(scanResults.modules)}
|
|
8569
|
+
${buildServiceReminderHtml(scanResults.modules, lang)}
|
|
7988
8570
|
|
|
7989
8571
|
${categorySections}
|
|
7990
8572
|
|
|
@@ -7993,8 +8575,8 @@ ${remediationHtml}
|
|
|
7993
8575
|
${naNote}
|
|
7994
8576
|
|
|
7995
8577
|
<footer>
|
|
7996
|
-
<p
|
|
7997
|
-
<p
|
|
8578
|
+
<p>${esc(t.mlpsFooterGenerated(VERSION))}</p>
|
|
8579
|
+
<p>${esc(t.mlpsFooterDisclaimer)}</p>
|
|
7998
8580
|
</footer>
|
|
7999
8581
|
|
|
8000
8582
|
</div>
|
|
@@ -8209,16 +8791,14 @@ Aggregates active findings from AWS Security Hub. Replaces individual config sca
|
|
|
8209
8791
|
- INFORMATIONAL findings are skipped.
|
|
8210
8792
|
|
|
8211
8793
|
## 3. GuardDuty Findings (guardduty_findings)
|
|
8212
|
-
|
|
8213
|
-
-
|
|
8214
|
-
-
|
|
8215
|
-
- 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.
|
|
8216
8797
|
|
|
8217
8798
|
## 4. Inspector Findings (inspector_findings)
|
|
8218
|
-
|
|
8219
|
-
-
|
|
8220
|
-
-
|
|
8221
|
-
- 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.
|
|
8222
8802
|
|
|
8223
8803
|
## 5. Trusted Advisor Findings (trusted_advisor_findings)
|
|
8224
8804
|
Aggregates security checks from AWS Trusted Advisor.
|
|
@@ -8254,19 +8834,15 @@ Finds unused/idle AWS resources (unattached EBS volumes, unused EIPs, stopped in
|
|
|
8254
8834
|
Assesses disaster recovery readiness \u2014 RDS Multi-AZ & backups, EBS snapshot coverage, S3 versioning & cross-region replication.
|
|
8255
8835
|
|
|
8256
8836
|
## 15. Config Rules Findings (config_rules_findings)
|
|
8257
|
-
|
|
8258
|
-
-
|
|
8259
|
-
-
|
|
8260
|
-
- Security-related rules (encryption, IAM, public access, etc.) mapped to HIGH severity (7.5).
|
|
8261
|
-
- 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.
|
|
8262
8840
|
- Gracefully handles regions where AWS Config is not enabled.
|
|
8263
8841
|
|
|
8264
8842
|
## 16. IAM Access Analyzer Findings (access_analyzer_findings)
|
|
8265
|
-
|
|
8266
|
-
-
|
|
8267
|
-
-
|
|
8268
|
-
- Covers S3 buckets, IAM roles, SQS queues, Lambda functions, KMS keys, and more.
|
|
8269
|
-
- 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.
|
|
8270
8846
|
- Returns warning if no analyzer is configured.
|
|
8271
8847
|
|
|
8272
8848
|
## 17. SSM Patch Compliance (patch_compliance_findings)
|
|
@@ -8351,112 +8927,18 @@ var MODULE_DESCRIPTIONS = {
|
|
|
8351
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.",
|
|
8352
8928
|
disaster_recovery: "Assesses disaster recovery readiness \u2014 RDS Multi-AZ & backups, EBS snapshot coverage, S3 versioning & cross-region replication.",
|
|
8353
8929
|
security_hub_findings: "Aggregates active findings from AWS Security Hub \u2014 replaces individual config scanners with centralized compliance checks.",
|
|
8354
|
-
guardduty_findings: "
|
|
8355
|
-
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.",
|
|
8356
8932
|
trusted_advisor_findings: "Aggregates security checks from AWS Trusted Advisor \u2014 requires Business or Enterprise Support plan.",
|
|
8357
|
-
config_rules_findings: "
|
|
8358
|
-
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.",
|
|
8359
8935
|
patch_compliance_findings: "Checks SSM Patch Manager compliance \u2014 managed instances with missing or failed security and system patches.",
|
|
8360
8936
|
imdsv2_enforcement: "Checks if EC2 instances enforce IMDSv2 (HttpTokens: required) \u2014 IMDSv1 allows credential theft via SSRF.",
|
|
8361
8937
|
waf_coverage: "Checks if internet-facing ALBs have WAF Web ACL associated for protection against common web exploits."
|
|
8362
8938
|
};
|
|
8363
|
-
|
|
8364
|
-
|
|
8365
|
-
|
|
8366
|
-
\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
|
|
8367
|
-
|
|
8368
|
-
\u4EE5\u4E0B\u4E8B\u9879\u9700\u8981\u4EBA\u5DE5\u786E\u8BA4\u548C\u6267\u884C\uFF1A
|
|
8369
|
-
|
|
8370
|
-
\u26A0\uFE0F \u5E94\u6025\u9694\u79BB/\u6B62\u8840\u65B9\u6848
|
|
8371
|
-
\u25A1 \u51C6\u5907\u4E13\u7528\u9694\u79BB\u5B89\u5168\u7EC4\uFF08\u65E0 Inbound/Outbound \u89C4\u5219\uFF09
|
|
8372
|
-
\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
|
|
8373
|
-
\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
|
|
8374
|
-
\u25A1 \u660E\u786E\u5404\u9879\u76EE\u8D26\u6237\u53CA\u8D44\u6E90\u7684\u8D1F\u8D23\u4EBA\u4E0E\u8054\u7CFB\u65B9\u5F0F
|
|
8375
|
-
|
|
8376
|
-
\u26A0\uFE0F \u6D4B\u8BD5/\u5F00\u53D1\u73AF\u5883\u5904\u7F6E
|
|
8377
|
-
\u25A1 \u975E\u6838\u5FC3\u7CFB\u7EDF\u5728\u62A4\u7F51\u671F\u95F4\u5173\u95ED
|
|
8378
|
-
\u25A1 \u6D4B\u8BD5/\u5F00\u53D1\u73AF\u5883\u5173\u95ED\u6216\u4E0E\u751F\u4EA7\u4FDD\u6301\u540C\u7B49\u5B89\u5168\u57FA\u7EBF
|
|
8379
|
-
\u25A1 \u786E\u8BA4\u54EA\u4E9B\u73AF\u5883\u53EF\u4EE5\u7D27\u6025\u5173\u505C\uFF0C\u907F\u514D\u653B\u51FB\u6269\u6563
|
|
8380
|
-
|
|
8381
|
-
\u26A0\uFE0F \u503C\u5B88\u56E2\u961F\u7EC4\u5EFA
|
|
8382
|
-
\u25A1 7\xD724 \u76D1\u63A7\u5FEB\u901F\u54CD\u5E94\u56E2\u961F
|
|
8383
|
-
\u25A1 \u6280\u672F\u4E0E\u98CE\u9669\u5206\u6790\u7EC4
|
|
8384
|
-
\u25A1 \u5B89\u5168\u7B56\u7565\u4E0B\u53D1\u7EC4
|
|
8385
|
-
\u25A1 \u4E1A\u52A1\u54CD\u5E94\u7EC4
|
|
8386
|
-
\u25A1 \u660E\u786E AWS TAM/Support \u8054\u7CFB\u65B9\u5F0F\uFF08ES/EOP \u5BA2\u6237\uFF09
|
|
8387
|
-
|
|
8388
|
-
\u26A0\uFE0F \u51FA\u5165\u7AD9\u8DEF\u5F84\u67B6\u6784\u56FE
|
|
8389
|
-
\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
|
|
8390
|
-
\u25A1 \u660E\u786E\u5404 ELB/Public EC2/S3/DX \u7684\u6570\u636E\u6D41\u5411
|
|
8391
|
-
\u25A1 \u8BC6\u522B\u6240\u6709\u9762\u5411\u4E92\u8054\u7F51\u7684\u6570\u636E\u4EA4\u4E92\u63A5\u53E3
|
|
8392
|
-
|
|
8393
|
-
\u26A0\uFE0F \u4E3B\u52A8\u5F0F\u6E17\u900F\u6D4B\u8BD5
|
|
8394
|
-
\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
|
|
8395
|
-
\u25A1 \u57FA\u4E8E\u6E17\u900F\u6D4B\u8BD5\u62A5\u544A\u8FDB\u884C\u6B63\u5F0F\u62A4\u7F51\u524D\u7684\u5B89\u5168\u52A0\u56FA
|
|
8396
|
-
\u25A1 \u5173\u6CE8 AWS \u5B89\u5168\u516C\u544A\uFF08\u5DF2\u77E5\u6F0F\u6D1E\u4E0E\u8865\u4E01\uFF09
|
|
8397
|
-
|
|
8398
|
-
\u26A0\uFE0F WAR-ROOM \u5B9E\u65F6\u6C9F\u901A
|
|
8399
|
-
\u25A1 \u521B\u5EFA\u62A4\u7F51\u671F\u95F4\u4E13\u7528\u6C9F\u901A\u6E20\u9053\uFF08\u4F01\u5FAE/\u9489\u9489/\u98DE\u4E66/Chime\uFF09
|
|
8400
|
-
\u25A1 \u4E0E AWS TAM \u5EFA\u7ACB WAR-ROOM \u8054\u7CFB\uFF08\u4F01\u4E1A\u7EA7\u652F\u6301\u5BA2\u6237\uFF09
|
|
8401
|
-
\u25A1 \u7EDF\u4E00\u6848\u4F8B\u6807\u9898\u683C\u5F0F\uFF1A"\u3010\u62A4\u7F51\u3011+ \u95EE\u9898\u63CF\u8FF0"
|
|
8402
|
-
|
|
8403
|
-
\u26A0\uFE0F \u5BC6\u7801\u4E0E\u51ED\u8BC1\u7BA1\u7406
|
|
8404
|
-
\u25A1 \u6240\u6709 IAM \u7528\u6237\u7ED1\u5B9A MFA
|
|
8405
|
-
\u25A1 AKSK \u8F6E\u8F6C\u5468\u671F \u2264 90 \u5929
|
|
8406
|
-
\u25A1 \u907F\u514D\u5171\u4EAB\u8D26\u6237\u4F7F\u7528
|
|
8407
|
-
\u25A1 S3/Lambda/\u5E94\u7528\u4EE3\u7801\u4E2D\u65E0\u660E\u6587\u5BC6\u7801
|
|
8408
|
-
|
|
8409
|
-
\u26A0\uFE0F \u62A4\u7F51\u540E\u4F18\u5316
|
|
8410
|
-
\u25A1 \u9488\u5BF9\u653B\u51FB\u62A5\u544A\u9010\u9879\u5E94\u7B54\u4E0E\u4FEE\u590D
|
|
8411
|
-
\u25A1 \u4E0E\u5B89\u5168\u56E2\u961F\u5EFA\u7ACB\u5468\u671F\u6027\u5B89\u5168\u7EF4\u62A4\u6D41\u7A0B
|
|
8412
|
-
\u25A1 \u6301\u7EED\u8865\u5168\u5B89\u5168\u98CE\u9669
|
|
8413
|
-
|
|
8414
|
-
\u53C2\u8003\uFF1AAWS \u62A4\u7F51\u884C\u52A8 Standard Operation Procedure (Compliance IEM)
|
|
8415
|
-
`;
|
|
8416
|
-
var SERVICE_RECOMMENDATIONS2 = {
|
|
8417
|
-
security_hub_findings: {
|
|
8418
|
-
icon: "\u{1F534}",
|
|
8419
|
-
service: "Security Hub",
|
|
8420
|
-
impact: "\u65E0\u6CD5\u83B7\u53D6 300+ \u9879\u81EA\u52A8\u5316\u5B89\u5168\u68C0\u67E5\uFF08FSBP/CIS/PCI DSS \u6807\u51C6\uFF09",
|
|
8421
|
-
action: "\u542F\u7528 Security Hub \u83B7\u5F97\u6700\u5168\u9762\u7684\u5B89\u5168\u6001\u52BF\u8BC4\u4F30"
|
|
8422
|
-
},
|
|
8423
|
-
guardduty_findings: {
|
|
8424
|
-
icon: "\u{1F534}",
|
|
8425
|
-
service: "GuardDuty",
|
|
8426
|
-
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",
|
|
8427
|
-
action: "\u542F\u7528 GuardDuty \u83B7\u5F97\u6301\u7EED\u5A01\u80C1\u68C0\u6D4B\u80FD\u529B"
|
|
8428
|
-
},
|
|
8429
|
-
inspector_findings: {
|
|
8430
|
-
icon: "\u{1F7E1}",
|
|
8431
|
-
service: "Inspector",
|
|
8432
|
-
impact: "\u65E0\u6CD5\u626B\u63CF EC2/Lambda/\u5BB9\u5668\u7684\u8F6F\u4EF6\u6F0F\u6D1E\uFF08CVE\uFF09",
|
|
8433
|
-
action: "\u542F\u7528 Inspector \u53D1\u73B0\u5DF2\u77E5\u5B89\u5168\u6F0F\u6D1E"
|
|
8434
|
-
},
|
|
8435
|
-
trusted_advisor_findings: {
|
|
8436
|
-
icon: "\u{1F7E1}",
|
|
8437
|
-
service: "Trusted Advisor",
|
|
8438
|
-
impact: "\u65E0\u6CD5\u83B7\u53D6 AWS \u6700\u4F73\u5B9E\u8DF5\u5B89\u5168\u68C0\u67E5",
|
|
8439
|
-
action: "\u5347\u7EA7\u81F3 Business/Enterprise Support \u8BA1\u5212\u4EE5\u4F7F\u7528 Trusted Advisor \u5B89\u5168\u68C0\u67E5"
|
|
8440
|
-
},
|
|
8441
|
-
config_rules_findings: {
|
|
8442
|
-
icon: "\u{1F7E1}",
|
|
8443
|
-
service: "AWS Config",
|
|
8444
|
-
impact: "\u65E0\u6CD5\u68C0\u67E5\u8D44\u6E90\u914D\u7F6E\u5408\u89C4\u72B6\u6001",
|
|
8445
|
-
action: "\u542F\u7528 AWS Config \u5E76\u914D\u7F6E Config Rules"
|
|
8446
|
-
},
|
|
8447
|
-
access_analyzer_findings: {
|
|
8448
|
-
icon: "\u{1F7E1}",
|
|
8449
|
-
service: "IAM Access Analyzer",
|
|
8450
|
-
impact: "\u65E0\u6CD5\u68C0\u6D4B\u8D44\u6E90\u662F\u5426\u88AB\u5916\u90E8\u8D26\u53F7\u6216\u516C\u7F51\u8BBF\u95EE",
|
|
8451
|
-
action: "\u521B\u5EFA IAM Access Analyzer\uFF08\u8D26\u6237\u7EA7\u6216\u7EC4\u7EC7\u7EA7\uFF09"
|
|
8452
|
-
},
|
|
8453
|
-
patch_compliance_findings: {
|
|
8454
|
-
icon: "\u{1F7E1}",
|
|
8455
|
-
service: "SSM Patch Manager",
|
|
8456
|
-
impact: "\u65E0\u6CD5\u68C0\u67E5\u5B9E\u4F8B\u8865\u4E01\u5408\u89C4\u72B6\u6001",
|
|
8457
|
-
action: "\u5B89\u88C5 SSM Agent \u5E76\u914D\u7F6E Patch Manager"
|
|
8458
|
-
}
|
|
8459
|
-
};
|
|
8939
|
+
function getHwDefenseChecklist(lang) {
|
|
8940
|
+
return getI18n(lang ?? "zh").hwChecklist;
|
|
8941
|
+
}
|
|
8460
8942
|
var SERVICE_NOT_ENABLED_PATTERNS2 = [
|
|
8461
8943
|
"not enabled",
|
|
8462
8944
|
"not found",
|
|
@@ -8466,10 +8948,11 @@ var SERVICE_NOT_ENABLED_PATTERNS2 = [
|
|
|
8466
8948
|
"not available",
|
|
8467
8949
|
"is not enabled"
|
|
8468
8950
|
];
|
|
8469
|
-
function buildServiceReminder(modules) {
|
|
8951
|
+
function buildServiceReminder(modules, lang) {
|
|
8952
|
+
const t = getI18n(lang ?? "zh");
|
|
8470
8953
|
const disabledServices = [];
|
|
8471
8954
|
for (const mod of modules) {
|
|
8472
|
-
const rec =
|
|
8955
|
+
const rec = t.serviceRecommendations[mod.module];
|
|
8473
8956
|
if (!rec) continue;
|
|
8474
8957
|
if (!mod.warnings?.length) continue;
|
|
8475
8958
|
const hasNotEnabled = mod.warnings.some(
|
|
@@ -8482,26 +8965,26 @@ function buildServiceReminder(modules) {
|
|
|
8482
8965
|
if (disabledServices.length === 0) return "";
|
|
8483
8966
|
const lines = [
|
|
8484
8967
|
"",
|
|
8485
|
-
|
|
8968
|
+
t.serviceReminderTitle,
|
|
8486
8969
|
""
|
|
8487
8970
|
];
|
|
8488
8971
|
for (const svc of disabledServices) {
|
|
8489
|
-
lines.push(`${svc.icon} ${svc.service}
|
|
8490
|
-
lines.push(`
|
|
8491
|
-
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}`);
|
|
8492
8975
|
lines.push("");
|
|
8493
8976
|
}
|
|
8494
|
-
lines.push(
|
|
8977
|
+
lines.push(t.serviceReminderFooter);
|
|
8495
8978
|
return lines.join("\n");
|
|
8496
8979
|
}
|
|
8497
|
-
function summarizeResult(result) {
|
|
8980
|
+
function summarizeResult(result, lang) {
|
|
8498
8981
|
const { summary } = result;
|
|
8499
8982
|
const lines = [
|
|
8500
8983
|
`Scan complete for account ${result.accountId} in ${result.region}.`,
|
|
8501
8984
|
`Total findings: ${summary.totalFindings} (${summary.critical} Critical, ${summary.high} High, ${summary.medium} Medium, ${summary.low} Low)`,
|
|
8502
8985
|
`Modules: ${summary.modulesSuccess} succeeded, ${summary.modulesError} errored`
|
|
8503
8986
|
];
|
|
8504
|
-
const reminder = buildServiceReminder(result.modules);
|
|
8987
|
+
const reminder = buildServiceReminder(result.modules, lang);
|
|
8505
8988
|
if (reminder) {
|
|
8506
8989
|
lines.push(reminder);
|
|
8507
8990
|
}
|
|
@@ -8563,9 +9046,10 @@ function createServer(defaultRegion) {
|
|
|
8563
9046
|
region: z.string().optional().describe("AWS region to scan (default: server region)"),
|
|
8564
9047
|
org_mode: z.boolean().optional().describe("Enable multi-account scanning via AWS Organizations"),
|
|
8565
9048
|
role_name: z.string().optional().describe("IAM role name to assume in child accounts (default: AWSSecurityMCPAudit)"),
|
|
8566
|
-
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)")
|
|
8567
9051
|
},
|
|
8568
|
-
async ({ region, org_mode, role_name, account_ids }) => {
|
|
9052
|
+
async ({ region, org_mode, role_name, account_ids, lang }) => {
|
|
8569
9053
|
try {
|
|
8570
9054
|
const r = region ?? defaultRegion;
|
|
8571
9055
|
let result;
|
|
@@ -8580,7 +9064,7 @@ function createServer(defaultRegion) {
|
|
|
8580
9064
|
}
|
|
8581
9065
|
return {
|
|
8582
9066
|
content: [
|
|
8583
|
-
{ type: "text", text: summarizeResult(result) },
|
|
9067
|
+
{ type: "text", text: summarizeResult(result, lang ?? "zh") },
|
|
8584
9068
|
{ type: "text", text: JSON.stringify(result, null, 2) }
|
|
8585
9069
|
]
|
|
8586
9070
|
};
|
|
@@ -8641,9 +9125,10 @@ function createServer(defaultRegion) {
|
|
|
8641
9125
|
region: z.string().optional().describe("AWS region to scan (default: server region)"),
|
|
8642
9126
|
org_mode: z.boolean().optional().describe("Enable multi-account scanning via AWS Organizations"),
|
|
8643
9127
|
role_name: z.string().optional().describe("IAM role name to assume in child accounts (default: AWSSecurityMCPAudit)"),
|
|
8644
|
-
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)")
|
|
8645
9130
|
},
|
|
8646
|
-
async ({ group, region, org_mode, role_name, account_ids }) => {
|
|
9131
|
+
async ({ group, region, org_mode, role_name, account_ids, lang }) => {
|
|
8647
9132
|
try {
|
|
8648
9133
|
const groupDef = SCAN_GROUPS[group];
|
|
8649
9134
|
if (!groupDef) {
|
|
@@ -8725,7 +9210,7 @@ function createServer(defaultRegion) {
|
|
|
8725
9210
|
`Scan group: ${groupDef.name} (${group})`,
|
|
8726
9211
|
groupDef.description,
|
|
8727
9212
|
"",
|
|
8728
|
-
summarizeResult(result)
|
|
9213
|
+
summarizeResult(result, lang ?? "zh")
|
|
8729
9214
|
];
|
|
8730
9215
|
if (missingModules.length > 0) {
|
|
8731
9216
|
lines.push("");
|
|
@@ -8738,7 +9223,7 @@ function createServer(defaultRegion) {
|
|
|
8738
9223
|
if (group === "hw_defense") {
|
|
8739
9224
|
const summaryContent = content[0];
|
|
8740
9225
|
if (summaryContent && summaryContent.type === "text") {
|
|
8741
|
-
summaryContent.text += "\n\n" +
|
|
9226
|
+
summaryContent.text += "\n\n" + getHwDefenseChecklist(lang ?? "zh");
|
|
8742
9227
|
}
|
|
8743
9228
|
}
|
|
8744
9229
|
return { content };
|
|
@@ -8768,11 +9253,14 @@ function createServer(defaultRegion) {
|
|
|
8768
9253
|
server.tool(
|
|
8769
9254
|
"generate_report",
|
|
8770
9255
|
"Generate a Markdown security report from scan results. Read-only. Does not modify any AWS resources.",
|
|
8771
|
-
{
|
|
8772
|
-
|
|
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 }) => {
|
|
8773
9261
|
try {
|
|
8774
9262
|
const parsed = JSON.parse(scan_results);
|
|
8775
|
-
const report = generateMarkdownReport(parsed);
|
|
9263
|
+
const report = generateMarkdownReport(parsed, lang ?? "zh");
|
|
8776
9264
|
return { content: [{ type: "text", text: report }] };
|
|
8777
9265
|
} catch (err) {
|
|
8778
9266
|
return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
@@ -8782,11 +9270,14 @@ function createServer(defaultRegion) {
|
|
|
8782
9270
|
server.tool(
|
|
8783
9271
|
"generate_mlps3_report",
|
|
8784
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.",
|
|
8785
|
-
{
|
|
8786
|
-
|
|
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 }) => {
|
|
8787
9278
|
try {
|
|
8788
9279
|
const parsed = JSON.parse(scan_results);
|
|
8789
|
-
const report = generateMlps3Report(parsed);
|
|
9280
|
+
const report = generateMlps3Report(parsed, lang ?? "zh");
|
|
8790
9281
|
return { content: [{ type: "text", text: report }] };
|
|
8791
9282
|
} catch (err) {
|
|
8792
9283
|
return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
@@ -8798,13 +9289,14 @@ function createServer(defaultRegion) {
|
|
|
8798
9289
|
"Generate a professional HTML security report. Save the output as an .html file.",
|
|
8799
9290
|
{
|
|
8800
9291
|
scan_results: z.string().describe("JSON string of FullScanResult from scan_all"),
|
|
8801
|
-
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)")
|
|
8802
9294
|
},
|
|
8803
|
-
async ({ scan_results, history }) => {
|
|
9295
|
+
async ({ scan_results, history, lang }) => {
|
|
8804
9296
|
try {
|
|
8805
9297
|
const parsed = JSON.parse(scan_results);
|
|
8806
9298
|
const historyData = history ? JSON.parse(history) : void 0;
|
|
8807
|
-
const report = generateHtmlReport(parsed, historyData);
|
|
9299
|
+
const report = generateHtmlReport(parsed, historyData, lang ?? "zh");
|
|
8808
9300
|
return { content: [{ type: "text", text: report }] };
|
|
8809
9301
|
} catch (err) {
|
|
8810
9302
|
return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
@@ -8816,13 +9308,14 @@ function createServer(defaultRegion) {
|
|
|
8816
9308
|
"Generate a professional HTML MLPS Level 3 compliance report (\u7B49\u4FDD\u4E09\u7EA7). Save as .html file.",
|
|
8817
9309
|
{
|
|
8818
9310
|
scan_results: z.string().describe("JSON string of FullScanResult from scan_group mlps3_precheck or scan_all"),
|
|
8819
|
-
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)")
|
|
8820
9313
|
},
|
|
8821
|
-
async ({ scan_results, history }) => {
|
|
9314
|
+
async ({ scan_results, history, lang }) => {
|
|
8822
9315
|
try {
|
|
8823
9316
|
const parsed = JSON.parse(scan_results);
|
|
8824
9317
|
const historyData = history ? JSON.parse(history) : void 0;
|
|
8825
|
-
const report = generateMlps3HtmlReport(parsed, historyData);
|
|
9318
|
+
const report = generateMlps3HtmlReport(parsed, historyData, lang ?? "zh");
|
|
8826
9319
|
return { content: [{ type: "text", text: report }] };
|
|
8827
9320
|
} catch (err) {
|
|
8828
9321
|
return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
@@ -8832,7 +9325,10 @@ function createServer(defaultRegion) {
|
|
|
8832
9325
|
server.tool(
|
|
8833
9326
|
"generate_maturity_report",
|
|
8834
9327
|
"Generate a security maturity assessment report from scan_all results. Requires service_detection module output. Read-only.",
|
|
8835
|
-
{
|
|
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
|
+
},
|
|
8836
9332
|
async ({ scan_results }) => {
|
|
8837
9333
|
try {
|
|
8838
9334
|
const parsed = JSON.parse(scan_results);
|
|
@@ -9103,7 +9599,7 @@ ${finding}`
|
|
|
9103
9599
|
type: "text",
|
|
9104
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
|
|
9105
9601
|
|
|
9106
|
-
${
|
|
9602
|
+
${getHwDefenseChecklist("zh")}
|
|
9107
9603
|
|
|
9108
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`
|
|
9109
9605
|
}
|