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
package/dist/src/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
|
|
6
6
|
// src/version.ts
|
|
7
|
-
var VERSION = "0.
|
|
7
|
+
var VERSION = "0.6.0";
|
|
8
8
|
|
|
9
9
|
// src/utils/aws-client.ts
|
|
10
10
|
import { STSClient, GetCallerIdentityCommand } from "@aws-sdk/client-sts";
|
|
@@ -88,7 +88,9 @@ async function listOrgAccounts(region) {
|
|
|
88
88
|
var AGGREGATION_MODULES = /* @__PURE__ */ new Set([
|
|
89
89
|
"security_hub_findings",
|
|
90
90
|
"guardduty_findings",
|
|
91
|
-
"inspector_findings"
|
|
91
|
+
"inspector_findings",
|
|
92
|
+
"config_rules_findings",
|
|
93
|
+
"access_analyzer_findings"
|
|
92
94
|
]);
|
|
93
95
|
function buildSummary(modules) {
|
|
94
96
|
let critical = 0;
|
|
@@ -480,9 +482,15 @@ var ServiceDetectionScanner = class {
|
|
|
480
482
|
const insp = createClient(Inspector2Client, region, ctx.credentials);
|
|
481
483
|
const resp = await insp.send(new BatchGetAccountStatusCommand({ accountIds: [accountId] }));
|
|
482
484
|
const accounts = resp.accounts ?? [];
|
|
483
|
-
const active = accounts.some(
|
|
484
|
-
|
|
485
|
-
|
|
485
|
+
const active = accounts.some((a) => {
|
|
486
|
+
const s = a.state?.status;
|
|
487
|
+
if (s === "ENABLED" || s === "ENABLING") return true;
|
|
488
|
+
const rs = a.resourceState;
|
|
489
|
+
if (!rs) return false;
|
|
490
|
+
return ["ec2", "ecr", "lambda", "lambdaCode", "codeRepository"].some(
|
|
491
|
+
(k) => rs[k]?.status === "ENABLED"
|
|
492
|
+
);
|
|
493
|
+
});
|
|
486
494
|
if (active) {
|
|
487
495
|
services.push({
|
|
488
496
|
name: "Inspector",
|
|
@@ -2735,14 +2743,21 @@ var SecurityHubFindingsScanner = class {
|
|
|
2735
2743
|
const resourceType = f.Resources?.[0]?.Type ?? "AWS::Unknown";
|
|
2736
2744
|
const resourceArn = resourceId.startsWith("arn:") ? resourceId : `arn:${partition}:securityhub:${region}:${accountId}:finding/${f.Id ?? "unknown"}`;
|
|
2737
2745
|
const remediationSteps = [];
|
|
2738
|
-
|
|
2739
|
-
|
|
2746
|
+
const title = f.Title ?? "Security Hub Finding";
|
|
2747
|
+
if (/^KB\d+$/.test(title)) {
|
|
2748
|
+
remediationSteps.push(`Install Windows patch ${title} via WSUS or SSM Patch Manager`);
|
|
2749
|
+
remediationSteps.push(`Microsoft KB article: https://support.microsoft.com/help/${title}`);
|
|
2750
|
+
} else if (/^CVE-/.test(title)) {
|
|
2751
|
+
remediationSteps.push(`Fix vulnerability ${title}: update affected software to patched version`);
|
|
2752
|
+
} else {
|
|
2753
|
+
remediationSteps.push(title);
|
|
2740
2754
|
}
|
|
2741
2755
|
if (f.Remediation?.Recommendation?.Url) {
|
|
2742
|
-
remediationSteps.push(`
|
|
2756
|
+
remediationSteps.push(`Documentation: ${f.Remediation.Recommendation.Url}`);
|
|
2743
2757
|
}
|
|
2744
|
-
|
|
2745
|
-
|
|
2758
|
+
const recText = f.Remediation?.Recommendation?.Text ?? "";
|
|
2759
|
+
if (recText && !["See References", "None Provided", ""].includes(recText.trim())) {
|
|
2760
|
+
remediationSteps.push(recText);
|
|
2746
2761
|
}
|
|
2747
2762
|
findings.push({
|
|
2748
2763
|
severity,
|
|
@@ -2895,10 +2910,11 @@ var GuardDutyFindingsScanner = class {
|
|
|
2895
2910
|
impact: `GuardDuty threat type: ${gdf.Type ?? "unknown"} (severity ${gdSeverity})`,
|
|
2896
2911
|
riskScore: score,
|
|
2897
2912
|
remediationSteps: [
|
|
2898
|
-
|
|
2899
|
-
`
|
|
2900
|
-
"
|
|
2901
|
-
|
|
2913
|
+
`Investigate ${gdf.Type ?? "unknown threat"}: ${gdf.Title ?? "threat detected"}`,
|
|
2914
|
+
gdf.Description ? `Details: ${gdf.Description.substring(0, 200)}` : "",
|
|
2915
|
+
"Isolate affected resources if compromise is confirmed.",
|
|
2916
|
+
"Review CloudTrail logs for related suspicious activity."
|
|
2917
|
+
].filter(Boolean),
|
|
2902
2918
|
priority: priorityFromSeverity(severity),
|
|
2903
2919
|
module: this.moduleName,
|
|
2904
2920
|
accountId: gdf.AccountId ?? accountId
|
|
@@ -2989,18 +3005,44 @@ var InspectorFindingsScanner = class {
|
|
|
2989
3005
|
const resourceType = f.resources?.[0]?.type ?? "AWS::Unknown";
|
|
2990
3006
|
const resourceArn = resourceId.startsWith("arn:") ? resourceId : `arn:${partition}:inspector2:${region}:${accountId}:finding/${f.findingArn ?? "unknown"}`;
|
|
2991
3007
|
const remediationSteps = [];
|
|
2992
|
-
|
|
3008
|
+
const vulnPkgs = f.packageVulnerabilityDetails?.vulnerablePackages;
|
|
3009
|
+
if (vulnPkgs?.length) {
|
|
3010
|
+
for (const pkg of vulnPkgs.slice(0, 3)) {
|
|
3011
|
+
const name = pkg.name ?? "unknown-package";
|
|
3012
|
+
const installed = pkg.version ?? "unknown";
|
|
3013
|
+
const fixed = pkg.fixedInVersion ?? "latest";
|
|
3014
|
+
const cveRef = cveId ? ` to fix ${cveId}` : "";
|
|
3015
|
+
remediationSteps.push(`Update ${name} from ${installed} to ${fixed}${cveRef}`);
|
|
3016
|
+
}
|
|
3017
|
+
} else if (f.remediation?.recommendation?.text) {
|
|
2993
3018
|
remediationSteps.push(f.remediation.recommendation.text);
|
|
2994
3019
|
}
|
|
3020
|
+
const genericPatterns = ["See References", "None Provided", "Review the finding"];
|
|
3021
|
+
if (remediationSteps.length === 0 || genericPatterns.some((p) => remediationSteps[0]?.startsWith(p))) {
|
|
3022
|
+
remediationSteps.length = 0;
|
|
3023
|
+
const rawTitle = f.title ?? "";
|
|
3024
|
+
if (rawTitle.includes("KB")) {
|
|
3025
|
+
const kbMatch = rawTitle.match(/KB\d+/);
|
|
3026
|
+
const kb = kbMatch ? kbMatch[0] : "patch";
|
|
3027
|
+
remediationSteps.push(`Install Windows patch ${kb} via WSUS or AWS Systems Manager Patch Manager`);
|
|
3028
|
+
remediationSteps.push(`Run: aws ssm send-command --document-name "AWS-InstallWindowsUpdates" --targets "Key=InstanceIds,Values=${resourceId}"`);
|
|
3029
|
+
if (kbMatch) {
|
|
3030
|
+
remediationSteps.push(`Microsoft KB article: https://support.microsoft.com/help/${kb}`);
|
|
3031
|
+
}
|
|
3032
|
+
} else if (rawTitle.includes("CVE-") || cveId) {
|
|
3033
|
+
const cveMatch = rawTitle.match(/CVE-[\d-]+/);
|
|
3034
|
+
const cve = cveMatch ? cveMatch[0] : cveId ?? "vulnerability";
|
|
3035
|
+
remediationSteps.push(`Fix ${cve}: update the affected software package to the latest patched version`);
|
|
3036
|
+
} else {
|
|
3037
|
+
remediationSteps.push(`Review and remediate: ${rawTitle}`);
|
|
3038
|
+
}
|
|
3039
|
+
}
|
|
2995
3040
|
if (f.remediation?.recommendation?.Url) {
|
|
2996
|
-
remediationSteps.push(`
|
|
3041
|
+
remediationSteps.push(`Documentation: ${f.remediation.recommendation.Url}`);
|
|
2997
3042
|
}
|
|
2998
3043
|
if (f.packageVulnerabilityDetails?.referenceUrls?.length) {
|
|
2999
3044
|
remediationSteps.push(`CVE references: ${f.packageVulnerabilityDetails.referenceUrls.slice(0, 3).join(", ")}`);
|
|
3000
3045
|
}
|
|
3001
|
-
if (remediationSteps.length === 0) {
|
|
3002
|
-
remediationSteps.push("Review the finding in the Amazon Inspector console and apply the recommended patch or update.");
|
|
3003
|
-
}
|
|
3004
3046
|
const description = f.description ?? titleBase;
|
|
3005
3047
|
const impact = cveId ? `Vulnerability ${cveId} \u2014 CVSS: ${f.packageVulnerabilityDetails?.cvss?.[0]?.baseScore ?? "N/A"}` : `Inspector finding type: ${f.type ?? "unknown"}`;
|
|
3006
3048
|
findings.push({
|
|
@@ -3335,10 +3377,10 @@ var ConfigRulesFindingsScanner = class {
|
|
|
3335
3377
|
impact: `Resource is non-compliant with Config Rule: ${ruleName}`,
|
|
3336
3378
|
riskScore,
|
|
3337
3379
|
remediationSteps: [
|
|
3338
|
-
`
|
|
3339
|
-
`
|
|
3340
|
-
|
|
3341
|
-
],
|
|
3380
|
+
`Fix Config Rule violation: ${ruleName}`,
|
|
3381
|
+
annotation ? `Details: ${annotation}` : "",
|
|
3382
|
+
`Resource: ${resourceType}/${resourceId}`
|
|
3383
|
+
].filter(Boolean),
|
|
3342
3384
|
priority: priorityFromSeverity(severity),
|
|
3343
3385
|
module: this.moduleName,
|
|
3344
3386
|
accountId
|
|
@@ -3487,13 +3529,13 @@ var AccessAnalyzerFindingsScanner = class {
|
|
|
3487
3529
|
const title = buildFindingTitle(aaf);
|
|
3488
3530
|
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"}`;
|
|
3489
3531
|
const remediationSteps = external ? [
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3532
|
+
`Restrict external access on ${resourceType} ${resourceId}`,
|
|
3533
|
+
"Remove or narrow the resource policy to eliminate unintended external access.",
|
|
3534
|
+
`Resource ARN: ${resourceArn}`
|
|
3493
3535
|
] : [
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3536
|
+
`Remove unused access on ${resourceType} ${resourceId}`,
|
|
3537
|
+
"Remove unused permissions, roles, or credentials to follow least-privilege.",
|
|
3538
|
+
`Resource ARN: ${resourceArn}`
|
|
3497
3539
|
];
|
|
3498
3540
|
findings.push({
|
|
3499
3541
|
severity,
|
|
@@ -3968,6 +4010,479 @@ var WafCoverageScanner = class {
|
|
|
3968
4010
|
}
|
|
3969
4011
|
};
|
|
3970
4012
|
|
|
4013
|
+
// src/i18n/zh.ts
|
|
4014
|
+
var zhI18n = {
|
|
4015
|
+
// HTML Security Report
|
|
4016
|
+
securityReportTitle: "AWS \u5B89\u5168\u626B\u63CF\u62A5\u544A",
|
|
4017
|
+
securityScore: "\u5B89\u5168\u8BC4\u5206",
|
|
4018
|
+
critical: "\u4E25\u91CD",
|
|
4019
|
+
high: "\u9AD8",
|
|
4020
|
+
medium: "\u4E2D",
|
|
4021
|
+
low: "\u4F4E",
|
|
4022
|
+
scanStatistics: "\u626B\u63CF\u7EDF\u8BA1",
|
|
4023
|
+
module: "\u6A21\u5757",
|
|
4024
|
+
resources: "\u8D44\u6E90",
|
|
4025
|
+
findings: "\u53D1\u73B0",
|
|
4026
|
+
status: "\u72B6\u6001",
|
|
4027
|
+
allFindings: "\u6240\u6709\u53D1\u73B0",
|
|
4028
|
+
recommendations: "\u5EFA\u8BAE",
|
|
4029
|
+
unique: "\u53BB\u91CD",
|
|
4030
|
+
showMore: "\u663E\u793A\u66F4\u591A",
|
|
4031
|
+
noIssuesFound: "\u672A\u53D1\u73B0\u5B89\u5168\u95EE\u9898\u3002",
|
|
4032
|
+
allModulesClean: "\u6240\u6709\u6A21\u5757\u6B63\u5E38",
|
|
4033
|
+
generatedBy: "\u7531 AWS Security MCP Server \u751F\u6210",
|
|
4034
|
+
informationalOnly: "\u672C\u62A5\u544A\u4EC5\u4F9B\u53C2\u8003\u3002",
|
|
4035
|
+
// MLPS Report
|
|
4036
|
+
mlpsTitle: "\u7B49\u4FDD\u4E09\u7EA7\u9884\u68C0\u62A5\u544A",
|
|
4037
|
+
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",
|
|
4038
|
+
checkedItems: "\u5DF2\u68C0\u67E5\u9879",
|
|
4039
|
+
noIssues: "\u672A\u53D1\u73B0\u95EE\u9898",
|
|
4040
|
+
issuesFound: "\u53D1\u73B0\u95EE\u9898",
|
|
4041
|
+
notChecked: "\u672A\u68C0\u67E5",
|
|
4042
|
+
cloudProvider: "\u4E91\u5E73\u53F0\u8D1F\u8D23",
|
|
4043
|
+
manualReview: "\u9700\u4EBA\u5DE5\u8BC4\u4F30",
|
|
4044
|
+
notApplicable: "\u4E0D\u9002\u7528",
|
|
4045
|
+
checkResult: "\u68C0\u67E5\u7ED3\u679C",
|
|
4046
|
+
noRelatedIssues: "\u68C0\u67E5\u7ED3\u679C\uFF1A\u672A\u53D1\u73B0\u76F8\u5173\u95EE\u9898",
|
|
4047
|
+
issuesFoundCount: (n) => `\u68C0\u67E5\u7ED3\u679C\uFF1A\u53D1\u73B0 ${n} \u4E2A\u76F8\u5173\u95EE\u9898`,
|
|
4048
|
+
remediation: "\u5EFA\u8BAE",
|
|
4049
|
+
remediationItems: (n) => `\u5EFA\u8BAE\u6574\u6539\u9879\uFF08${n} \u9879\u53BB\u91CD\uFF09`,
|
|
4050
|
+
showRemaining: (n) => `\u663E\u793A\u5176\u4F59 ${n} \u9879`,
|
|
4051
|
+
// HW Defense Checklist
|
|
4052
|
+
hwChecklistTitle: "\u{1F4CB} \u62A4\u7F51\u884C\u52A8\u8865\u5145\u63D0\u9192\uFF08\u8D85\u51FA\u81EA\u52A8\u5316\u626B\u63CF\u8303\u56F4\uFF09",
|
|
4053
|
+
hwChecklistSubtitle: "\u4EE5\u4E0B\u4E8B\u9879\u9700\u8981\u4EBA\u5DE5\u786E\u8BA4\u548C\u6267\u884C\uFF1A",
|
|
4054
|
+
hwEmergencyIsolation: `\u26A0\uFE0F \u5E94\u6025\u9694\u79BB/\u6B62\u8840\u65B9\u6848
|
|
4055
|
+
\u25A1 \u51C6\u5907\u4E13\u7528\u9694\u79BB\u5B89\u5168\u7EC4\uFF08\u65E0 Inbound/Outbound \u89C4\u5219\uFF09
|
|
4056
|
+
\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
|
|
4057
|
+
\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
|
|
4058
|
+
\u25A1 \u660E\u786E\u5404\u9879\u76EE\u8D26\u6237\u53CA\u8D44\u6E90\u7684\u8D1F\u8D23\u4EBA\u4E0E\u8054\u7CFB\u65B9\u5F0F`,
|
|
4059
|
+
hwTestEnvShutdown: `\u26A0\uFE0F \u6D4B\u8BD5/\u5F00\u53D1\u73AF\u5883\u5904\u7F6E
|
|
4060
|
+
\u25A1 \u975E\u6838\u5FC3\u7CFB\u7EDF\u5728\u62A4\u7F51\u671F\u95F4\u5173\u95ED
|
|
4061
|
+
\u25A1 \u6D4B\u8BD5/\u5F00\u53D1\u73AF\u5883\u5173\u95ED\u6216\u4E0E\u751F\u4EA7\u4FDD\u6301\u540C\u7B49\u5B89\u5168\u57FA\u7EBF
|
|
4062
|
+
\u25A1 \u786E\u8BA4\u54EA\u4E9B\u73AF\u5883\u53EF\u4EE5\u7D27\u6025\u5173\u505C\uFF0C\u907F\u514D\u653B\u51FB\u6269\u6563`,
|
|
4063
|
+
hwDutyTeam: `\u26A0\uFE0F \u503C\u5B88\u56E2\u961F\u7EC4\u5EFA
|
|
4064
|
+
\u25A1 7\xD724 \u76D1\u63A7\u5FEB\u901F\u54CD\u5E94\u56E2\u961F
|
|
4065
|
+
\u25A1 \u6280\u672F\u4E0E\u98CE\u9669\u5206\u6790\u7EC4
|
|
4066
|
+
\u25A1 \u5B89\u5168\u7B56\u7565\u4E0B\u53D1\u7EC4
|
|
4067
|
+
\u25A1 \u4E1A\u52A1\u54CD\u5E94\u7EC4
|
|
4068
|
+
\u25A1 \u660E\u786E AWS TAM/Support \u8054\u7CFB\u65B9\u5F0F\uFF08ES/EOP \u5BA2\u6237\uFF09`,
|
|
4069
|
+
hwNetworkDiagram: `\u26A0\uFE0F \u51FA\u5165\u7AD9\u8DEF\u5F84\u67B6\u6784\u56FE
|
|
4070
|
+
\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
|
|
4071
|
+
\u25A1 \u660E\u786E\u5404 ELB/Public EC2/S3/DX \u7684\u6570\u636E\u6D41\u5411
|
|
4072
|
+
\u25A1 \u8BC6\u522B\u6240\u6709\u9762\u5411\u4E92\u8054\u7F51\u7684\u6570\u636E\u4EA4\u4E92\u63A5\u53E3`,
|
|
4073
|
+
hwPentest: `\u26A0\uFE0F \u4E3B\u52A8\u5F0F\u6E17\u900F\u6D4B\u8BD5
|
|
4074
|
+
\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
|
|
4075
|
+
\u25A1 \u57FA\u4E8E\u6E17\u900F\u6D4B\u8BD5\u62A5\u544A\u8FDB\u884C\u6B63\u5F0F\u62A4\u7F51\u524D\u7684\u5B89\u5168\u52A0\u56FA
|
|
4076
|
+
\u25A1 \u5173\u6CE8 AWS \u5B89\u5168\u516C\u544A\uFF08\u5DF2\u77E5\u6F0F\u6D1E\u4E0E\u8865\u4E01\uFF09`,
|
|
4077
|
+
hwWarRoom: `\u26A0\uFE0F WAR-ROOM \u5B9E\u65F6\u6C9F\u901A
|
|
4078
|
+
\u25A1 \u521B\u5EFA\u62A4\u7F51\u671F\u95F4\u4E13\u7528\u6C9F\u901A\u6E20\u9053\uFF08\u4F01\u5FAE/\u9489\u9489/\u98DE\u4E66/Chime\uFF09
|
|
4079
|
+
\u25A1 \u4E0E AWS TAM \u5EFA\u7ACB WAR-ROOM \u8054\u7CFB\uFF08\u4F01\u4E1A\u7EA7\u652F\u6301\u5BA2\u6237\uFF09
|
|
4080
|
+
\u25A1 \u7EDF\u4E00\u6848\u4F8B\u6807\u9898\u683C\u5F0F\uFF1A\u201C\u3010\u62A4\u7F51\u3011+ \u95EE\u9898\u63CF\u8FF0\u201D`,
|
|
4081
|
+
hwCredentials: `\u26A0\uFE0F \u5BC6\u7801\u4E0E\u51ED\u8BC1\u7BA1\u7406
|
|
4082
|
+
\u25A1 \u6240\u6709 IAM \u7528\u6237\u7ED1\u5B9A MFA
|
|
4083
|
+
\u25A1 AKSK \u8F6E\u8F6C\u5468\u671F \u2264 90 \u5929
|
|
4084
|
+
\u25A1 \u907F\u514D\u5171\u4EAB\u8D26\u6237\u4F7F\u7528
|
|
4085
|
+
\u25A1 S3/Lambda/\u5E94\u7528\u4EE3\u7801\u4E2D\u65E0\u660E\u6587\u5BC6\u7801`,
|
|
4086
|
+
hwPostOptimization: `\u26A0\uFE0F \u62A4\u7F51\u540E\u4F18\u5316
|
|
4087
|
+
\u25A1 \u9488\u5BF9\u653B\u51FB\u62A5\u544A\u9010\u9879\u5E94\u7B54\u4E0E\u4FEE\u590D
|
|
4088
|
+
\u25A1 \u4E0E\u5B89\u5168\u56E2\u961F\u5EFA\u7ACB\u5468\u671F\u6027\u5B89\u5168\u7EF4\u62A4\u6D41\u7A0B
|
|
4089
|
+
\u25A1 \u6301\u7EED\u8865\u5168\u5B89\u5168\u98CE\u9669`,
|
|
4090
|
+
hwReference: "\u53C2\u8003\uFF1AAWS \u62A4\u7F51\u884C\u52A8 Standard Operation Procedure (Compliance IEM)",
|
|
4091
|
+
// Service Reminders
|
|
4092
|
+
serviceReminderTitle: "\u26A1 \u4EE5\u4E0B\u5B89\u5168\u670D\u52A1\u672A\u542F\u7528\uFF0C\u90E8\u5206\u68C0\u67E5\u65E0\u6CD5\u6267\u884C\uFF1A",
|
|
4093
|
+
serviceReminderFooter: "\u542F\u7528\u4EE5\u4E0A\u670D\u52A1\u540E\u91CD\u65B0\u626B\u63CF\u53EF\u83B7\u5F97\u66F4\u5B8C\u6574\u7684\u5B89\u5168\u8BC4\u4F30\u3002",
|
|
4094
|
+
serviceImpact: "\u5F71\u54CD",
|
|
4095
|
+
serviceAction: "\u5EFA\u8BAE",
|
|
4096
|
+
// Common
|
|
4097
|
+
account: "\u8D26\u6237",
|
|
4098
|
+
region: "\u533A\u57DF",
|
|
4099
|
+
scanTime: "\u626B\u63CF\u65F6\u95F4",
|
|
4100
|
+
duration: "\u8017\u65F6",
|
|
4101
|
+
severityDistribution: "\u4E25\u91CD\u6027\u5206\u5E03",
|
|
4102
|
+
findingsByModule: "\u6309\u6A21\u5757\u5206\u7C7B\u7684\u53D1\u73B0",
|
|
4103
|
+
details: "\u8BE6\u60C5",
|
|
4104
|
+
// Extended — HTML Security Report extras
|
|
4105
|
+
topHighestRiskFindings: (n) => `\u524D ${n} \u9879\u6700\u9AD8\u98CE\u9669\u53D1\u73B0`,
|
|
4106
|
+
resource: "\u8D44\u6E90",
|
|
4107
|
+
impact: "\u5F71\u54CD",
|
|
4108
|
+
riskScore: "\u98CE\u9669\u8BC4\u5206",
|
|
4109
|
+
showRemainingFindings: (n) => `\u663E\u793A\u5269\u4F59 ${n} \u9879\u53D1\u73B0\u2026`,
|
|
4110
|
+
trendTitle: "30\u65E5\u8D8B\u52BF",
|
|
4111
|
+
findingsBySeverity: "\u6309\u4E25\u91CD\u6027\u5206\u7C7B\u7684\u53D1\u73B0",
|
|
4112
|
+
showMoreCount: (n) => `\u663E\u793A\u5269\u4F59 ${n} \u9879\u2026`,
|
|
4113
|
+
// Extended — MLPS extras
|
|
4114
|
+
// Markdown report
|
|
4115
|
+
executiveSummary: "\u6267\u884C\u6458\u8981",
|
|
4116
|
+
totalFindingsLabel: "\u53D1\u73B0\u603B\u6570",
|
|
4117
|
+
description: "\u63CF\u8FF0",
|
|
4118
|
+
priority: "\u4F18\u5148\u7EA7",
|
|
4119
|
+
noFindingsForSeverity: (severity) => `\u65E0${severity}\u53D1\u73B0\u3002`,
|
|
4120
|
+
preCheckOverview: "\u9884\u68C0\u603B\u89C8",
|
|
4121
|
+
accountInfo: "\u8D26\u6237\u4FE1\u606F",
|
|
4122
|
+
checkedCount: (total, clean, issues) => `\u5DF2\u68C0\u67E5: ${total} \u9879\uFF08\u672A\u53D1\u73B0\u95EE\u9898: ${clean} \u9879 | \u53D1\u73B0\u95EE\u9898: ${issues} \u9879\uFF09`,
|
|
4123
|
+
uncheckedCount: (n) => `\u672A\u68C0\u67E5: ${n} \u9879\uFF08\u5BF9\u5E94\u626B\u63CF\u6A21\u5757\u672A\u8FD0\u884C\uFF09`,
|
|
4124
|
+
cloudProviderCount: (n) => `\u4E91\u5E73\u53F0\u8D1F\u8D23: ${n} \u9879`,
|
|
4125
|
+
manualReviewCount: (n) => `\u9700\u4EBA\u5DE5\u8BC4\u4F30: ${n} \u9879`,
|
|
4126
|
+
naCount: (n) => `\u4E0D\u9002\u7528: ${n} \u9879`,
|
|
4127
|
+
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`,
|
|
4128
|
+
unknownNote: (n) => `\uFF08${n} \u9879\u672A\u68C0\u67E5\uFF0C\u5BF9\u5E94\u626B\u63CF\u6A21\u5757\u672A\u8FD0\u884C\uFF09`,
|
|
4129
|
+
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`,
|
|
4130
|
+
mlpsFooterGenerated: (version) => `\u7531 AWS Security MCP Server v${version} \u751F\u6210`,
|
|
4131
|
+
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",
|
|
4132
|
+
andMore: (n) => `... \u53CA\u5176\u4ED6 ${n} \u9879`,
|
|
4133
|
+
remediationByPriority: "\u5EFA\u8BAE\u6574\u6539\u9879\uFF08\u6309\u4F18\u5148\u7EA7\uFF09",
|
|
4134
|
+
affectedResources: (n) => `\u6D89\u53CA ${n} \u4E2A\u8D44\u6E90`,
|
|
4135
|
+
installWindowsPatches: (n, kbs) => `\u5B89\u88C5 ${n} \u4E2A Windows \u8865\u4E01 (${kbs})`,
|
|
4136
|
+
mlpsCategorySection: {
|
|
4137
|
+
"\u5B89\u5168\u7269\u7406\u73AF\u5883": "\u4E00\u3001\u5B89\u5168\u7269\u7406\u73AF\u5883",
|
|
4138
|
+
"\u5B89\u5168\u901A\u4FE1\u7F51\u7EDC": "\u4E8C\u3001\u5B89\u5168\u901A\u4FE1\u7F51\u7EDC",
|
|
4139
|
+
"\u5B89\u5168\u533A\u57DF\u8FB9\u754C": "\u4E09\u3001\u5B89\u5168\u533A\u57DF\u8FB9\u754C",
|
|
4140
|
+
"\u5B89\u5168\u8BA1\u7B97\u73AF\u5883": "\u56DB\u3001\u5B89\u5168\u8BA1\u7B97\u73AF\u5883",
|
|
4141
|
+
"\u5B89\u5168\u7BA1\u7406\u4E2D\u5FC3": "\u4E94\u3001\u5B89\u5168\u7BA1\u7406\u4E2D\u5FC3"
|
|
4142
|
+
},
|
|
4143
|
+
// Service Recommendations
|
|
4144
|
+
notEnabled: "\u672A\u542F\u7528",
|
|
4145
|
+
serviceRecommendations: {
|
|
4146
|
+
security_hub_findings: {
|
|
4147
|
+
icon: "\u{1F534}",
|
|
4148
|
+
service: "Security Hub",
|
|
4149
|
+
impact: "\u65E0\u6CD5\u83B7\u53D6 300+ \u9879\u81EA\u52A8\u5316\u5B89\u5168\u68C0\u67E5\uFF08FSBP/CIS/PCI DSS \u6807\u51C6\uFF09",
|
|
4150
|
+
action: "\u542F\u7528 Security Hub \u83B7\u5F97\u6700\u5168\u9762\u7684\u5B89\u5168\u6001\u52BF\u8BC4\u4F30"
|
|
4151
|
+
},
|
|
4152
|
+
guardduty_findings: {
|
|
4153
|
+
icon: "\u{1F534}",
|
|
4154
|
+
service: "GuardDuty",
|
|
4155
|
+
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",
|
|
4156
|
+
action: "\u542F\u7528 GuardDuty \u83B7\u5F97\u6301\u7EED\u5A01\u80C1\u68C0\u6D4B\u80FD\u529B"
|
|
4157
|
+
},
|
|
4158
|
+
inspector_findings: {
|
|
4159
|
+
icon: "\u{1F7E1}",
|
|
4160
|
+
service: "Inspector",
|
|
4161
|
+
impact: "\u65E0\u6CD5\u626B\u63CF EC2/Lambda/\u5BB9\u5668\u7684\u8F6F\u4EF6\u6F0F\u6D1E\uFF08CVE\uFF09",
|
|
4162
|
+
action: "\u542F\u7528 Inspector \u53D1\u73B0\u5DF2\u77E5\u5B89\u5168\u6F0F\u6D1E"
|
|
4163
|
+
},
|
|
4164
|
+
trusted_advisor_findings: {
|
|
4165
|
+
icon: "\u{1F7E1}",
|
|
4166
|
+
service: "Trusted Advisor",
|
|
4167
|
+
impact: "\u65E0\u6CD5\u83B7\u53D6 AWS \u6700\u4F73\u5B9E\u8DF5\u5B89\u5168\u68C0\u67E5",
|
|
4168
|
+
action: "\u5347\u7EA7\u81F3 Business/Enterprise Support \u8BA1\u5212\u4EE5\u4F7F\u7528 Trusted Advisor \u5B89\u5168\u68C0\u67E5"
|
|
4169
|
+
},
|
|
4170
|
+
config_rules_findings: {
|
|
4171
|
+
icon: "\u{1F7E1}",
|
|
4172
|
+
service: "AWS Config",
|
|
4173
|
+
impact: "\u65E0\u6CD5\u68C0\u67E5\u8D44\u6E90\u914D\u7F6E\u5408\u89C4\u72B6\u6001",
|
|
4174
|
+
action: "\u542F\u7528 AWS Config \u5E76\u914D\u7F6E Config Rules"
|
|
4175
|
+
},
|
|
4176
|
+
access_analyzer_findings: {
|
|
4177
|
+
icon: "\u{1F7E1}",
|
|
4178
|
+
service: "IAM Access Analyzer",
|
|
4179
|
+
impact: "\u65E0\u6CD5\u68C0\u6D4B\u8D44\u6E90\u662F\u5426\u88AB\u5916\u90E8\u8D26\u53F7\u6216\u516C\u7F51\u8BBF\u95EE",
|
|
4180
|
+
action: "\u521B\u5EFA IAM Access Analyzer\uFF08\u8D26\u6237\u7EA7\u6216\u7EC4\u7EC7\u7EA7\uFF09"
|
|
4181
|
+
},
|
|
4182
|
+
patch_compliance_findings: {
|
|
4183
|
+
icon: "\u{1F7E1}",
|
|
4184
|
+
service: "SSM Patch Manager",
|
|
4185
|
+
impact: "\u65E0\u6CD5\u68C0\u67E5\u5B9E\u4F8B\u8865\u4E01\u5408\u89C4\u72B6\u6001",
|
|
4186
|
+
action: "\u5B89\u88C5 SSM Agent \u5E76\u914D\u7F6E Patch Manager"
|
|
4187
|
+
}
|
|
4188
|
+
},
|
|
4189
|
+
// HW Checklist (full composite)
|
|
4190
|
+
hwChecklist: `
|
|
4191
|
+
\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
4192
|
+
\u{1F4CB} \u62A4\u7F51\u884C\u52A8\u8865\u5145\u63D0\u9192\uFF08\u8D85\u51FA\u81EA\u52A8\u5316\u626B\u63CF\u8303\u56F4\uFF09
|
|
4193
|
+
\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
4194
|
+
|
|
4195
|
+
\u4EE5\u4E0B\u4E8B\u9879\u9700\u8981\u4EBA\u5DE5\u786E\u8BA4\u548C\u6267\u884C\uFF1A
|
|
4196
|
+
|
|
4197
|
+
\u26A0\uFE0F \u5E94\u6025\u9694\u79BB/\u6B62\u8840\u65B9\u6848
|
|
4198
|
+
\u25A1 \u51C6\u5907\u4E13\u7528\u9694\u79BB\u5B89\u5168\u7EC4\uFF08\u65E0 Inbound/Outbound \u89C4\u5219\uFF09
|
|
4199
|
+
\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
|
|
4200
|
+
\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
|
|
4201
|
+
\u25A1 \u660E\u786E\u5404\u9879\u76EE\u8D26\u6237\u53CA\u8D44\u6E90\u7684\u8D1F\u8D23\u4EBA\u4E0E\u8054\u7CFB\u65B9\u5F0F
|
|
4202
|
+
|
|
4203
|
+
\u26A0\uFE0F \u6D4B\u8BD5/\u5F00\u53D1\u73AF\u5883\u5904\u7F6E
|
|
4204
|
+
\u25A1 \u975E\u6838\u5FC3\u7CFB\u7EDF\u5728\u62A4\u7F51\u671F\u95F4\u5173\u95ED
|
|
4205
|
+
\u25A1 \u6D4B\u8BD5/\u5F00\u53D1\u73AF\u5883\u5173\u95ED\u6216\u4E0E\u751F\u4EA7\u4FDD\u6301\u540C\u7B49\u5B89\u5168\u57FA\u7EBF
|
|
4206
|
+
\u25A1 \u786E\u8BA4\u54EA\u4E9B\u73AF\u5883\u53EF\u4EE5\u7D27\u6025\u5173\u505C\uFF0C\u907F\u514D\u653B\u51FB\u6269\u6563
|
|
4207
|
+
|
|
4208
|
+
\u26A0\uFE0F \u503C\u5B88\u56E2\u961F\u7EC4\u5EFA
|
|
4209
|
+
\u25A1 7\xD724 \u76D1\u63A7\u5FEB\u901F\u54CD\u5E94\u56E2\u961F
|
|
4210
|
+
\u25A1 \u6280\u672F\u4E0E\u98CE\u9669\u5206\u6790\u7EC4
|
|
4211
|
+
\u25A1 \u5B89\u5168\u7B56\u7565\u4E0B\u53D1\u7EC4
|
|
4212
|
+
\u25A1 \u4E1A\u52A1\u54CD\u5E94\u7EC4
|
|
4213
|
+
\u25A1 \u660E\u786E AWS TAM/Support \u8054\u7CFB\u65B9\u5F0F\uFF08ES/EOP \u5BA2\u6237\uFF09
|
|
4214
|
+
|
|
4215
|
+
\u26A0\uFE0F \u51FA\u5165\u7AD9\u8DEF\u5F84\u67B6\u6784\u56FE
|
|
4216
|
+
\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
|
|
4217
|
+
\u25A1 \u660E\u786E\u5404 ELB/Public EC2/S3/DX \u7684\u6570\u636E\u6D41\u5411
|
|
4218
|
+
\u25A1 \u8BC6\u522B\u6240\u6709\u9762\u5411\u4E92\u8054\u7F51\u7684\u6570\u636E\u4EA4\u4E92\u63A5\u53E3
|
|
4219
|
+
|
|
4220
|
+
\u26A0\uFE0F \u4E3B\u52A8\u5F0F\u6E17\u900F\u6D4B\u8BD5
|
|
4221
|
+
\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
|
|
4222
|
+
\u25A1 \u57FA\u4E8E\u6E17\u900F\u6D4B\u8BD5\u62A5\u544A\u8FDB\u884C\u6B63\u5F0F\u62A4\u7F51\u524D\u7684\u5B89\u5168\u52A0\u56FA
|
|
4223
|
+
\u25A1 \u5173\u6CE8 AWS \u5B89\u5168\u516C\u544A\uFF08\u5DF2\u77E5\u6F0F\u6D1E\u4E0E\u8865\u4E01\uFF09
|
|
4224
|
+
|
|
4225
|
+
\u26A0\uFE0F WAR-ROOM \u5B9E\u65F6\u6C9F\u901A
|
|
4226
|
+
\u25A1 \u521B\u5EFA\u62A4\u7F51\u671F\u95F4\u4E13\u7528\u6C9F\u901A\u6E20\u9053\uFF08\u4F01\u5FAE/\u9489\u9489/\u98DE\u4E66/Chime\uFF09
|
|
4227
|
+
\u25A1 \u4E0E AWS TAM \u5EFA\u7ACB WAR-ROOM \u8054\u7CFB\uFF08\u4F01\u4E1A\u7EA7\u652F\u6301\u5BA2\u6237\uFF09
|
|
4228
|
+
\u25A1 \u7EDF\u4E00\u6848\u4F8B\u6807\u9898\u683C\u5F0F\uFF1A\u201C\u3010\u62A4\u7F51\u3011+ \u95EE\u9898\u63CF\u8FF0\u201D
|
|
4229
|
+
|
|
4230
|
+
\u26A0\uFE0F \u5BC6\u7801\u4E0E\u51ED\u8BC1\u7BA1\u7406
|
|
4231
|
+
\u25A1 \u6240\u6709 IAM \u7528\u6237\u7ED1\u5B9A MFA
|
|
4232
|
+
\u25A1 AKSK \u8F6E\u8F6C\u5468\u671F \u2264 90 \u5929
|
|
4233
|
+
\u25A1 \u907F\u514D\u5171\u4EAB\u8D26\u6237\u4F7F\u7528
|
|
4234
|
+
\u25A1 S3/Lambda/\u5E94\u7528\u4EE3\u7801\u4E2D\u65E0\u660E\u6587\u5BC6\u7801
|
|
4235
|
+
|
|
4236
|
+
\u26A0\uFE0F \u62A4\u7F51\u540E\u4F18\u5316
|
|
4237
|
+
\u25A1 \u9488\u5BF9\u653B\u51FB\u62A5\u544A\u9010\u9879\u5E94\u7B54\u4E0E\u4FEE\u590D
|
|
4238
|
+
\u25A1 \u4E0E\u5B89\u5168\u56E2\u961F\u5EFA\u7ACB\u5468\u671F\u6027\u5B89\u5168\u7EF4\u62A4\u6D41\u7A0B
|
|
4239
|
+
\u25A1 \u6301\u7EED\u8865\u5168\u5B89\u5168\u98CE\u9669
|
|
4240
|
+
|
|
4241
|
+
\u53C2\u8003\uFF1AAWS \u62A4\u7F51\u884C\u52A8 Standard Operation Procedure (Compliance IEM)
|
|
4242
|
+
`
|
|
4243
|
+
};
|
|
4244
|
+
|
|
4245
|
+
// src/i18n/en.ts
|
|
4246
|
+
var enI18n = {
|
|
4247
|
+
// HTML Security Report
|
|
4248
|
+
securityReportTitle: "AWS Security Scan Report",
|
|
4249
|
+
securityScore: "Security Score",
|
|
4250
|
+
critical: "Critical",
|
|
4251
|
+
high: "High",
|
|
4252
|
+
medium: "Medium",
|
|
4253
|
+
low: "Low",
|
|
4254
|
+
scanStatistics: "Scan Statistics",
|
|
4255
|
+
module: "Module",
|
|
4256
|
+
resources: "Resources",
|
|
4257
|
+
findings: "Findings",
|
|
4258
|
+
status: "Status",
|
|
4259
|
+
allFindings: "All Findings",
|
|
4260
|
+
recommendations: "Recommendations",
|
|
4261
|
+
unique: "unique",
|
|
4262
|
+
showMore: "Show more",
|
|
4263
|
+
noIssuesFound: "No security issues found.",
|
|
4264
|
+
allModulesClean: "All modules clean",
|
|
4265
|
+
generatedBy: "Generated by AWS Security MCP Server",
|
|
4266
|
+
informationalOnly: "This report is for informational purposes only.",
|
|
4267
|
+
// MLPS Report
|
|
4268
|
+
mlpsTitle: "MLPS Level 3 Pre-Check Report",
|
|
4269
|
+
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)",
|
|
4270
|
+
checkedItems: "Checked Items",
|
|
4271
|
+
noIssues: "No Issues Found",
|
|
4272
|
+
issuesFound: "Issues Found",
|
|
4273
|
+
notChecked: "Not Checked",
|
|
4274
|
+
cloudProvider: "Cloud Provider Responsible",
|
|
4275
|
+
manualReview: "Manual Review Required",
|
|
4276
|
+
notApplicable: "Not Applicable",
|
|
4277
|
+
checkResult: "Check Result",
|
|
4278
|
+
noRelatedIssues: "Check Result: No related issues found",
|
|
4279
|
+
issuesFoundCount: (n) => `Check Result: Found ${n} related issue${n === 1 ? "" : "s"}`,
|
|
4280
|
+
remediation: "Remediation",
|
|
4281
|
+
remediationItems: (n) => `Remediation Items (${n} unique)`,
|
|
4282
|
+
showRemaining: (n) => `Show remaining ${n} items`,
|
|
4283
|
+
// HW Defense Checklist
|
|
4284
|
+
hwChecklistTitle: "\u{1F4CB} Cyber Defense Drill Supplementary Reminders (Beyond Automated Scanning)",
|
|
4285
|
+
hwChecklistSubtitle: "The following items require manual verification and execution:",
|
|
4286
|
+
hwEmergencyIsolation: `\u26A0\uFE0F Emergency Isolation / Incident Response Plan
|
|
4287
|
+
\u25A1 Prepare dedicated isolation security groups (no Inbound/Outbound rules)
|
|
4288
|
+
\u25A1 Establish instance isolation SOP: Alert \u2192 Investigate \u2192 Block attacker IP \u2192 Network isolation \u2192 Security response \u2192 Log attack details
|
|
4289
|
+
\u25A1 Define emergency response procedures for each system (production core/non-core/test/dev)
|
|
4290
|
+
\u25A1 Identify responsible personnel and contacts for each project account and resource`,
|
|
4291
|
+
hwTestEnvShutdown: `\u26A0\uFE0F Test/Development Environment Handling
|
|
4292
|
+
\u25A1 Shut down non-critical systems during the drill period
|
|
4293
|
+
\u25A1 Shut down test/dev environments or maintain same security baseline as production
|
|
4294
|
+
\u25A1 Confirm which environments can be emergency-stopped to prevent attack propagation`,
|
|
4295
|
+
hwDutyTeam: `\u26A0\uFE0F On-Duty Team Formation
|
|
4296
|
+
\u25A1 7\xD724 monitoring and rapid response team
|
|
4297
|
+
\u25A1 Technical and risk analysis team
|
|
4298
|
+
\u25A1 Security policy deployment team
|
|
4299
|
+
\u25A1 Business response team
|
|
4300
|
+
\u25A1 Confirm AWS TAM/Support contact information (ES/EOP customers)`,
|
|
4301
|
+
hwNetworkDiagram: `\u26A0\uFE0F Ingress/Egress Path Architecture Diagram
|
|
4302
|
+
\u25A1 Ensure all Internet/DX dedicated line ingress/egress paths are clearly marked in architecture diagrams
|
|
4303
|
+
\u25A1 Clarify data flow for each ELB/Public EC2/S3/DX
|
|
4304
|
+
\u25A1 Identify all internet-facing data interaction interfaces`,
|
|
4305
|
+
hwPentest: `\u26A0\uFE0F Proactive Penetration Testing
|
|
4306
|
+
\u25A1 Contact security vendors for simulated attack drills before the exercise
|
|
4307
|
+
\u25A1 Conduct security hardening based on penetration test reports
|
|
4308
|
+
\u25A1 Monitor AWS security advisories (known vulnerabilities and patches)`,
|
|
4309
|
+
hwWarRoom: `\u26A0\uFE0F WAR-ROOM Real-Time Communication
|
|
4310
|
+
\u25A1 Create dedicated communication channels for the drill period (Teams/Slack/Chime)
|
|
4311
|
+
\u25A1 Establish WAR-ROOM connection with AWS TAM (Enterprise Support customers)
|
|
4312
|
+
\u25A1 Standardize case title format: "[CyberDrill] + Issue Description"`,
|
|
4313
|
+
hwCredentials: `\u26A0\uFE0F Password & Credential Management
|
|
4314
|
+
\u25A1 All IAM users must have MFA enabled
|
|
4315
|
+
\u25A1 Access key rotation cycle \u2264 90 days
|
|
4316
|
+
\u25A1 Avoid shared account usage
|
|
4317
|
+
\u25A1 No plaintext passwords in S3/Lambda/application code`,
|
|
4318
|
+
hwPostOptimization: `\u26A0\uFE0F Post-Drill Optimization
|
|
4319
|
+
\u25A1 Address and remediate each item from the attack report
|
|
4320
|
+
\u25A1 Establish periodic security maintenance processes with the security team
|
|
4321
|
+
\u25A1 Continuously fill security risk gaps`,
|
|
4322
|
+
hwReference: "Reference: AWS Cyber Defense Drill Standard Operation Procedure (Compliance IEM)",
|
|
4323
|
+
// Service Reminders
|
|
4324
|
+
serviceReminderTitle: "\u26A1 The following security services are not enabled; some checks cannot be performed:",
|
|
4325
|
+
serviceReminderFooter: "Re-scan after enabling the above services for a more complete security assessment.",
|
|
4326
|
+
serviceImpact: "Impact",
|
|
4327
|
+
serviceAction: "Action",
|
|
4328
|
+
// Common
|
|
4329
|
+
account: "Account",
|
|
4330
|
+
region: "Region",
|
|
4331
|
+
scanTime: "Scan Time",
|
|
4332
|
+
duration: "Duration",
|
|
4333
|
+
severityDistribution: "Severity Distribution",
|
|
4334
|
+
findingsByModule: "Findings by Module",
|
|
4335
|
+
details: "Details",
|
|
4336
|
+
// Extended \u2014 HTML Security Report extras
|
|
4337
|
+
topHighestRiskFindings: (n) => `Top ${n} Highest Risk Findings`,
|
|
4338
|
+
resource: "Resource",
|
|
4339
|
+
impact: "Impact",
|
|
4340
|
+
riskScore: "Risk Score",
|
|
4341
|
+
showRemainingFindings: (n) => `Show remaining ${n} findings\u2026`,
|
|
4342
|
+
trendTitle: "30-Day Trends",
|
|
4343
|
+
findingsBySeverity: "Findings by Severity",
|
|
4344
|
+
showMoreCount: (n) => `Show ${n} more\u2026`,
|
|
4345
|
+
// Extended \u2014 MLPS extras
|
|
4346
|
+
// Markdown report
|
|
4347
|
+
executiveSummary: "Executive Summary",
|
|
4348
|
+
totalFindingsLabel: "Total Findings",
|
|
4349
|
+
description: "Description",
|
|
4350
|
+
priority: "Priority",
|
|
4351
|
+
noFindingsForSeverity: (severity) => `No ${severity.toLowerCase()} findings.`,
|
|
4352
|
+
preCheckOverview: "Pre-Check Overview",
|
|
4353
|
+
accountInfo: "Account Information",
|
|
4354
|
+
checkedCount: (total, clean, issues) => `Checked: ${total} items (No issues: ${clean} | Issues found: ${issues})`,
|
|
4355
|
+
uncheckedCount: (n) => `Not checked: ${n} items (corresponding scan modules not run)`,
|
|
4356
|
+
cloudProviderCount: (n) => `Cloud provider responsible: ${n} items`,
|
|
4357
|
+
manualReviewCount: (n) => `Manual review required: ${n} items`,
|
|
4358
|
+
naCount: (n) => `Not applicable: ${n} items`,
|
|
4359
|
+
naNote: (n) => `Not applicable: ${n} items (IoT/wireless networks/mobile terminals/ICS/trusted verification, etc.)`,
|
|
4360
|
+
unknownNote: (n) => `(${n} items not checked \u2014 corresponding scan modules not run)`,
|
|
4361
|
+
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.`,
|
|
4362
|
+
mlpsFooterGenerated: (version) => `Generated by AWS Security MCP Server v${version}`,
|
|
4363
|
+
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.",
|
|
4364
|
+
andMore: (n) => `\u2026 and ${n} more`,
|
|
4365
|
+
remediationByPriority: "Remediation Items (by Priority)",
|
|
4366
|
+
affectedResources: (n) => `${n} resource${n === 1 ? "" : "s"} affected`,
|
|
4367
|
+
installWindowsPatches: (n, kbs) => `Install ${n} Windows patch${n === 1 ? "" : "es"} (${kbs})`,
|
|
4368
|
+
mlpsCategorySection: {
|
|
4369
|
+
"\u5B89\u5168\u7269\u7406\u73AF\u5883": "I. Physical Environment Security",
|
|
4370
|
+
"\u5B89\u5168\u901A\u4FE1\u7F51\u7EDC": "II. Communication Network Security",
|
|
4371
|
+
"\u5B89\u5168\u533A\u57DF\u8FB9\u754C": "III. Area Boundary Security",
|
|
4372
|
+
"\u5B89\u5168\u8BA1\u7B97\u73AF\u5883": "IV. Computing Environment Security",
|
|
4373
|
+
"\u5B89\u5168\u7BA1\u7406\u4E2D\u5FC3": "V. Security Management Center"
|
|
4374
|
+
},
|
|
4375
|
+
// Service Recommendations
|
|
4376
|
+
notEnabled: "Not Enabled",
|
|
4377
|
+
serviceRecommendations: {
|
|
4378
|
+
security_hub_findings: {
|
|
4379
|
+
icon: "\u{1F534}",
|
|
4380
|
+
service: "Security Hub",
|
|
4381
|
+
impact: "Cannot obtain 300+ automated security checks (FSBP/CIS/PCI DSS standards)",
|
|
4382
|
+
action: "Enable Security Hub for the most comprehensive security posture assessment"
|
|
4383
|
+
},
|
|
4384
|
+
guardduty_findings: {
|
|
4385
|
+
icon: "\u{1F534}",
|
|
4386
|
+
service: "GuardDuty",
|
|
4387
|
+
impact: "Cannot detect threat activity (malicious IPs, anomalous API calls, crypto mining, etc.)",
|
|
4388
|
+
action: "Enable GuardDuty for continuous threat detection"
|
|
4389
|
+
},
|
|
4390
|
+
inspector_findings: {
|
|
4391
|
+
icon: "\u{1F7E1}",
|
|
4392
|
+
service: "Inspector",
|
|
4393
|
+
impact: "Cannot scan EC2/Lambda/container software vulnerabilities (CVEs)",
|
|
4394
|
+
action: "Enable Inspector to discover known security vulnerabilities"
|
|
4395
|
+
},
|
|
4396
|
+
trusted_advisor_findings: {
|
|
4397
|
+
icon: "\u{1F7E1}",
|
|
4398
|
+
service: "Trusted Advisor",
|
|
4399
|
+
impact: "Cannot obtain AWS best practice security checks",
|
|
4400
|
+
action: "Upgrade to Business/Enterprise Support plan to use Trusted Advisor security checks"
|
|
4401
|
+
},
|
|
4402
|
+
config_rules_findings: {
|
|
4403
|
+
icon: "\u{1F7E1}",
|
|
4404
|
+
service: "AWS Config",
|
|
4405
|
+
impact: "Cannot check resource configuration compliance status",
|
|
4406
|
+
action: "Enable AWS Config and configure Config Rules"
|
|
4407
|
+
},
|
|
4408
|
+
access_analyzer_findings: {
|
|
4409
|
+
icon: "\u{1F7E1}",
|
|
4410
|
+
service: "IAM Access Analyzer",
|
|
4411
|
+
impact: "Cannot detect whether resources are accessed by external accounts or public networks",
|
|
4412
|
+
action: "Create IAM Access Analyzer (account-level or organization-level)"
|
|
4413
|
+
},
|
|
4414
|
+
patch_compliance_findings: {
|
|
4415
|
+
icon: "\u{1F7E1}",
|
|
4416
|
+
service: "SSM Patch Manager",
|
|
4417
|
+
impact: "Cannot check instance patch compliance status",
|
|
4418
|
+
action: "Install SSM Agent and configure Patch Manager"
|
|
4419
|
+
}
|
|
4420
|
+
},
|
|
4421
|
+
// HW Checklist (full composite)
|
|
4422
|
+
hwChecklist: `
|
|
4423
|
+
\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
4424
|
+
\u{1F4CB} Cyber Defense Drill Supplementary Reminders (Beyond Automated Scanning)
|
|
4425
|
+
\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
4426
|
+
|
|
4427
|
+
The following items require manual verification and execution:
|
|
4428
|
+
|
|
4429
|
+
\u26A0\uFE0F Emergency Isolation / Incident Response Plan
|
|
4430
|
+
\u25A1 Prepare dedicated isolation security groups (no Inbound/Outbound rules)
|
|
4431
|
+
\u25A1 Establish instance isolation SOP: Alert \u2192 Investigate \u2192 Block attacker IP \u2192 Network isolation \u2192 Security response \u2192 Log attack details
|
|
4432
|
+
\u25A1 Define emergency response procedures for each system (production core/non-core/test/dev)
|
|
4433
|
+
\u25A1 Identify responsible personnel and contacts for each project account and resource
|
|
4434
|
+
|
|
4435
|
+
\u26A0\uFE0F Test/Development Environment Handling
|
|
4436
|
+
\u25A1 Shut down non-critical systems during the drill period
|
|
4437
|
+
\u25A1 Shut down test/dev environments or maintain same security baseline as production
|
|
4438
|
+
\u25A1 Confirm which environments can be emergency-stopped to prevent attack propagation
|
|
4439
|
+
|
|
4440
|
+
\u26A0\uFE0F On-Duty Team Formation
|
|
4441
|
+
\u25A1 7\xD724 monitoring and rapid response team
|
|
4442
|
+
\u25A1 Technical and risk analysis team
|
|
4443
|
+
\u25A1 Security policy deployment team
|
|
4444
|
+
\u25A1 Business response team
|
|
4445
|
+
\u25A1 Confirm AWS TAM/Support contact information (ES/EOP customers)
|
|
4446
|
+
|
|
4447
|
+
\u26A0\uFE0F Ingress/Egress Path Architecture Diagram
|
|
4448
|
+
\u25A1 Ensure all Internet/DX dedicated line ingress/egress paths are clearly marked in architecture diagrams
|
|
4449
|
+
\u25A1 Clarify data flow for each ELB/Public EC2/S3/DX
|
|
4450
|
+
\u25A1 Identify all internet-facing data interaction interfaces
|
|
4451
|
+
|
|
4452
|
+
\u26A0\uFE0F Proactive Penetration Testing
|
|
4453
|
+
\u25A1 Contact security vendors for simulated attack drills before the exercise
|
|
4454
|
+
\u25A1 Conduct security hardening based on penetration test reports
|
|
4455
|
+
\u25A1 Monitor AWS security advisories (known vulnerabilities and patches)
|
|
4456
|
+
|
|
4457
|
+
\u26A0\uFE0F WAR-ROOM Real-Time Communication
|
|
4458
|
+
\u25A1 Create dedicated communication channels for the drill period (Teams/Slack/Chime)
|
|
4459
|
+
\u25A1 Establish WAR-ROOM connection with AWS TAM (Enterprise Support customers)
|
|
4460
|
+
\u25A1 Standardize case title format: "[CyberDrill] + Issue Description"
|
|
4461
|
+
|
|
4462
|
+
\u26A0\uFE0F Password & Credential Management
|
|
4463
|
+
\u25A1 All IAM users must have MFA enabled
|
|
4464
|
+
\u25A1 Access key rotation cycle \u2264 90 days
|
|
4465
|
+
\u25A1 Avoid shared account usage
|
|
4466
|
+
\u25A1 No plaintext passwords in S3/Lambda/application code
|
|
4467
|
+
|
|
4468
|
+
\u26A0\uFE0F Post-Drill Optimization
|
|
4469
|
+
\u25A1 Address and remediate each item from the attack report
|
|
4470
|
+
\u25A1 Establish periodic security maintenance processes with the security team
|
|
4471
|
+
\u25A1 Continuously fill security risk gaps
|
|
4472
|
+
|
|
4473
|
+
Reference: AWS Cyber Defense Drill Standard Operation Procedure (Compliance IEM)
|
|
4474
|
+
`
|
|
4475
|
+
};
|
|
4476
|
+
|
|
4477
|
+
// src/i18n/index.ts
|
|
4478
|
+
var translations = {
|
|
4479
|
+
zh: zhI18n,
|
|
4480
|
+
en: enI18n
|
|
4481
|
+
};
|
|
4482
|
+
function getI18n(lang = "zh") {
|
|
4483
|
+
return translations[lang] ?? translations.zh;
|
|
4484
|
+
}
|
|
4485
|
+
|
|
3971
4486
|
// src/tools/report-tool.ts
|
|
3972
4487
|
var SEVERITY_ICON = {
|
|
3973
4488
|
CRITICAL: "\u{1F534}",
|
|
@@ -3985,38 +4500,45 @@ function formatDuration(start, end) {
|
|
|
3985
4500
|
const remainSecs = secs % 60;
|
|
3986
4501
|
return `${mins}m ${remainSecs}s`;
|
|
3987
4502
|
}
|
|
3988
|
-
function
|
|
3989
|
-
const
|
|
3990
|
-
return [
|
|
3991
|
-
`#### ${f.title}`,
|
|
3992
|
-
`- **Resource:** ${f.resourceId} (\`${f.resourceArn}\`)`,
|
|
3993
|
-
`- **Description:** ${f.description}`,
|
|
3994
|
-
`- **Impact:** ${f.impact}`,
|
|
3995
|
-
`- **Risk Score:** ${f.riskScore}/10`,
|
|
3996
|
-
`- **Remediation:**`,
|
|
3997
|
-
steps,
|
|
3998
|
-
`- **Priority:** ${f.priority}`
|
|
3999
|
-
].join("\n");
|
|
4000
|
-
}
|
|
4001
|
-
function generateMarkdownReport(scanResults) {
|
|
4503
|
+
function generateMarkdownReport(scanResults, lang) {
|
|
4504
|
+
const t = getI18n(lang ?? "zh");
|
|
4002
4505
|
const { summary, modules, accountId, region, scanStart, scanEnd } = scanResults;
|
|
4003
4506
|
const date = scanStart.split("T")[0];
|
|
4004
4507
|
const duration = formatDuration(scanStart, scanEnd);
|
|
4508
|
+
const sevLabel = {
|
|
4509
|
+
CRITICAL: t.critical,
|
|
4510
|
+
HIGH: t.high,
|
|
4511
|
+
MEDIUM: t.medium,
|
|
4512
|
+
LOW: t.low
|
|
4513
|
+
};
|
|
4514
|
+
function renderFinding(f) {
|
|
4515
|
+
const steps = f.remediationSteps.map((s, i) => ` ${i + 1}. ${s}`).join("\n");
|
|
4516
|
+
return [
|
|
4517
|
+
`#### ${f.title}`,
|
|
4518
|
+
`- **${t.resource}:** ${f.resourceId} (\`${f.resourceArn}\`)`,
|
|
4519
|
+
`- **${t.description}:** ${f.description}`,
|
|
4520
|
+
`- **${t.impact}:** ${f.impact}`,
|
|
4521
|
+
`- **${t.riskScore}:** ${f.riskScore}/10`,
|
|
4522
|
+
`- **${t.remediation}:**`,
|
|
4523
|
+
steps,
|
|
4524
|
+
`- **${t.priority}:** ${f.priority}`
|
|
4525
|
+
].join("\n");
|
|
4526
|
+
}
|
|
4005
4527
|
const lines = [];
|
|
4006
|
-
lines.push(`#
|
|
4528
|
+
lines.push(`# ${t.securityReportTitle} \u2014 ${date}`);
|
|
4007
4529
|
lines.push("");
|
|
4008
|
-
lines.push(
|
|
4009
|
-
lines.push(`-
|
|
4010
|
-
lines.push(`-
|
|
4011
|
-
lines.push(`-
|
|
4530
|
+
lines.push(`## ${t.executiveSummary}`);
|
|
4531
|
+
lines.push(`- **${t.account}:** ${accountId}`);
|
|
4532
|
+
lines.push(`- **${t.region}:** ${region}`);
|
|
4533
|
+
lines.push(`- **${t.duration}:** ${duration}`);
|
|
4012
4534
|
lines.push(
|
|
4013
|
-
`-
|
|
4535
|
+
`- **${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})`
|
|
4014
4536
|
);
|
|
4015
4537
|
lines.push("");
|
|
4016
4538
|
if (summary.totalFindings === 0) {
|
|
4017
|
-
lines.push(
|
|
4539
|
+
lines.push(`## ${t.findingsBySeverity}`);
|
|
4018
4540
|
lines.push("");
|
|
4019
|
-
lines.push(
|
|
4541
|
+
lines.push(`\u2705 ${t.noIssuesFound}`);
|
|
4020
4542
|
lines.push("");
|
|
4021
4543
|
} else {
|
|
4022
4544
|
const allFindings = modules.flatMap((m) => m.findings);
|
|
@@ -4027,15 +4549,15 @@ function generateMarkdownReport(scanResults) {
|
|
|
4027
4549
|
for (const f of allFindings) {
|
|
4028
4550
|
grouped.get(f.severity).push(f);
|
|
4029
4551
|
}
|
|
4030
|
-
lines.push(
|
|
4552
|
+
lines.push(`## ${t.findingsBySeverity}`);
|
|
4031
4553
|
lines.push("");
|
|
4032
4554
|
for (const sev of SEVERITY_ORDER) {
|
|
4033
4555
|
const findings = grouped.get(sev);
|
|
4034
4556
|
const icon = SEVERITY_ICON[sev];
|
|
4035
|
-
lines.push(`### ${icon} ${sev
|
|
4557
|
+
lines.push(`### ${icon} ${sevLabel[sev]}`);
|
|
4036
4558
|
lines.push("");
|
|
4037
4559
|
if (findings.length === 0) {
|
|
4038
|
-
lines.push(
|
|
4560
|
+
lines.push(t.noFindingsForSeverity(sevLabel[sev]));
|
|
4039
4561
|
lines.push("");
|
|
4040
4562
|
continue;
|
|
4041
4563
|
}
|
|
@@ -4046,9 +4568,9 @@ function generateMarkdownReport(scanResults) {
|
|
|
4046
4568
|
}
|
|
4047
4569
|
}
|
|
4048
4570
|
}
|
|
4049
|
-
lines.push(
|
|
4571
|
+
lines.push(`## ${t.scanStatistics}`);
|
|
4050
4572
|
lines.push(
|
|
4051
|
-
|
|
4573
|
+
`| ${t.module} | ${t.resources} | ${t.findings} | ${t.status} |`
|
|
4052
4574
|
);
|
|
4053
4575
|
lines.push("|--------|------------------|----------|--------|");
|
|
4054
4576
|
for (const m of modules) {
|
|
@@ -4061,7 +4583,7 @@ function generateMarkdownReport(scanResults) {
|
|
|
4061
4583
|
if (summary.totalFindings > 0) {
|
|
4062
4584
|
const allFindings = modules.flatMap((m) => m.findings);
|
|
4063
4585
|
allFindings.sort((a, b) => b.riskScore - a.riskScore);
|
|
4064
|
-
lines.push(
|
|
4586
|
+
lines.push(`## ${t.recommendations}`);
|
|
4065
4587
|
for (let i = 0; i < allFindings.length; i++) {
|
|
4066
4588
|
const f = allFindings[i];
|
|
4067
4589
|
lines.push(`${i + 1}. [${f.priority}] ${f.title}: ${f.remediationSteps[0] ?? "Review and remediate."}`);
|
|
@@ -6108,13 +6630,6 @@ var MLPS3_CATEGORY_ORDER = [
|
|
|
6108
6630
|
"\u5B89\u5168\u8BA1\u7B97\u73AF\u5883",
|
|
6109
6631
|
"\u5B89\u5168\u7BA1\u7406\u4E2D\u5FC3"
|
|
6110
6632
|
];
|
|
6111
|
-
var MLPS3_CATEGORY_SECTION = {
|
|
6112
|
-
"\u5B89\u5168\u7269\u7406\u73AF\u5883": "\u4E00\u3001\u5B89\u5168\u7269\u7406\u73AF\u5883",
|
|
6113
|
-
"\u5B89\u5168\u901A\u4FE1\u7F51\u7EDC": "\u4E8C\u3001\u5B89\u5168\u901A\u4FE1\u7F51\u7EDC",
|
|
6114
|
-
"\u5B89\u5168\u533A\u57DF\u8FB9\u754C": "\u4E09\u3001\u5B89\u5168\u533A\u57DF\u8FB9\u754C",
|
|
6115
|
-
"\u5B89\u5168\u8BA1\u7B97\u73AF\u5883": "\u56DB\u3001\u5B89\u5168\u8BA1\u7B97\u73AF\u5883",
|
|
6116
|
-
"\u5B89\u5168\u7BA1\u7406\u4E2D\u5FC3": "\u4E94\u3001\u5B89\u5168\u7BA1\u7406\u4E2D\u5FC3"
|
|
6117
|
-
};
|
|
6118
6633
|
var MLPS3_CHECK_MAPPING = [
|
|
6119
6634
|
// =========================================================================
|
|
6120
6635
|
// 安全物理环境 — L3-PES1-* (22 items) → cloud_provider
|
|
@@ -6774,7 +7289,9 @@ function evaluateAllFullChecks(scanResults) {
|
|
|
6774
7289
|
return evaluateFullCheck(item, mapping, allFindings, scanModules);
|
|
6775
7290
|
});
|
|
6776
7291
|
}
|
|
6777
|
-
function generateMlps3Report(scanResults) {
|
|
7292
|
+
function generateMlps3Report(scanResults, lang) {
|
|
7293
|
+
const t = getI18n(lang ?? "zh");
|
|
7294
|
+
const isEn = (lang ?? "zh") === "en";
|
|
6778
7295
|
const { accountId, region, scanStart } = scanResults;
|
|
6779
7296
|
const scanTime = scanStart.replace("T", " ").replace(/\.\d+Z$/, " UTC");
|
|
6780
7297
|
const results = evaluateAllFullChecks(scanResults);
|
|
@@ -6786,27 +7303,28 @@ function generateMlps3Report(scanResults) {
|
|
|
6786
7303
|
const cloudCount = results.filter((r) => r.status === "cloud_provider").length;
|
|
6787
7304
|
const manualCount = results.filter((r) => r.status === "manual").length;
|
|
6788
7305
|
const naCount = results.filter((r) => r.status === "not_applicable").length;
|
|
7306
|
+
const itemControl = (r) => isEn ? r.item.controlEn : r.item.controlCn;
|
|
7307
|
+
const itemReq = (r) => isEn ? r.item.requirementEn : r.item.requirementCn;
|
|
6789
7308
|
const lines = [];
|
|
6790
|
-
lines.push(
|
|
6791
|
-
lines.push(
|
|
6792
|
-
lines.push("> **\uFF08GB/T 22239-2019 \u5B8C\u6574\u68C0\u67E5\u6E05\u5355 184 \u9879\uFF09**");
|
|
7309
|
+
lines.push(`# ${t.mlpsTitle}`);
|
|
7310
|
+
lines.push(`> **${t.mlpsDisclaimer}**`);
|
|
6793
7311
|
lines.push("");
|
|
6794
|
-
lines.push(
|
|
6795
|
-
lines.push(`-
|
|
7312
|
+
lines.push(`## ${t.accountInfo}`);
|
|
7313
|
+
lines.push(`- ${t.account}: ${accountId} | ${t.region}: ${region} | ${t.scanTime}: ${scanTime}`);
|
|
6796
7314
|
lines.push("");
|
|
6797
|
-
lines.push(
|
|
6798
|
-
lines.push(`-
|
|
7315
|
+
lines.push(`## ${t.preCheckOverview}`);
|
|
7316
|
+
lines.push(`- ${t.checkedCount(checkedTotal, autoClean, autoIssues)}`);
|
|
6799
7317
|
if (autoUnknown > 0) {
|
|
6800
|
-
lines.push(`-
|
|
7318
|
+
lines.push(`- ${t.uncheckedCount(autoUnknown)}`);
|
|
6801
7319
|
}
|
|
6802
|
-
lines.push(`-
|
|
6803
|
-
lines.push(`-
|
|
7320
|
+
lines.push(`- ${t.cloudProviderCount(cloudCount)}`);
|
|
7321
|
+
lines.push(`- ${t.manualReviewCount(manualCount)}`);
|
|
6804
7322
|
if (naCount > 0) {
|
|
6805
|
-
lines.push(`-
|
|
7323
|
+
lines.push(`- ${t.naCount(naCount)}`);
|
|
6806
7324
|
}
|
|
6807
7325
|
lines.push("");
|
|
6808
7326
|
for (const category of MLPS3_CATEGORY_ORDER) {
|
|
6809
|
-
const sectionTitle =
|
|
7327
|
+
const sectionTitle = t.mlpsCategorySection[category] ?? category;
|
|
6810
7328
|
const catResults = results.filter(
|
|
6811
7329
|
(r) => r.item.categoryCn === category && r.status !== "not_applicable"
|
|
6812
7330
|
);
|
|
@@ -6819,18 +7337,20 @@ function generateMlps3Report(scanResults) {
|
|
|
6819
7337
|
if (!controlMap.has(key)) controlMap.set(key, []);
|
|
6820
7338
|
controlMap.get(key).push(r);
|
|
6821
7339
|
}
|
|
6822
|
-
for (const [
|
|
7340
|
+
for (const [_controlKey, controlResults] of controlMap) {
|
|
7341
|
+
const controlName = itemControl(controlResults[0]);
|
|
6823
7342
|
lines.push(`### ${controlName}`);
|
|
6824
7343
|
for (const r of controlResults) {
|
|
6825
7344
|
const icon = r.status === "clean" ? "\u2705" : r.status === "issues" ? "\u274C" : r.status === "unknown" ? "\u26A0\uFE0F" : r.status === "manual" ? "\u{1F4CB}" : "\u{1F3E2}";
|
|
6826
|
-
const suffix = r.status === "unknown" ?
|
|
6827
|
-
|
|
7345
|
+
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}`;
|
|
7346
|
+
const reqText = itemReq(r);
|
|
7347
|
+
lines.push(`- [${icon}] ${r.item.id} ${reqText.slice(0, 60)}${reqText.length > 60 ? "\u2026" : ""}${suffix}`);
|
|
6828
7348
|
if (r.status === "issues" && r.relatedFindings.length > 0) {
|
|
6829
7349
|
for (const f of r.relatedFindings.slice(0, 3)) {
|
|
6830
7350
|
lines.push(` - ${f.severity}: ${f.title}`);
|
|
6831
7351
|
}
|
|
6832
7352
|
if (r.relatedFindings.length > 3) {
|
|
6833
|
-
lines.push(` -
|
|
7353
|
+
lines.push(` - ${t.andMore(r.relatedFindings.length - 3)}`);
|
|
6834
7354
|
}
|
|
6835
7355
|
}
|
|
6836
7356
|
}
|
|
@@ -6839,7 +7359,7 @@ function generateMlps3Report(scanResults) {
|
|
|
6839
7359
|
}
|
|
6840
7360
|
const failedResults = results.filter((r) => r.status === "issues");
|
|
6841
7361
|
if (failedResults.length > 0) {
|
|
6842
|
-
lines.push(
|
|
7362
|
+
lines.push(`## ${t.remediationByPriority}`);
|
|
6843
7363
|
lines.push("");
|
|
6844
7364
|
const allFailedFindings = /* @__PURE__ */ new Map();
|
|
6845
7365
|
for (const r of failedResults) {
|
|
@@ -6862,7 +7382,7 @@ function generateMlps3Report(scanResults) {
|
|
|
6862
7382
|
lines.push("");
|
|
6863
7383
|
}
|
|
6864
7384
|
if (naCount > 0) {
|
|
6865
|
-
lines.push(`>
|
|
7385
|
+
lines.push(`> ${t.naNote(naCount)}`);
|
|
6866
7386
|
lines.push("");
|
|
6867
7387
|
}
|
|
6868
7388
|
return lines.join("\n");
|
|
@@ -6872,6 +7392,15 @@ function generateMlps3Report(scanResults) {
|
|
|
6872
7392
|
function esc(s) {
|
|
6873
7393
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
6874
7394
|
}
|
|
7395
|
+
function escWithLinks(s) {
|
|
7396
|
+
const parts = s.split(/(https?:\/\/\S+)/);
|
|
7397
|
+
return parts.map((part, i) => {
|
|
7398
|
+
if (i % 2 === 1) {
|
|
7399
|
+
return `<a href="${esc(part)}" style="color:#60a5fa" target="_blank" rel="noopener">${esc(part)}</a>`;
|
|
7400
|
+
}
|
|
7401
|
+
return esc(part);
|
|
7402
|
+
}).join("");
|
|
7403
|
+
}
|
|
6875
7404
|
function calcScore(summary) {
|
|
6876
7405
|
const raw = 100 - summary.critical * 15 - summary.high * 5 - summary.medium * 2 - summary.low * 0.5;
|
|
6877
7406
|
return Math.max(0, Math.min(100, Math.round(raw)));
|
|
@@ -6895,50 +7424,6 @@ function scoreColor(score) {
|
|
|
6895
7424
|
if (score >= 50) return "#eab308";
|
|
6896
7425
|
return "#ef4444";
|
|
6897
7426
|
}
|
|
6898
|
-
var SERVICE_RECOMMENDATIONS = {
|
|
6899
|
-
security_hub_findings: {
|
|
6900
|
-
icon: "\u{1F534}",
|
|
6901
|
-
service: "Security Hub",
|
|
6902
|
-
impact: "\u65E0\u6CD5\u83B7\u53D6 300+ \u9879\u81EA\u52A8\u5316\u5B89\u5168\u68C0\u67E5\uFF08FSBP/CIS/PCI DSS \u6807\u51C6\uFF09",
|
|
6903
|
-
action: "\u542F\u7528 Security Hub \u83B7\u5F97\u6700\u5168\u9762\u7684\u5B89\u5168\u6001\u52BF\u8BC4\u4F30"
|
|
6904
|
-
},
|
|
6905
|
-
guardduty_findings: {
|
|
6906
|
-
icon: "\u{1F534}",
|
|
6907
|
-
service: "GuardDuty",
|
|
6908
|
-
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",
|
|
6909
|
-
action: "\u542F\u7528 GuardDuty \u83B7\u5F97\u6301\u7EED\u5A01\u80C1\u68C0\u6D4B\u80FD\u529B"
|
|
6910
|
-
},
|
|
6911
|
-
inspector_findings: {
|
|
6912
|
-
icon: "\u{1F7E1}",
|
|
6913
|
-
service: "Inspector",
|
|
6914
|
-
impact: "\u65E0\u6CD5\u626B\u63CF EC2/Lambda/\u5BB9\u5668\u7684\u8F6F\u4EF6\u6F0F\u6D1E\uFF08CVE\uFF09",
|
|
6915
|
-
action: "\u542F\u7528 Inspector \u53D1\u73B0\u5DF2\u77E5\u5B89\u5168\u6F0F\u6D1E"
|
|
6916
|
-
},
|
|
6917
|
-
trusted_advisor_findings: {
|
|
6918
|
-
icon: "\u{1F7E1}",
|
|
6919
|
-
service: "Trusted Advisor",
|
|
6920
|
-
impact: "\u65E0\u6CD5\u83B7\u53D6 AWS \u6700\u4F73\u5B9E\u8DF5\u5B89\u5168\u68C0\u67E5",
|
|
6921
|
-
action: "\u5347\u7EA7\u81F3 Business/Enterprise Support \u8BA1\u5212\u4EE5\u4F7F\u7528 Trusted Advisor \u5B89\u5168\u68C0\u67E5"
|
|
6922
|
-
},
|
|
6923
|
-
config_rules_findings: {
|
|
6924
|
-
icon: "\u{1F7E1}",
|
|
6925
|
-
service: "AWS Config",
|
|
6926
|
-
impact: "\u65E0\u6CD5\u68C0\u67E5\u8D44\u6E90\u914D\u7F6E\u5408\u89C4\u72B6\u6001",
|
|
6927
|
-
action: "\u542F\u7528 AWS Config \u5E76\u914D\u7F6E Config Rules"
|
|
6928
|
-
},
|
|
6929
|
-
access_analyzer_findings: {
|
|
6930
|
-
icon: "\u{1F7E1}",
|
|
6931
|
-
service: "IAM Access Analyzer",
|
|
6932
|
-
impact: "\u65E0\u6CD5\u68C0\u6D4B\u8D44\u6E90\u662F\u5426\u88AB\u5916\u90E8\u8D26\u53F7\u6216\u516C\u7F51\u8BBF\u95EE",
|
|
6933
|
-
action: "\u521B\u5EFA IAM Access Analyzer\uFF08\u8D26\u6237\u7EA7\u6216\u7EC4\u7EC7\u7EA7\uFF09"
|
|
6934
|
-
},
|
|
6935
|
-
patch_compliance_findings: {
|
|
6936
|
-
icon: "\u{1F7E1}",
|
|
6937
|
-
service: "SSM Patch Manager",
|
|
6938
|
-
impact: "\u65E0\u6CD5\u68C0\u67E5\u5B9E\u4F8B\u8865\u4E01\u5408\u89C4\u72B6\u6001",
|
|
6939
|
-
action: "\u5B89\u88C5 SSM Agent \u5E76\u914D\u7F6E Patch Manager"
|
|
6940
|
-
}
|
|
6941
|
-
};
|
|
6942
7427
|
var SERVICE_NOT_ENABLED_PATTERNS = [
|
|
6943
7428
|
"not enabled",
|
|
6944
7429
|
"not found",
|
|
@@ -6948,10 +7433,11 @@ var SERVICE_NOT_ENABLED_PATTERNS = [
|
|
|
6948
7433
|
"not available",
|
|
6949
7434
|
"is not enabled"
|
|
6950
7435
|
];
|
|
6951
|
-
function getDisabledServices(modules) {
|
|
7436
|
+
function getDisabledServices(modules, lang) {
|
|
7437
|
+
const t = getI18n(lang ?? "zh");
|
|
6952
7438
|
const disabled = [];
|
|
6953
7439
|
for (const mod of modules) {
|
|
6954
|
-
const rec =
|
|
7440
|
+
const rec = t.serviceRecommendations[mod.module];
|
|
6955
7441
|
if (!rec) continue;
|
|
6956
7442
|
if (!mod.warnings?.length) continue;
|
|
6957
7443
|
const hasNotEnabled = mod.warnings.some(
|
|
@@ -6963,21 +7449,22 @@ function getDisabledServices(modules) {
|
|
|
6963
7449
|
}
|
|
6964
7450
|
return disabled;
|
|
6965
7451
|
}
|
|
6966
|
-
function buildServiceReminderHtml(modules) {
|
|
6967
|
-
const
|
|
7452
|
+
function buildServiceReminderHtml(modules, lang) {
|
|
7453
|
+
const t = getI18n(lang ?? "zh");
|
|
7454
|
+
const disabled = getDisabledServices(modules, lang);
|
|
6968
7455
|
if (disabled.length === 0) return "";
|
|
6969
7456
|
const items = disabled.map((svc) => `
|
|
6970
7457
|
<div style="margin-bottom:12px">
|
|
6971
|
-
<div style="font-weight:600;font-size:15px">${esc(svc.icon)} ${esc(svc.service)}
|
|
6972
|
-
<div style="margin-left:28px;color:#cbd5e1;font-size:13px"
|
|
6973
|
-
<div style="margin-left:28px;color:#cbd5e1;font-size:13px"
|
|
7458
|
+
<div style="font-weight:600;font-size:15px">${esc(svc.icon)} ${esc(svc.service)} ${esc(t.notEnabled)}</div>
|
|
7459
|
+
<div style="margin-left:28px;color:#cbd5e1;font-size:13px">${esc(t.serviceImpact)}\uFF1A${esc(svc.impact)}</div>
|
|
7460
|
+
<div style="margin-left:28px;color:#cbd5e1;font-size:13px">${esc(t.serviceAction)}\uFF1A${esc(svc.action)}</div>
|
|
6974
7461
|
</div>`).join("\n");
|
|
6975
7462
|
return `
|
|
6976
7463
|
<section>
|
|
6977
7464
|
<div style="background:#2d1f00;border:1px solid #b45309;border-radius:8px;padding:20px;margin-bottom:32px">
|
|
6978
|
-
<div style="font-size:17px;font-weight:700;margin-bottom:12px"
|
|
7465
|
+
<div style="font-size:17px;font-weight:700;margin-bottom:12px">${esc(t.serviceReminderTitle)}</div>
|
|
6979
7466
|
${items}
|
|
6980
|
-
<div style="margin-top:12px;font-size:13px;color:#fbbf24;font-weight:500"
|
|
7467
|
+
<div style="margin-top:12px;font-size:13px;color:#fbbf24;font-weight:500">${esc(t.serviceReminderFooter)}</div>
|
|
6981
7468
|
</div>
|
|
6982
7469
|
</section>`;
|
|
6983
7470
|
}
|
|
@@ -7179,12 +7666,12 @@ function donutChart(summary) {
|
|
|
7179
7666
|
"</svg>"
|
|
7180
7667
|
].join("\n");
|
|
7181
7668
|
}
|
|
7182
|
-
function barChart(modules) {
|
|
7669
|
+
function barChart(modules, allCleanLabel = "All modules clean") {
|
|
7183
7670
|
const withFindings = modules.filter((m) => m.findingsCount > 0).sort((a, b) => b.findingsCount - a.findingsCount).slice(0, 12);
|
|
7184
7671
|
if (withFindings.length === 0) {
|
|
7185
7672
|
return [
|
|
7186
7673
|
'<svg viewBox="0 0 400 50" width="100%">',
|
|
7187
|
-
|
|
7674
|
+
` <text x="200" y="30" text-anchor="middle" fill="#22c55e" font-size="14" font-weight="600">${esc(allCleanLabel)}</text>`,
|
|
7188
7675
|
"</svg>"
|
|
7189
7676
|
].join("\n");
|
|
7190
7677
|
}
|
|
@@ -7299,7 +7786,9 @@ function scoreTrendChart(history) {
|
|
|
7299
7786
|
"</svg>"
|
|
7300
7787
|
].join("\n");
|
|
7301
7788
|
}
|
|
7302
|
-
function generateHtmlReport(scanResults, history) {
|
|
7789
|
+
function generateHtmlReport(scanResults, history, lang) {
|
|
7790
|
+
const t = getI18n(lang ?? "zh");
|
|
7791
|
+
const htmlLang = (lang ?? "zh") === "zh" ? "zh-CN" : "en";
|
|
7303
7792
|
const { summary, modules, accountId, region, scanStart, scanEnd } = scanResults;
|
|
7304
7793
|
const date = scanStart.split("T")[0];
|
|
7305
7794
|
const duration = formatDuration2(scanStart, scanEnd);
|
|
@@ -7317,23 +7806,23 @@ function generateHtmlReport(scanResults, history) {
|
|
|
7317
7806
|
<div class="top5-content">
|
|
7318
7807
|
<span class="badge badge-${esc(f.severity.toLowerCase())}">${esc(f.severity)}</span>
|
|
7319
7808
|
<div class="top5-title">${esc(f.title)}</div>
|
|
7320
|
-
<div class="top5-detail"><strong
|
|
7321
|
-
<div class="top5-detail"><strong
|
|
7322
|
-
<div class="top5-detail"><strong
|
|
7323
|
-
<h4
|
|
7324
|
-
<ol class="top5-remediation">${f.remediationSteps.map((s) => `<li>${
|
|
7809
|
+
<div class="top5-detail"><strong>${t.resource}:</strong> ${esc(f.resourceId)}</div>
|
|
7810
|
+
<div class="top5-detail"><strong>${t.impact}:</strong> ${esc(f.impact)}</div>
|
|
7811
|
+
<div class="top5-detail"><strong>${t.riskScore}:</strong> ${f.riskScore}/10</div>
|
|
7812
|
+
<h4>${t.remediation}</h4>
|
|
7813
|
+
<ol class="top5-remediation">${f.remediationSteps.map((s) => `<li>${escWithLinks(s)}</li>`).join("")}</ol>
|
|
7325
7814
|
</div>
|
|
7326
7815
|
</div>`
|
|
7327
7816
|
).join("\n");
|
|
7328
7817
|
top5Html = `
|
|
7329
7818
|
<section>
|
|
7330
|
-
<h2
|
|
7819
|
+
<h2>${esc(t.topHighestRiskFindings(top5.length))}</h2>
|
|
7331
7820
|
${cards}
|
|
7332
7821
|
</section>`;
|
|
7333
7822
|
}
|
|
7334
7823
|
let findingsHtml;
|
|
7335
7824
|
if (summary.totalFindings === 0) {
|
|
7336
|
-
findingsHtml =
|
|
7825
|
+
findingsHtml = `<div class="no-findings">${esc(t.noIssuesFound)}</div>`;
|
|
7337
7826
|
} else {
|
|
7338
7827
|
const FOLD_THRESHOLD = 20;
|
|
7339
7828
|
const renderCard = (f) => {
|
|
@@ -7342,10 +7831,10 @@ function generateHtmlReport(scanResults, history) {
|
|
|
7342
7831
|
<span class="badge badge-${esc(sev)}">${esc(f.severity)}</span>
|
|
7343
7832
|
<span class="finding-title-text">${esc(f.title)}</span>
|
|
7344
7833
|
<span class="finding-resource">${esc(f.resourceArn || f.resourceId)}</span>
|
|
7345
|
-
<details><summary
|
|
7834
|
+
<details><summary>${t.details}</summary><div class="finding-card-body">
|
|
7346
7835
|
<p>${esc(f.description)}</p>
|
|
7347
|
-
<p><strong
|
|
7348
|
-
<ol>${f.remediationSteps.map((s) => `<li>${
|
|
7836
|
+
<p><strong>${t.remediation}:</strong></p>
|
|
7837
|
+
<ol>${f.remediationSteps.map((s) => `<li>${escWithLinks(s)}</li>`).join("")}</ol>
|
|
7349
7838
|
</div></details>
|
|
7350
7839
|
</div>`;
|
|
7351
7840
|
};
|
|
@@ -7356,7 +7845,7 @@ function generateHtmlReport(scanResults, history) {
|
|
|
7356
7845
|
const first = findings.slice(0, FOLD_THRESHOLD).map(renderCard).join("\n");
|
|
7357
7846
|
const rest = findings.slice(FOLD_THRESHOLD).map(renderCard).join("\n");
|
|
7358
7847
|
return `${first}
|
|
7359
|
-
<details><summary
|
|
7848
|
+
<details><summary>${t.showRemainingFindings(findings.length - FOLD_THRESHOLD)}</summary>
|
|
7360
7849
|
${rest}
|
|
7361
7850
|
</details>`;
|
|
7362
7851
|
};
|
|
@@ -7408,13 +7897,13 @@ ${rest}
|
|
|
7408
7897
|
if (history && history.length >= 2) {
|
|
7409
7898
|
trendHtml = `
|
|
7410
7899
|
<section class="trend-section">
|
|
7411
|
-
<h2
|
|
7900
|
+
<h2>${esc(t.trendTitle)}</h2>
|
|
7412
7901
|
<div class="trend-chart">
|
|
7413
|
-
<div class="trend-title"
|
|
7902
|
+
<div class="trend-title">${esc(t.findingsBySeverity)}</div>
|
|
7414
7903
|
${findingsTrendChart(history)}
|
|
7415
7904
|
</div>
|
|
7416
7905
|
<div class="trend-chart">
|
|
7417
|
-
<div class="trend-title"
|
|
7906
|
+
<div class="trend-title">${esc(t.securityScore)}</div>
|
|
7418
7907
|
${scoreTrendChart(history)}
|
|
7419
7908
|
</div>
|
|
7420
7909
|
</section>`;
|
|
@@ -7425,16 +7914,57 @@ ${rest}
|
|
|
7425
7914
|
let recsHtml = "";
|
|
7426
7915
|
if (summary.totalFindings > 0) {
|
|
7427
7916
|
const recMap = /* @__PURE__ */ new Map();
|
|
7917
|
+
const kbPatches = [];
|
|
7918
|
+
let kbSeverity = "LOW";
|
|
7919
|
+
let kbUrl;
|
|
7920
|
+
const genericPatterns = ["See References", "None Provided", "Review the finding", "Review and remediate."];
|
|
7428
7921
|
for (const f of allFindings) {
|
|
7429
7922
|
const rem = f.remediationSteps[0] ?? "Review and remediate.";
|
|
7923
|
+
const url = f.remediationSteps.find((s) => s.startsWith("Documentation:"))?.replace("Documentation: ", "");
|
|
7924
|
+
if (genericPatterns.some((p) => rem.startsWith(p))) continue;
|
|
7925
|
+
const kbMatch = f.title.match(/KB\d+/);
|
|
7926
|
+
if (kbMatch && (f.module === "security_hub_findings" || f.module === "inspector_findings")) {
|
|
7927
|
+
kbPatches.push(kbMatch[0]);
|
|
7928
|
+
if (SEVERITY_ORDER2.indexOf(f.severity) < SEVERITY_ORDER2.indexOf(kbSeverity)) kbSeverity = f.severity;
|
|
7929
|
+
if (!kbUrl && url) kbUrl = url;
|
|
7930
|
+
continue;
|
|
7931
|
+
}
|
|
7932
|
+
if (f.module === "security_hub_findings") {
|
|
7933
|
+
const controlMatch = f.title.match(/^([A-Z][A-Za-z0-9]*\.\d+)\s/);
|
|
7934
|
+
if (controlMatch) {
|
|
7935
|
+
const controlId = controlMatch[1];
|
|
7936
|
+
const key = `ctrl:${controlId}`;
|
|
7937
|
+
const existing2 = recMap.get(key);
|
|
7938
|
+
if (existing2) {
|
|
7939
|
+
existing2.count++;
|
|
7940
|
+
if (!existing2.url && url) existing2.url = url;
|
|
7941
|
+
if (SEVERITY_ORDER2.indexOf(f.severity) < SEVERITY_ORDER2.indexOf(existing2.severity)) existing2.severity = f.severity;
|
|
7942
|
+
} else {
|
|
7943
|
+
recMap.set(key, { text: `[${controlId}] ${rem}`, severity: f.severity, count: 1, url });
|
|
7944
|
+
}
|
|
7945
|
+
continue;
|
|
7946
|
+
}
|
|
7947
|
+
}
|
|
7430
7948
|
const existing = recMap.get(rem);
|
|
7431
7949
|
if (existing) {
|
|
7432
7950
|
existing.count++;
|
|
7951
|
+
if (!existing.url && url) existing.url = url;
|
|
7433
7952
|
if (SEVERITY_ORDER2.indexOf(f.severity) < SEVERITY_ORDER2.indexOf(existing.severity)) {
|
|
7434
7953
|
existing.severity = f.severity;
|
|
7435
7954
|
}
|
|
7436
7955
|
} else {
|
|
7437
|
-
recMap.set(rem, { text: rem, severity: f.severity, count: 1 });
|
|
7956
|
+
recMap.set(rem, { text: rem, severity: f.severity, count: 1, url });
|
|
7957
|
+
}
|
|
7958
|
+
}
|
|
7959
|
+
if (kbPatches.length > 0) {
|
|
7960
|
+
const unique = [...new Set(kbPatches)];
|
|
7961
|
+
const kbList = unique.slice(0, 5).join(", ") + (unique.length > 5 ? ", \u2026" : "");
|
|
7962
|
+
recMap.set("__kb__", { text: t.installWindowsPatches(unique.length, kbList), severity: kbSeverity, count: 1, url: kbUrl });
|
|
7963
|
+
}
|
|
7964
|
+
for (const [key, rec] of recMap) {
|
|
7965
|
+
if (key.startsWith("ctrl:") && rec.count > 1) {
|
|
7966
|
+
rec.text += ` \u2014 ${t.affectedResources(rec.count)}`;
|
|
7967
|
+
rec.count = 1;
|
|
7438
7968
|
}
|
|
7439
7969
|
}
|
|
7440
7970
|
const uniqueRecs = [...recMap.values()].sort((a, b) => {
|
|
@@ -7445,60 +7975,61 @@ ${rest}
|
|
|
7445
7975
|
const renderRec = (r) => {
|
|
7446
7976
|
const sev = r.severity.toLowerCase();
|
|
7447
7977
|
const countLabel = r.count > 1 ? ` (× ${r.count})` : "";
|
|
7448
|
-
|
|
7978
|
+
const linkHtml = r.url ? ` <a href="${esc(r.url)}" style="color:#60a5fa" target="_blank" rel="noopener">📖</a>` : "";
|
|
7979
|
+
return `<li><span class="badge badge-${esc(sev)}">${esc(r.severity)}</span> ${esc(r.text)}${countLabel}${linkHtml}</li>`;
|
|
7449
7980
|
};
|
|
7450
7981
|
const TOP_N = 10;
|
|
7451
7982
|
const topItems = uniqueRecs.slice(0, TOP_N).map(renderRec).join("\n");
|
|
7452
7983
|
const remaining = uniqueRecs.slice(TOP_N);
|
|
7453
7984
|
const moreHtml = remaining.length > 0 ? `
|
|
7454
|
-
<details><summary
|
|
7985
|
+
<details><summary>${t.showMoreCount(remaining.length)}</summary>
|
|
7455
7986
|
${remaining.map(renderRec).join("\n")}
|
|
7456
7987
|
</details>` : "";
|
|
7457
7988
|
recsHtml = `
|
|
7458
7989
|
<details class="rec-fold">
|
|
7459
|
-
<summary><h2 style="margin:0;border:0;display:inline"
|
|
7990
|
+
<summary><h2 style="margin:0;border:0;display:inline">${esc(t.recommendations)} (${uniqueRecs.length} ${esc(t.unique)})</h2></summary>
|
|
7460
7991
|
<div class="rec-body">
|
|
7461
7992
|
<ol>${topItems}${moreHtml}</ol>
|
|
7462
7993
|
</div>
|
|
7463
7994
|
</details>`;
|
|
7464
7995
|
}
|
|
7465
7996
|
return `<!DOCTYPE html>
|
|
7466
|
-
<html lang="
|
|
7997
|
+
<html lang="${htmlLang}">
|
|
7467
7998
|
<head>
|
|
7468
7999
|
<meta charset="UTF-8">
|
|
7469
8000
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
7470
|
-
<title
|
|
8001
|
+
<title>${esc(t.securityReportTitle)} — ${esc(date)}</title>
|
|
7471
8002
|
<style>${sharedCss()}</style>
|
|
7472
8003
|
</head>
|
|
7473
8004
|
<body>
|
|
7474
8005
|
<div class="container">
|
|
7475
8006
|
|
|
7476
8007
|
<header>
|
|
7477
|
-
<h1>🛡️
|
|
7478
|
-
<div class="meta"
|
|
8008
|
+
<h1>🛡️ ${esc(t.securityReportTitle)}</h1>
|
|
8009
|
+
<div class="meta">${esc(t.account)}: ${esc(accountId)} | ${esc(t.region)}: ${esc(region)} | ${esc(date)} | ${esc(t.duration)}: ${esc(duration)}</div>
|
|
7479
8010
|
</header>
|
|
7480
8011
|
|
|
7481
8012
|
<section class="summary">
|
|
7482
8013
|
<div class="score-card">
|
|
7483
8014
|
<div class="score-value" style="color:${scoreColor(score)}">${score}</div>
|
|
7484
|
-
<div class="score-label"
|
|
8015
|
+
<div class="score-label">${esc(t.securityScore)}</div>
|
|
7485
8016
|
</div>
|
|
7486
8017
|
<div class="severity-stats">
|
|
7487
|
-
<div class="stat-card stat-critical"><div class="stat-count">${summary.critical}</div><div class="stat-label"
|
|
7488
|
-
<div class="stat-card stat-high"><div class="stat-count">${summary.high}</div><div class="stat-label"
|
|
7489
|
-
<div class="stat-card stat-medium"><div class="stat-count">${summary.medium}</div><div class="stat-label"
|
|
7490
|
-
<div class="stat-card stat-low"><div class="stat-count">${summary.low}</div><div class="stat-label"
|
|
8018
|
+
<div class="stat-card stat-critical"><div class="stat-count">${summary.critical}</div><div class="stat-label">${esc(t.critical)}</div></div>
|
|
8019
|
+
<div class="stat-card stat-high"><div class="stat-count">${summary.high}</div><div class="stat-label">${esc(t.high)}</div></div>
|
|
8020
|
+
<div class="stat-card stat-medium"><div class="stat-count">${summary.medium}</div><div class="stat-label">${esc(t.medium)}</div></div>
|
|
8021
|
+
<div class="stat-card stat-low"><div class="stat-count">${summary.low}</div><div class="stat-label">${esc(t.low)}</div></div>
|
|
7491
8022
|
</div>
|
|
7492
8023
|
</section>
|
|
7493
8024
|
|
|
7494
8025
|
<section class="charts">
|
|
7495
8026
|
<div class="chart-box">
|
|
7496
|
-
<div class="chart-title"
|
|
8027
|
+
<div class="chart-title">${esc(t.severityDistribution)}</div>
|
|
7497
8028
|
<div style="text-align:center">${donutChart(summary)}</div>
|
|
7498
8029
|
</div>
|
|
7499
8030
|
<div class="chart-box">
|
|
7500
|
-
<div class="chart-title"
|
|
7501
|
-
${barChart(modules)}
|
|
8031
|
+
<div class="chart-title">${esc(t.findingsByModule)}</div>
|
|
8032
|
+
${barChart(modules, t.allModulesClean)}
|
|
7502
8033
|
</div>
|
|
7503
8034
|
</section>
|
|
7504
8035
|
|
|
@@ -7506,33 +8037,35 @@ ${trendHtml}
|
|
|
7506
8037
|
|
|
7507
8038
|
${top5Html}
|
|
7508
8039
|
|
|
7509
|
-
${buildServiceReminderHtml(modules)}
|
|
8040
|
+
${buildServiceReminderHtml(modules, lang)}
|
|
7510
8041
|
|
|
7511
8042
|
<section>
|
|
7512
|
-
<h2
|
|
8043
|
+
<h2>${esc(t.scanStatistics)}</h2>
|
|
7513
8044
|
<table>
|
|
7514
|
-
<thead><tr><th
|
|
8045
|
+
<thead><tr><th>${esc(t.module)}</th><th>${esc(t.resources)}</th><th>${esc(t.findings)}</th><th>${esc(t.status)}</th></tr></thead>
|
|
7515
8046
|
<tbody>${statsRows}</tbody>
|
|
7516
8047
|
</table>
|
|
7517
8048
|
</section>
|
|
7518
8049
|
|
|
7519
8050
|
<section>
|
|
7520
|
-
<h2
|
|
8051
|
+
<h2>${esc(t.allFindings)}</h2>
|
|
7521
8052
|
${findingsHtml}
|
|
7522
8053
|
</section>
|
|
7523
8054
|
|
|
7524
8055
|
${recsHtml}
|
|
7525
8056
|
|
|
7526
8057
|
<footer>
|
|
7527
|
-
<p
|
|
7528
|
-
<p
|
|
8058
|
+
<p>${esc(t.generatedBy)} v${VERSION}</p>
|
|
8059
|
+
<p>${esc(t.informationalOnly)}</p>
|
|
7529
8060
|
</footer>
|
|
7530
8061
|
|
|
7531
8062
|
</div>
|
|
7532
8063
|
</body>
|
|
7533
8064
|
</html>`;
|
|
7534
8065
|
}
|
|
7535
|
-
function generateMlps3HtmlReport(scanResults, history) {
|
|
8066
|
+
function generateMlps3HtmlReport(scanResults, history, lang) {
|
|
8067
|
+
const t = getI18n(lang ?? "zh");
|
|
8068
|
+
const htmlLang = (lang ?? "zh") === "zh" ? "zh-CN" : "en";
|
|
7536
8069
|
const { accountId, region, scanStart } = scanResults;
|
|
7537
8070
|
const date = scanStart.split("T")[0];
|
|
7538
8071
|
const scanTime = scanStart.replace("T", " ").replace(/\.\d+Z$/, " UTC");
|
|
@@ -7549,17 +8082,21 @@ function generateMlps3HtmlReport(scanResults, history) {
|
|
|
7549
8082
|
if (history && history.length >= 2) {
|
|
7550
8083
|
trendHtml = `
|
|
7551
8084
|
<section class="trend-section">
|
|
7552
|
-
<h2
|
|
8085
|
+
<h2>${esc(t.trendTitle)}</h2>
|
|
7553
8086
|
<div class="trend-chart">
|
|
7554
|
-
<div class="trend-title"
|
|
8087
|
+
<div class="trend-title">${esc(t.findingsBySeverity)}</div>
|
|
7555
8088
|
${findingsTrendChart(history)}
|
|
7556
8089
|
</div>
|
|
7557
8090
|
<div class="trend-chart">
|
|
7558
|
-
<div class="trend-title"
|
|
8091
|
+
<div class="trend-title">${esc(t.securityScore)}</div>
|
|
7559
8092
|
${scoreTrendChart(history)}
|
|
7560
8093
|
</div>
|
|
7561
8094
|
</section>`;
|
|
7562
8095
|
}
|
|
8096
|
+
const isEn = (lang ?? "zh") === "en";
|
|
8097
|
+
const itemCat = (r) => isEn ? r.item.categoryEn : r.item.categoryCn;
|
|
8098
|
+
const itemControl = (r) => isEn ? r.item.controlEn : r.item.controlCn;
|
|
8099
|
+
const itemReq = (r) => isEn ? r.item.requirementEn : r.item.requirementCn;
|
|
7563
8100
|
const categoryMap = /* @__PURE__ */ new Map();
|
|
7564
8101
|
for (const r of results) {
|
|
7565
8102
|
if (r.status === "not_applicable") continue;
|
|
@@ -7568,7 +8105,7 @@ function generateMlps3HtmlReport(scanResults, history) {
|
|
|
7568
8105
|
categoryMap.get(cat).push(r);
|
|
7569
8106
|
}
|
|
7570
8107
|
const categorySections = MLPS3_CATEGORY_ORDER.map((category) => {
|
|
7571
|
-
const sectionTitle =
|
|
8108
|
+
const sectionTitle = t.mlpsCategorySection[category] ?? category;
|
|
7572
8109
|
const catResults = categoryMap.get(category);
|
|
7573
8110
|
if (!catResults || catResults.length === 0) return "";
|
|
7574
8111
|
const allCloud = catResults.every((r) => r.status === "cloud_provider");
|
|
@@ -7576,11 +8113,11 @@ function generateMlps3HtmlReport(scanResults, history) {
|
|
|
7576
8113
|
return `<details class="category-fold mlps-cloud-section">
|
|
7577
8114
|
<summary>
|
|
7578
8115
|
<span class="category-title">${esc(sectionTitle)}</span>
|
|
7579
|
-
<span class="category-stats"><span class="category-stat-cloud">\u{1F3E2} ${catResults.length}
|
|
8116
|
+
<span class="category-stats"><span class="category-stat-cloud">\u{1F3E2} ${catResults.length} ${esc(t.cloudProvider)}</span></span>
|
|
7580
8117
|
</summary>
|
|
7581
8118
|
<div class="category-body">
|
|
7582
|
-
<div class="mlps-cloud-note"
|
|
7583
|
-
${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
|
|
8119
|
+
<div class="mlps-cloud-note">${esc(t.cloudItemsNote(catResults.length))}</div>
|
|
8120
|
+
${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")}
|
|
7584
8121
|
</div>
|
|
7585
8122
|
</details>`;
|
|
7586
8123
|
}
|
|
@@ -7602,31 +8139,34 @@ function generateMlps3HtmlReport(scanResults, history) {
|
|
|
7602
8139
|
if (!controlMap.has(key)) controlMap.set(key, []);
|
|
7603
8140
|
controlMap.get(key).push(r);
|
|
7604
8141
|
}
|
|
7605
|
-
const controlGroups = [...controlMap.entries()].map(([
|
|
8142
|
+
const controlGroups = [...controlMap.entries()].map(([_controlKey, controlResults]) => {
|
|
8143
|
+
const controlName = itemControl(controlResults[0]);
|
|
7606
8144
|
const cloudItems = controlResults.filter((r) => r.status === "cloud_provider");
|
|
7607
8145
|
const nonCloudItems = controlResults.filter((r) => r.status !== "cloud_provider");
|
|
7608
8146
|
let itemsHtml = "";
|
|
7609
8147
|
for (const r of nonCloudItems) {
|
|
7610
8148
|
const icon = r.status === "clean" ? "\u{1F7E2}" : r.status === "issues" ? "\u{1F534}" : r.status === "unknown" ? "\u2B1C" : r.status === "manual" ? "\u{1F4CB}" : "\u{1F3E2}";
|
|
7611
8149
|
const cls = `check-${r.status === "cloud_provider" ? "cloud" : r.status}`;
|
|
7612
|
-
const suffix = r.status === "unknown" ?
|
|
8150
|
+
const suffix = r.status === "unknown" ? ` \u2014 ${esc(t.notChecked)}` : r.status === "manual" ? ` \u2014 ${esc(r.mapping.guidance ?? t.manualReview)}` : "";
|
|
7613
8151
|
let findingsDetail = "";
|
|
7614
8152
|
if (r.status === "clean") {
|
|
7615
|
-
findingsDetail = `<div class="check-detail"
|
|
8153
|
+
findingsDetail = `<div class="check-detail">${esc(t.noRelatedIssues)}</div>`;
|
|
7616
8154
|
} else if (r.status === "issues" && r.relatedFindings.length > 0) {
|
|
7617
8155
|
const fItems = r.relatedFindings.slice(0, 5).map((f) => `<li>${esc(f.severity)}: ${esc(f.title)}</li>`);
|
|
7618
8156
|
if (r.relatedFindings.length > 5) {
|
|
7619
|
-
fItems.push(`<li
|
|
8157
|
+
fItems.push(`<li>${esc(t.andMore(r.relatedFindings.length - 5))}</li>`);
|
|
7620
8158
|
}
|
|
7621
|
-
const remediationHint = r.relatedFindings[0]?.remediationSteps?.[0] ? `<p style="color:#fbbf24;font-size:12px;margin-top:4px"
|
|
7622
|
-
findingsDetail = `<div class="check-findings-wrap"><details><summary
|
|
8159
|
+
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>` : "";
|
|
8160
|
+
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>`;
|
|
7623
8161
|
}
|
|
7624
|
-
|
|
8162
|
+
const reqText = itemReq(r);
|
|
8163
|
+
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>
|
|
7625
8164
|
${findingsDetail}`;
|
|
7626
8165
|
}
|
|
7627
8166
|
if (cloudItems.length > 0) {
|
|
7628
8167
|
for (const r of cloudItems) {
|
|
7629
|
-
|
|
8168
|
+
const reqText = itemReq(r);
|
|
8169
|
+
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>
|
|
7630
8170
|
`;
|
|
7631
8171
|
}
|
|
7632
8172
|
}
|
|
@@ -7659,20 +8199,61 @@ ${itemsHtml}
|
|
|
7659
8199
|
let remediationHtml = "";
|
|
7660
8200
|
if (failedResults.length > 0) {
|
|
7661
8201
|
const mlpsRecMap = /* @__PURE__ */ new Map();
|
|
8202
|
+
const mlpsKbPatches = [];
|
|
8203
|
+
let mlpsKbSeverity = "LOW";
|
|
8204
|
+
let mlpsKbUrl;
|
|
8205
|
+
const mlpsGenericPatterns = ["See References", "None Provided", "Review the finding", "Review and remediate."];
|
|
7662
8206
|
for (const r of failedResults) {
|
|
7663
8207
|
for (const f of r.relatedFindings) {
|
|
7664
8208
|
const rem = f.remediationSteps[0] ?? "Review and remediate.";
|
|
8209
|
+
const url = f.remediationSteps.find((s) => s.startsWith("Documentation:"))?.replace("Documentation: ", "");
|
|
8210
|
+
if (mlpsGenericPatterns.some((p) => rem.startsWith(p))) continue;
|
|
8211
|
+
const kbMatch = f.title.match(/KB\d+/);
|
|
8212
|
+
if (kbMatch && (f.module === "security_hub_findings" || f.module === "inspector_findings")) {
|
|
8213
|
+
mlpsKbPatches.push(kbMatch[0]);
|
|
8214
|
+
if (SEVERITY_ORDER2.indexOf(f.severity) < SEVERITY_ORDER2.indexOf(mlpsKbSeverity)) mlpsKbSeverity = f.severity;
|
|
8215
|
+
if (!mlpsKbUrl && url) mlpsKbUrl = url;
|
|
8216
|
+
continue;
|
|
8217
|
+
}
|
|
8218
|
+
if (f.module === "security_hub_findings") {
|
|
8219
|
+
const controlMatch = f.title.match(/^([A-Z][A-Za-z0-9]*\.\d+)\s/);
|
|
8220
|
+
if (controlMatch) {
|
|
8221
|
+
const controlId = controlMatch[1];
|
|
8222
|
+
const key = `ctrl:${controlId}`;
|
|
8223
|
+
const existing2 = mlpsRecMap.get(key);
|
|
8224
|
+
if (existing2) {
|
|
8225
|
+
existing2.count++;
|
|
8226
|
+
if (!existing2.url && url) existing2.url = url;
|
|
8227
|
+
if (SEVERITY_ORDER2.indexOf(f.severity) < SEVERITY_ORDER2.indexOf(existing2.severity)) existing2.severity = f.severity;
|
|
8228
|
+
} else {
|
|
8229
|
+
mlpsRecMap.set(key, { text: `[${controlId}] ${rem}`, severity: f.severity, count: 1, url });
|
|
8230
|
+
}
|
|
8231
|
+
continue;
|
|
8232
|
+
}
|
|
8233
|
+
}
|
|
7665
8234
|
const existing = mlpsRecMap.get(rem);
|
|
7666
8235
|
if (existing) {
|
|
7667
8236
|
existing.count++;
|
|
8237
|
+
if (!existing.url && url) existing.url = url;
|
|
7668
8238
|
if (SEVERITY_ORDER2.indexOf(f.severity) < SEVERITY_ORDER2.indexOf(existing.severity)) {
|
|
7669
8239
|
existing.severity = f.severity;
|
|
7670
8240
|
}
|
|
7671
8241
|
} else {
|
|
7672
|
-
mlpsRecMap.set(rem, { text: rem, severity: f.severity, count: 1 });
|
|
8242
|
+
mlpsRecMap.set(rem, { text: rem, severity: f.severity, count: 1, url });
|
|
7673
8243
|
}
|
|
7674
8244
|
}
|
|
7675
8245
|
}
|
|
8246
|
+
if (mlpsKbPatches.length > 0) {
|
|
8247
|
+
const unique = [...new Set(mlpsKbPatches)];
|
|
8248
|
+
const kbList = unique.slice(0, 5).join(", ") + (unique.length > 5 ? ", \u2026" : "");
|
|
8249
|
+
mlpsRecMap.set("__kb__", { text: t.installWindowsPatches(unique.length, kbList), severity: mlpsKbSeverity, count: 1, url: mlpsKbUrl });
|
|
8250
|
+
}
|
|
8251
|
+
for (const [key, rec] of mlpsRecMap) {
|
|
8252
|
+
if (key.startsWith("ctrl:") && rec.count > 1) {
|
|
8253
|
+
rec.text += ` \u2014 ${t.affectedResources(rec.count)}`;
|
|
8254
|
+
rec.count = 1;
|
|
8255
|
+
}
|
|
8256
|
+
}
|
|
7676
8257
|
const mlpsUniqueRecs = [...mlpsRecMap.values()].sort((a, b) => {
|
|
7677
8258
|
const sevDiff = SEVERITY_ORDER2.indexOf(a.severity) - SEVERITY_ORDER2.indexOf(b.severity);
|
|
7678
8259
|
if (sevDiff !== 0) return sevDiff;
|
|
@@ -7682,26 +8263,27 @@ ${itemsHtml}
|
|
|
7682
8263
|
const renderMlpsRec = (r) => {
|
|
7683
8264
|
const sev = r.severity.toLowerCase();
|
|
7684
8265
|
const countLabel = r.count > 1 ? ` (× ${r.count})` : "";
|
|
7685
|
-
|
|
8266
|
+
const linkHtml = r.url ? ` <a href="${esc(r.url)}" style="color:#60a5fa" target="_blank" rel="noopener">📖</a>` : "";
|
|
8267
|
+
return `<li><span class="badge badge-${esc(sev)}">${esc(r.severity)}</span> ${esc(r.text)}${countLabel}${linkHtml}</li>`;
|
|
7686
8268
|
};
|
|
7687
8269
|
const MLPS_TOP_N = 10;
|
|
7688
8270
|
const mlpsTopItems = mlpsUniqueRecs.slice(0, MLPS_TOP_N).map(renderMlpsRec).join("\n");
|
|
7689
8271
|
const mlpsRemaining = mlpsUniqueRecs.slice(MLPS_TOP_N);
|
|
7690
8272
|
const mlpsMoreHtml = mlpsRemaining.length > 0 ? `
|
|
7691
|
-
<details><summary
|
|
8273
|
+
<details><summary>${esc(t.showRemaining(mlpsRemaining.length))}…</summary>
|
|
7692
8274
|
${mlpsRemaining.map(renderMlpsRec).join("\n")}
|
|
7693
8275
|
</details>` : "";
|
|
7694
8276
|
remediationHtml = `
|
|
7695
8277
|
<details class="rec-fold" open>
|
|
7696
|
-
<summary><h2 style="margin:0;border:0;display:inline"
|
|
8278
|
+
<summary><h2 style="margin:0;border:0;display:inline">${esc(t.remediationItems(mlpsUniqueRecs.length))}</h2></summary>
|
|
7697
8279
|
<div class="rec-body">
|
|
7698
8280
|
<ol>${mlpsTopItems}${mlpsMoreHtml}</ol>
|
|
7699
8281
|
</div>
|
|
7700
8282
|
</details>`;
|
|
7701
8283
|
}
|
|
7702
8284
|
}
|
|
7703
|
-
const naNote = naCount > 0 ? `<p style="color:#64748b;font-size:13px;margin-top:24px"
|
|
7704
|
-
const unknownNote = autoUnknown > 0 ? `<div style="color:#94a3b8;font-size:12px;margin-top:8px"
|
|
8285
|
+
const naNote = naCount > 0 ? `<p style="color:#64748b;font-size:13px;margin-top:24px">${esc(t.naNote(naCount))}</p>` : "";
|
|
8286
|
+
const unknownNote = autoUnknown > 0 ? `<div style="color:#94a3b8;font-size:12px;margin-top:8px">${esc(t.unknownNote(autoUnknown))}</div>` : "";
|
|
7705
8287
|
const mlpsCss = `
|
|
7706
8288
|
.mlps-cloud-section>summary{color:#94a3b8}
|
|
7707
8289
|
.mlps-cloud-note{color:#94a3b8;font-size:13px;margin-bottom:12px;font-style:italic}
|
|
@@ -7723,40 +8305,40 @@ ${mlpsRemaining.map(renderMlpsRec).join("\n")}
|
|
|
7723
8305
|
.mlps-summary-card .stat-label{font-size:12px;color:#94a3b8;margin-top:2px}
|
|
7724
8306
|
`;
|
|
7725
8307
|
return `<!DOCTYPE html>
|
|
7726
|
-
<html lang="
|
|
8308
|
+
<html lang="${htmlLang}">
|
|
7727
8309
|
<head>
|
|
7728
8310
|
<meta charset="UTF-8">
|
|
7729
8311
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
7730
|
-
<title
|
|
8312
|
+
<title>${esc(t.mlpsTitle)} — ${esc(date)}</title>
|
|
7731
8313
|
<style>${sharedCss()}${mlpsCss}</style>
|
|
7732
8314
|
</head>
|
|
7733
8315
|
<body>
|
|
7734
8316
|
<div class="container">
|
|
7735
8317
|
|
|
7736
8318
|
<header>
|
|
7737
|
-
<h1>🛡️
|
|
7738
|
-
<div class="disclaimer"
|
|
7739
|
-
<div class="meta"
|
|
8319
|
+
<h1>🛡️ ${esc(t.mlpsTitle)}</h1>
|
|
8320
|
+
<div class="disclaimer">${esc(t.mlpsDisclaimer)}</div>
|
|
8321
|
+
<div class="meta">${esc(t.account)}: ${esc(accountId)} | ${esc(t.region)}: ${esc(region)} | ${esc(t.scanTime)}: ${esc(scanTime)}</div>
|
|
7740
8322
|
</header>
|
|
7741
8323
|
|
|
7742
8324
|
<section class="summary" style="display:block;text-align:center">
|
|
7743
8325
|
<div style="font-size:36px;font-weight:700;margin-bottom:12px">
|
|
7744
|
-
<span style="color:#22c55e">${autoClean}</span> <span style="color:#94a3b8;font-size:18px"
|
|
8326
|
+
<span style="color:#22c55e">${autoClean}</span> <span style="color:#94a3b8;font-size:18px">${esc(t.noIssues)}</span>
|
|
7745
8327
|
<span style="color:#475569;margin:0 16px">/</span>
|
|
7746
|
-
<span style="color:#ef4444">${autoIssues}</span> <span style="color:#94a3b8;font-size:18px"
|
|
8328
|
+
<span style="color:#ef4444">${autoIssues}</span> <span style="color:#94a3b8;font-size:18px">${esc(t.issuesFound)}</span>
|
|
7747
8329
|
</div>
|
|
7748
8330
|
<div class="mlps-summary-cards" style="justify-content:center">
|
|
7749
|
-
<div class="mlps-summary-card"><div class="stat-count" style="color:#60a5fa">${checkedTotal}</div><div class="stat-label"
|
|
7750
|
-
<div class="mlps-summary-card"><div class="stat-count" style="color:#94a3b8">${cloudCount}</div><div class="stat-label">\u{1F3E2}
|
|
7751
|
-
<div class="mlps-summary-card"><div class="stat-count" style="color:#eab308">${manualCount}</div><div class="stat-label">\u{1F4CB}
|
|
7752
|
-
${naCount > 0 ? `<div class="mlps-summary-card"><div class="stat-count" style="color:#64748b">${naCount}</div><div class="stat-label">\u2796
|
|
8331
|
+
<div class="mlps-summary-card"><div class="stat-count" style="color:#60a5fa">${checkedTotal}</div><div class="stat-label">${esc(t.checkedItems)}</div></div>
|
|
8332
|
+
<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>
|
|
8333
|
+
<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>
|
|
8334
|
+
${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>` : ""}
|
|
7753
8335
|
</div>
|
|
7754
8336
|
</section>
|
|
7755
8337
|
${unknownNote}
|
|
7756
8338
|
|
|
7757
8339
|
${trendHtml}
|
|
7758
8340
|
|
|
7759
|
-
${buildServiceReminderHtml(scanResults.modules)}
|
|
8341
|
+
${buildServiceReminderHtml(scanResults.modules, lang)}
|
|
7760
8342
|
|
|
7761
8343
|
${categorySections}
|
|
7762
8344
|
|
|
@@ -7765,8 +8347,8 @@ ${remediationHtml}
|
|
|
7765
8347
|
${naNote}
|
|
7766
8348
|
|
|
7767
8349
|
<footer>
|
|
7768
|
-
<p
|
|
7769
|
-
<p
|
|
8350
|
+
<p>${esc(t.mlpsFooterGenerated(VERSION))}</p>
|
|
8351
|
+
<p>${esc(t.mlpsFooterDisclaimer)}</p>
|
|
7770
8352
|
</footer>
|
|
7771
8353
|
|
|
7772
8354
|
</div>
|
|
@@ -7981,16 +8563,14 @@ Aggregates active findings from AWS Security Hub. Replaces individual config sca
|
|
|
7981
8563
|
- INFORMATIONAL findings are skipped.
|
|
7982
8564
|
|
|
7983
8565
|
## 3. GuardDuty Findings (guardduty_findings)
|
|
7984
|
-
|
|
7985
|
-
-
|
|
7986
|
-
-
|
|
7987
|
-
- Only non-archived findings are included.
|
|
8566
|
+
Detection-only: checks if GuardDuty is enabled in the region.
|
|
8567
|
+
- GuardDuty findings are aggregated via Security Hub (security_hub_findings module).
|
|
8568
|
+
- Reports whether GuardDuty detectors are active.
|
|
7988
8569
|
|
|
7989
8570
|
## 4. Inspector Findings (inspector_findings)
|
|
7990
|
-
|
|
7991
|
-
-
|
|
7992
|
-
-
|
|
7993
|
-
- CVE IDs are included in finding titles when available.
|
|
8571
|
+
Detection-only: checks if Inspector is enabled in the region.
|
|
8572
|
+
- Inspector findings are aggregated via Security Hub (security_hub_findings module).
|
|
8573
|
+
- Reports whether Inspector scanning (EC2/Lambda) is active.
|
|
7994
8574
|
|
|
7995
8575
|
## 5. Trusted Advisor Findings (trusted_advisor_findings)
|
|
7996
8576
|
Aggregates security checks from AWS Trusted Advisor.
|
|
@@ -8026,19 +8606,15 @@ Finds unused/idle AWS resources (unattached EBS volumes, unused EIPs, stopped in
|
|
|
8026
8606
|
Assesses disaster recovery readiness \u2014 RDS Multi-AZ & backups, EBS snapshot coverage, S3 versioning & cross-region replication.
|
|
8027
8607
|
|
|
8028
8608
|
## 15. Config Rules Findings (config_rules_findings)
|
|
8029
|
-
|
|
8030
|
-
-
|
|
8031
|
-
-
|
|
8032
|
-
- Security-related rules (encryption, IAM, public access, etc.) mapped to HIGH severity (7.5).
|
|
8033
|
-
- Other non-compliant rules mapped to MEDIUM severity (5.5).
|
|
8609
|
+
Detection-only: checks if AWS Config Rules are configured.
|
|
8610
|
+
- Config Rule compliance findings are aggregated via Security Hub (security_hub_findings module).
|
|
8611
|
+
- Reports whether Config is enabled and counts active rules.
|
|
8034
8612
|
- Gracefully handles regions where AWS Config is not enabled.
|
|
8035
8613
|
|
|
8036
8614
|
## 16. IAM Access Analyzer Findings (access_analyzer_findings)
|
|
8037
|
-
|
|
8038
|
-
-
|
|
8039
|
-
-
|
|
8040
|
-
- Covers S3 buckets, IAM roles, SQS queues, Lambda functions, KMS keys, and more.
|
|
8041
|
-
- Severity mapped: CRITICAL \u2192 9.5, HIGH \u2192 8.0, MEDIUM \u2192 5.5, LOW \u2192 3.0.
|
|
8615
|
+
Detection-only: checks if IAM Access Analyzer is configured.
|
|
8616
|
+
- Access Analyzer findings are aggregated via Security Hub (security_hub_findings module).
|
|
8617
|
+
- Reports whether active analyzers exist.
|
|
8042
8618
|
- Returns warning if no analyzer is configured.
|
|
8043
8619
|
|
|
8044
8620
|
## 17. SSM Patch Compliance (patch_compliance_findings)
|
|
@@ -8123,112 +8699,18 @@ var MODULE_DESCRIPTIONS = {
|
|
|
8123
8699
|
idle_resources: "Finds unused/idle AWS resources (unattached EBS volumes, unused EIPs, stopped instances, unused security groups) that waste money and increase attack surface.",
|
|
8124
8700
|
disaster_recovery: "Assesses disaster recovery readiness \u2014 RDS Multi-AZ & backups, EBS snapshot coverage, S3 versioning & cross-region replication.",
|
|
8125
8701
|
security_hub_findings: "Aggregates active findings from AWS Security Hub \u2014 replaces individual config scanners with centralized compliance checks.",
|
|
8126
|
-
guardduty_findings: "
|
|
8127
|
-
inspector_findings: "
|
|
8702
|
+
guardduty_findings: "Checks if GuardDuty is enabled. Findings are aggregated via Security Hub.",
|
|
8703
|
+
inspector_findings: "Checks if Inspector is enabled. Findings are aggregated via Security Hub.",
|
|
8128
8704
|
trusted_advisor_findings: "Aggregates security checks from AWS Trusted Advisor \u2014 requires Business or Enterprise Support plan.",
|
|
8129
|
-
config_rules_findings: "
|
|
8130
|
-
access_analyzer_findings: "
|
|
8705
|
+
config_rules_findings: "Checks if AWS Config Rules are configured. Findings are aggregated via Security Hub.",
|
|
8706
|
+
access_analyzer_findings: "Checks if IAM Access Analyzer is configured. Findings are aggregated via Security Hub.",
|
|
8131
8707
|
patch_compliance_findings: "Checks SSM Patch Manager compliance \u2014 managed instances with missing or failed security and system patches.",
|
|
8132
8708
|
imdsv2_enforcement: "Checks if EC2 instances enforce IMDSv2 (HttpTokens: required) \u2014 IMDSv1 allows credential theft via SSRF.",
|
|
8133
8709
|
waf_coverage: "Checks if internet-facing ALBs have WAF Web ACL associated for protection against common web exploits."
|
|
8134
8710
|
};
|
|
8135
|
-
|
|
8136
|
-
|
|
8137
|
-
|
|
8138
|
-
\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
8139
|
-
|
|
8140
|
-
\u4EE5\u4E0B\u4E8B\u9879\u9700\u8981\u4EBA\u5DE5\u786E\u8BA4\u548C\u6267\u884C\uFF1A
|
|
8141
|
-
|
|
8142
|
-
\u26A0\uFE0F \u5E94\u6025\u9694\u79BB/\u6B62\u8840\u65B9\u6848
|
|
8143
|
-
\u25A1 \u51C6\u5907\u4E13\u7528\u9694\u79BB\u5B89\u5168\u7EC4\uFF08\u65E0 Inbound/Outbound \u89C4\u5219\uFF09
|
|
8144
|
-
\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
|
|
8145
|
-
\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
|
|
8146
|
-
\u25A1 \u660E\u786E\u5404\u9879\u76EE\u8D26\u6237\u53CA\u8D44\u6E90\u7684\u8D1F\u8D23\u4EBA\u4E0E\u8054\u7CFB\u65B9\u5F0F
|
|
8147
|
-
|
|
8148
|
-
\u26A0\uFE0F \u6D4B\u8BD5/\u5F00\u53D1\u73AF\u5883\u5904\u7F6E
|
|
8149
|
-
\u25A1 \u975E\u6838\u5FC3\u7CFB\u7EDF\u5728\u62A4\u7F51\u671F\u95F4\u5173\u95ED
|
|
8150
|
-
\u25A1 \u6D4B\u8BD5/\u5F00\u53D1\u73AF\u5883\u5173\u95ED\u6216\u4E0E\u751F\u4EA7\u4FDD\u6301\u540C\u7B49\u5B89\u5168\u57FA\u7EBF
|
|
8151
|
-
\u25A1 \u786E\u8BA4\u54EA\u4E9B\u73AF\u5883\u53EF\u4EE5\u7D27\u6025\u5173\u505C\uFF0C\u907F\u514D\u653B\u51FB\u6269\u6563
|
|
8152
|
-
|
|
8153
|
-
\u26A0\uFE0F \u503C\u5B88\u56E2\u961F\u7EC4\u5EFA
|
|
8154
|
-
\u25A1 7\xD724 \u76D1\u63A7\u5FEB\u901F\u54CD\u5E94\u56E2\u961F
|
|
8155
|
-
\u25A1 \u6280\u672F\u4E0E\u98CE\u9669\u5206\u6790\u7EC4
|
|
8156
|
-
\u25A1 \u5B89\u5168\u7B56\u7565\u4E0B\u53D1\u7EC4
|
|
8157
|
-
\u25A1 \u4E1A\u52A1\u54CD\u5E94\u7EC4
|
|
8158
|
-
\u25A1 \u660E\u786E AWS TAM/Support \u8054\u7CFB\u65B9\u5F0F\uFF08ES/EOP \u5BA2\u6237\uFF09
|
|
8159
|
-
|
|
8160
|
-
\u26A0\uFE0F \u51FA\u5165\u7AD9\u8DEF\u5F84\u67B6\u6784\u56FE
|
|
8161
|
-
\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
|
|
8162
|
-
\u25A1 \u660E\u786E\u5404 ELB/Public EC2/S3/DX \u7684\u6570\u636E\u6D41\u5411
|
|
8163
|
-
\u25A1 \u8BC6\u522B\u6240\u6709\u9762\u5411\u4E92\u8054\u7F51\u7684\u6570\u636E\u4EA4\u4E92\u63A5\u53E3
|
|
8164
|
-
|
|
8165
|
-
\u26A0\uFE0F \u4E3B\u52A8\u5F0F\u6E17\u900F\u6D4B\u8BD5
|
|
8166
|
-
\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
|
|
8167
|
-
\u25A1 \u57FA\u4E8E\u6E17\u900F\u6D4B\u8BD5\u62A5\u544A\u8FDB\u884C\u6B63\u5F0F\u62A4\u7F51\u524D\u7684\u5B89\u5168\u52A0\u56FA
|
|
8168
|
-
\u25A1 \u5173\u6CE8 AWS \u5B89\u5168\u516C\u544A\uFF08\u5DF2\u77E5\u6F0F\u6D1E\u4E0E\u8865\u4E01\uFF09
|
|
8169
|
-
|
|
8170
|
-
\u26A0\uFE0F WAR-ROOM \u5B9E\u65F6\u6C9F\u901A
|
|
8171
|
-
\u25A1 \u521B\u5EFA\u62A4\u7F51\u671F\u95F4\u4E13\u7528\u6C9F\u901A\u6E20\u9053\uFF08\u4F01\u5FAE/\u9489\u9489/\u98DE\u4E66/Chime\uFF09
|
|
8172
|
-
\u25A1 \u4E0E AWS TAM \u5EFA\u7ACB WAR-ROOM \u8054\u7CFB\uFF08\u4F01\u4E1A\u7EA7\u652F\u6301\u5BA2\u6237\uFF09
|
|
8173
|
-
\u25A1 \u7EDF\u4E00\u6848\u4F8B\u6807\u9898\u683C\u5F0F\uFF1A"\u3010\u62A4\u7F51\u3011+ \u95EE\u9898\u63CF\u8FF0"
|
|
8174
|
-
|
|
8175
|
-
\u26A0\uFE0F \u5BC6\u7801\u4E0E\u51ED\u8BC1\u7BA1\u7406
|
|
8176
|
-
\u25A1 \u6240\u6709 IAM \u7528\u6237\u7ED1\u5B9A MFA
|
|
8177
|
-
\u25A1 AKSK \u8F6E\u8F6C\u5468\u671F \u2264 90 \u5929
|
|
8178
|
-
\u25A1 \u907F\u514D\u5171\u4EAB\u8D26\u6237\u4F7F\u7528
|
|
8179
|
-
\u25A1 S3/Lambda/\u5E94\u7528\u4EE3\u7801\u4E2D\u65E0\u660E\u6587\u5BC6\u7801
|
|
8180
|
-
|
|
8181
|
-
\u26A0\uFE0F \u62A4\u7F51\u540E\u4F18\u5316
|
|
8182
|
-
\u25A1 \u9488\u5BF9\u653B\u51FB\u62A5\u544A\u9010\u9879\u5E94\u7B54\u4E0E\u4FEE\u590D
|
|
8183
|
-
\u25A1 \u4E0E\u5B89\u5168\u56E2\u961F\u5EFA\u7ACB\u5468\u671F\u6027\u5B89\u5168\u7EF4\u62A4\u6D41\u7A0B
|
|
8184
|
-
\u25A1 \u6301\u7EED\u8865\u5168\u5B89\u5168\u98CE\u9669
|
|
8185
|
-
|
|
8186
|
-
\u53C2\u8003\uFF1AAWS \u62A4\u7F51\u884C\u52A8 Standard Operation Procedure (Compliance IEM)
|
|
8187
|
-
`;
|
|
8188
|
-
var SERVICE_RECOMMENDATIONS2 = {
|
|
8189
|
-
security_hub_findings: {
|
|
8190
|
-
icon: "\u{1F534}",
|
|
8191
|
-
service: "Security Hub",
|
|
8192
|
-
impact: "\u65E0\u6CD5\u83B7\u53D6 300+ \u9879\u81EA\u52A8\u5316\u5B89\u5168\u68C0\u67E5\uFF08FSBP/CIS/PCI DSS \u6807\u51C6\uFF09",
|
|
8193
|
-
action: "\u542F\u7528 Security Hub \u83B7\u5F97\u6700\u5168\u9762\u7684\u5B89\u5168\u6001\u52BF\u8BC4\u4F30"
|
|
8194
|
-
},
|
|
8195
|
-
guardduty_findings: {
|
|
8196
|
-
icon: "\u{1F534}",
|
|
8197
|
-
service: "GuardDuty",
|
|
8198
|
-
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",
|
|
8199
|
-
action: "\u542F\u7528 GuardDuty \u83B7\u5F97\u6301\u7EED\u5A01\u80C1\u68C0\u6D4B\u80FD\u529B"
|
|
8200
|
-
},
|
|
8201
|
-
inspector_findings: {
|
|
8202
|
-
icon: "\u{1F7E1}",
|
|
8203
|
-
service: "Inspector",
|
|
8204
|
-
impact: "\u65E0\u6CD5\u626B\u63CF EC2/Lambda/\u5BB9\u5668\u7684\u8F6F\u4EF6\u6F0F\u6D1E\uFF08CVE\uFF09",
|
|
8205
|
-
action: "\u542F\u7528 Inspector \u53D1\u73B0\u5DF2\u77E5\u5B89\u5168\u6F0F\u6D1E"
|
|
8206
|
-
},
|
|
8207
|
-
trusted_advisor_findings: {
|
|
8208
|
-
icon: "\u{1F7E1}",
|
|
8209
|
-
service: "Trusted Advisor",
|
|
8210
|
-
impact: "\u65E0\u6CD5\u83B7\u53D6 AWS \u6700\u4F73\u5B9E\u8DF5\u5B89\u5168\u68C0\u67E5",
|
|
8211
|
-
action: "\u5347\u7EA7\u81F3 Business/Enterprise Support \u8BA1\u5212\u4EE5\u4F7F\u7528 Trusted Advisor \u5B89\u5168\u68C0\u67E5"
|
|
8212
|
-
},
|
|
8213
|
-
config_rules_findings: {
|
|
8214
|
-
icon: "\u{1F7E1}",
|
|
8215
|
-
service: "AWS Config",
|
|
8216
|
-
impact: "\u65E0\u6CD5\u68C0\u67E5\u8D44\u6E90\u914D\u7F6E\u5408\u89C4\u72B6\u6001",
|
|
8217
|
-
action: "\u542F\u7528 AWS Config \u5E76\u914D\u7F6E Config Rules"
|
|
8218
|
-
},
|
|
8219
|
-
access_analyzer_findings: {
|
|
8220
|
-
icon: "\u{1F7E1}",
|
|
8221
|
-
service: "IAM Access Analyzer",
|
|
8222
|
-
impact: "\u65E0\u6CD5\u68C0\u6D4B\u8D44\u6E90\u662F\u5426\u88AB\u5916\u90E8\u8D26\u53F7\u6216\u516C\u7F51\u8BBF\u95EE",
|
|
8223
|
-
action: "\u521B\u5EFA IAM Access Analyzer\uFF08\u8D26\u6237\u7EA7\u6216\u7EC4\u7EC7\u7EA7\uFF09"
|
|
8224
|
-
},
|
|
8225
|
-
patch_compliance_findings: {
|
|
8226
|
-
icon: "\u{1F7E1}",
|
|
8227
|
-
service: "SSM Patch Manager",
|
|
8228
|
-
impact: "\u65E0\u6CD5\u68C0\u67E5\u5B9E\u4F8B\u8865\u4E01\u5408\u89C4\u72B6\u6001",
|
|
8229
|
-
action: "\u5B89\u88C5 SSM Agent \u5E76\u914D\u7F6E Patch Manager"
|
|
8230
|
-
}
|
|
8231
|
-
};
|
|
8711
|
+
function getHwDefenseChecklist(lang) {
|
|
8712
|
+
return getI18n(lang ?? "zh").hwChecklist;
|
|
8713
|
+
}
|
|
8232
8714
|
var SERVICE_NOT_ENABLED_PATTERNS2 = [
|
|
8233
8715
|
"not enabled",
|
|
8234
8716
|
"not found",
|
|
@@ -8238,10 +8720,11 @@ var SERVICE_NOT_ENABLED_PATTERNS2 = [
|
|
|
8238
8720
|
"not available",
|
|
8239
8721
|
"is not enabled"
|
|
8240
8722
|
];
|
|
8241
|
-
function buildServiceReminder(modules) {
|
|
8723
|
+
function buildServiceReminder(modules, lang) {
|
|
8724
|
+
const t = getI18n(lang ?? "zh");
|
|
8242
8725
|
const disabledServices = [];
|
|
8243
8726
|
for (const mod of modules) {
|
|
8244
|
-
const rec =
|
|
8727
|
+
const rec = t.serviceRecommendations[mod.module];
|
|
8245
8728
|
if (!rec) continue;
|
|
8246
8729
|
if (!mod.warnings?.length) continue;
|
|
8247
8730
|
const hasNotEnabled = mod.warnings.some(
|
|
@@ -8254,26 +8737,26 @@ function buildServiceReminder(modules) {
|
|
|
8254
8737
|
if (disabledServices.length === 0) return "";
|
|
8255
8738
|
const lines = [
|
|
8256
8739
|
"",
|
|
8257
|
-
|
|
8740
|
+
t.serviceReminderTitle,
|
|
8258
8741
|
""
|
|
8259
8742
|
];
|
|
8260
8743
|
for (const svc of disabledServices) {
|
|
8261
|
-
lines.push(`${svc.icon} ${svc.service}
|
|
8262
|
-
lines.push(`
|
|
8263
|
-
lines.push(`
|
|
8744
|
+
lines.push(`${svc.icon} ${svc.service} ${t.notEnabled}`);
|
|
8745
|
+
lines.push(` ${t.serviceImpact}: ${svc.impact}`);
|
|
8746
|
+
lines.push(` ${t.serviceAction}: ${svc.action}`);
|
|
8264
8747
|
lines.push("");
|
|
8265
8748
|
}
|
|
8266
|
-
lines.push(
|
|
8749
|
+
lines.push(t.serviceReminderFooter);
|
|
8267
8750
|
return lines.join("\n");
|
|
8268
8751
|
}
|
|
8269
|
-
function summarizeResult(result) {
|
|
8752
|
+
function summarizeResult(result, lang) {
|
|
8270
8753
|
const { summary } = result;
|
|
8271
8754
|
const lines = [
|
|
8272
8755
|
`Scan complete for account ${result.accountId} in ${result.region}.`,
|
|
8273
8756
|
`Total findings: ${summary.totalFindings} (${summary.critical} Critical, ${summary.high} High, ${summary.medium} Medium, ${summary.low} Low)`,
|
|
8274
8757
|
`Modules: ${summary.modulesSuccess} succeeded, ${summary.modulesError} errored`
|
|
8275
8758
|
];
|
|
8276
|
-
const reminder = buildServiceReminder(result.modules);
|
|
8759
|
+
const reminder = buildServiceReminder(result.modules, lang);
|
|
8277
8760
|
if (reminder) {
|
|
8278
8761
|
lines.push(reminder);
|
|
8279
8762
|
}
|
|
@@ -8335,9 +8818,10 @@ function createServer(defaultRegion) {
|
|
|
8335
8818
|
region: z.string().optional().describe("AWS region to scan (default: server region)"),
|
|
8336
8819
|
org_mode: z.boolean().optional().describe("Enable multi-account scanning via AWS Organizations"),
|
|
8337
8820
|
role_name: z.string().optional().describe("IAM role name to assume in child accounts (default: AWSSecurityMCPAudit)"),
|
|
8338
|
-
account_ids: z.array(z.string()).optional().describe("Specific account IDs to scan (default: all org accounts)")
|
|
8821
|
+
account_ids: z.array(z.string()).optional().describe("Specific account IDs to scan (default: all org accounts)"),
|
|
8822
|
+
lang: z.enum(["zh", "en"]).optional().describe("Report language (default: zh)")
|
|
8339
8823
|
},
|
|
8340
|
-
async ({ region, org_mode, role_name, account_ids }) => {
|
|
8824
|
+
async ({ region, org_mode, role_name, account_ids, lang }) => {
|
|
8341
8825
|
try {
|
|
8342
8826
|
const r = region ?? defaultRegion;
|
|
8343
8827
|
let result;
|
|
@@ -8352,7 +8836,7 @@ function createServer(defaultRegion) {
|
|
|
8352
8836
|
}
|
|
8353
8837
|
return {
|
|
8354
8838
|
content: [
|
|
8355
|
-
{ type: "text", text: summarizeResult(result) },
|
|
8839
|
+
{ type: "text", text: summarizeResult(result, lang ?? "zh") },
|
|
8356
8840
|
{ type: "text", text: JSON.stringify(result, null, 2) }
|
|
8357
8841
|
]
|
|
8358
8842
|
};
|
|
@@ -8413,9 +8897,10 @@ function createServer(defaultRegion) {
|
|
|
8413
8897
|
region: z.string().optional().describe("AWS region to scan (default: server region)"),
|
|
8414
8898
|
org_mode: z.boolean().optional().describe("Enable multi-account scanning via AWS Organizations"),
|
|
8415
8899
|
role_name: z.string().optional().describe("IAM role name to assume in child accounts (default: AWSSecurityMCPAudit)"),
|
|
8416
|
-
account_ids: z.array(z.string()).optional().describe("Specific account IDs to scan (default: all org accounts)")
|
|
8900
|
+
account_ids: z.array(z.string()).optional().describe("Specific account IDs to scan (default: all org accounts)"),
|
|
8901
|
+
lang: z.enum(["zh", "en"]).optional().describe("Report language (default: zh)")
|
|
8417
8902
|
},
|
|
8418
|
-
async ({ group, region, org_mode, role_name, account_ids }) => {
|
|
8903
|
+
async ({ group, region, org_mode, role_name, account_ids, lang }) => {
|
|
8419
8904
|
try {
|
|
8420
8905
|
const groupDef = SCAN_GROUPS[group];
|
|
8421
8906
|
if (!groupDef) {
|
|
@@ -8497,7 +8982,7 @@ function createServer(defaultRegion) {
|
|
|
8497
8982
|
`Scan group: ${groupDef.name} (${group})`,
|
|
8498
8983
|
groupDef.description,
|
|
8499
8984
|
"",
|
|
8500
|
-
summarizeResult(result)
|
|
8985
|
+
summarizeResult(result, lang ?? "zh")
|
|
8501
8986
|
];
|
|
8502
8987
|
if (missingModules.length > 0) {
|
|
8503
8988
|
lines.push("");
|
|
@@ -8510,7 +8995,7 @@ function createServer(defaultRegion) {
|
|
|
8510
8995
|
if (group === "hw_defense") {
|
|
8511
8996
|
const summaryContent = content[0];
|
|
8512
8997
|
if (summaryContent && summaryContent.type === "text") {
|
|
8513
|
-
summaryContent.text += "\n\n" +
|
|
8998
|
+
summaryContent.text += "\n\n" + getHwDefenseChecklist(lang ?? "zh");
|
|
8514
8999
|
}
|
|
8515
9000
|
}
|
|
8516
9001
|
return { content };
|
|
@@ -8540,11 +9025,14 @@ function createServer(defaultRegion) {
|
|
|
8540
9025
|
server.tool(
|
|
8541
9026
|
"generate_report",
|
|
8542
9027
|
"Generate a Markdown security report from scan results. Read-only. Does not modify any AWS resources.",
|
|
8543
|
-
{
|
|
8544
|
-
|
|
9028
|
+
{
|
|
9029
|
+
scan_results: z.string().describe("JSON string of FullScanResult from scan_all"),
|
|
9030
|
+
lang: z.enum(["zh", "en"]).optional().describe("Report language (default: zh)")
|
|
9031
|
+
},
|
|
9032
|
+
async ({ scan_results, lang }) => {
|
|
8545
9033
|
try {
|
|
8546
9034
|
const parsed = JSON.parse(scan_results);
|
|
8547
|
-
const report = generateMarkdownReport(parsed);
|
|
9035
|
+
const report = generateMarkdownReport(parsed, lang ?? "zh");
|
|
8548
9036
|
return { content: [{ type: "text", text: report }] };
|
|
8549
9037
|
} catch (err) {
|
|
8550
9038
|
return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
@@ -8554,11 +9042,14 @@ function createServer(defaultRegion) {
|
|
|
8554
9042
|
server.tool(
|
|
8555
9043
|
"generate_mlps3_report",
|
|
8556
9044
|
"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.",
|
|
8557
|
-
{
|
|
8558
|
-
|
|
9045
|
+
{
|
|
9046
|
+
scan_results: z.string().describe("JSON string of FullScanResult from scan_group mlps3_precheck or scan_all"),
|
|
9047
|
+
lang: z.enum(["zh", "en"]).optional().describe("Report language (default: zh)")
|
|
9048
|
+
},
|
|
9049
|
+
async ({ scan_results, lang }) => {
|
|
8559
9050
|
try {
|
|
8560
9051
|
const parsed = JSON.parse(scan_results);
|
|
8561
|
-
const report = generateMlps3Report(parsed);
|
|
9052
|
+
const report = generateMlps3Report(parsed, lang ?? "zh");
|
|
8562
9053
|
return { content: [{ type: "text", text: report }] };
|
|
8563
9054
|
} catch (err) {
|
|
8564
9055
|
return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
@@ -8570,13 +9061,14 @@ function createServer(defaultRegion) {
|
|
|
8570
9061
|
"Generate a professional HTML security report. Save the output as an .html file.",
|
|
8571
9062
|
{
|
|
8572
9063
|
scan_results: z.string().describe("JSON string of FullScanResult from scan_all"),
|
|
8573
|
-
history: z.string().optional().describe("JSON string of DashboardHistoryEntry[] from dashboard data.json for 30-day trend charts")
|
|
9064
|
+
history: z.string().optional().describe("JSON string of DashboardHistoryEntry[] from dashboard data.json for 30-day trend charts"),
|
|
9065
|
+
lang: z.enum(["zh", "en"]).optional().describe("Report language (default: zh)")
|
|
8574
9066
|
},
|
|
8575
|
-
async ({ scan_results, history }) => {
|
|
9067
|
+
async ({ scan_results, history, lang }) => {
|
|
8576
9068
|
try {
|
|
8577
9069
|
const parsed = JSON.parse(scan_results);
|
|
8578
9070
|
const historyData = history ? JSON.parse(history) : void 0;
|
|
8579
|
-
const report = generateHtmlReport(parsed, historyData);
|
|
9071
|
+
const report = generateHtmlReport(parsed, historyData, lang ?? "zh");
|
|
8580
9072
|
return { content: [{ type: "text", text: report }] };
|
|
8581
9073
|
} catch (err) {
|
|
8582
9074
|
return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
@@ -8588,13 +9080,14 @@ function createServer(defaultRegion) {
|
|
|
8588
9080
|
"Generate a professional HTML MLPS Level 3 compliance report (\u7B49\u4FDD\u4E09\u7EA7). Save as .html file.",
|
|
8589
9081
|
{
|
|
8590
9082
|
scan_results: z.string().describe("JSON string of FullScanResult from scan_group mlps3_precheck or scan_all"),
|
|
8591
|
-
history: z.string().optional().describe("JSON string of DashboardHistoryEntry[] from dashboard data.json for 30-day trend charts")
|
|
9083
|
+
history: z.string().optional().describe("JSON string of DashboardHistoryEntry[] from dashboard data.json for 30-day trend charts"),
|
|
9084
|
+
lang: z.enum(["zh", "en"]).optional().describe("Report language (default: zh)")
|
|
8592
9085
|
},
|
|
8593
|
-
async ({ scan_results, history }) => {
|
|
9086
|
+
async ({ scan_results, history, lang }) => {
|
|
8594
9087
|
try {
|
|
8595
9088
|
const parsed = JSON.parse(scan_results);
|
|
8596
9089
|
const historyData = history ? JSON.parse(history) : void 0;
|
|
8597
|
-
const report = generateMlps3HtmlReport(parsed, historyData);
|
|
9090
|
+
const report = generateMlps3HtmlReport(parsed, historyData, lang ?? "zh");
|
|
8598
9091
|
return { content: [{ type: "text", text: report }] };
|
|
8599
9092
|
} catch (err) {
|
|
8600
9093
|
return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
@@ -8604,7 +9097,10 @@ function createServer(defaultRegion) {
|
|
|
8604
9097
|
server.tool(
|
|
8605
9098
|
"generate_maturity_report",
|
|
8606
9099
|
"Generate a security maturity assessment report from scan_all results. Requires service_detection module output. Read-only.",
|
|
8607
|
-
{
|
|
9100
|
+
{
|
|
9101
|
+
scan_results: z.string().describe("JSON string of FullScanResult from scan_all"),
|
|
9102
|
+
lang: z.enum(["zh", "en"]).optional().describe("Report language (default: zh)")
|
|
9103
|
+
},
|
|
8608
9104
|
async ({ scan_results }) => {
|
|
8609
9105
|
try {
|
|
8610
9106
|
const parsed = JSON.parse(scan_results);
|
|
@@ -8875,7 +9371,7 @@ ${finding}`
|
|
|
8875
9371
|
type: "text",
|
|
8876
9372
|
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
|
|
8877
9373
|
|
|
8878
|
-
${
|
|
9374
|
+
${getHwDefenseChecklist("zh")}
|
|
8879
9375
|
|
|
8880
9376
|
\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`
|
|
8881
9377
|
}
|