aws-security-mcp 0.5.2 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -8
- package/dist/bin/aws-security-mcp.js +886 -557
- package/dist/bin/aws-security-mcp.js.map +1 -1
- package/dist/src/index.d.ts +6 -4
- package/dist/src/index.js +886 -557
- package/dist/src/index.js.map +1 -1
- package/package.json +1 -1
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",
|
|
@@ -2211,7 +2219,6 @@ import {
|
|
|
2211
2219
|
DescribeNetworkInterfacesCommand,
|
|
2212
2220
|
DescribeSecurityGroupsCommand as DescribeSecurityGroupsCommand2
|
|
2213
2221
|
} from "@aws-sdk/client-ec2";
|
|
2214
|
-
var THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
2215
2222
|
function makeFinding9(opts) {
|
|
2216
2223
|
const severity = severityFromScore(opts.riskScore);
|
|
2217
2224
|
return { ...opts, severity, priority: priorityFromSeverity(severity) };
|
|
@@ -2736,14 +2743,21 @@ var SecurityHubFindingsScanner = class {
|
|
|
2736
2743
|
const resourceType = f.Resources?.[0]?.Type ?? "AWS::Unknown";
|
|
2737
2744
|
const resourceArn = resourceId.startsWith("arn:") ? resourceId : `arn:${partition}:securityhub:${region}:${accountId}:finding/${f.Id ?? "unknown"}`;
|
|
2738
2745
|
const remediationSteps = [];
|
|
2739
|
-
|
|
2740
|
-
|
|
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);
|
|
2741
2754
|
}
|
|
2742
2755
|
if (f.Remediation?.Recommendation?.Url) {
|
|
2743
|
-
remediationSteps.push(`
|
|
2756
|
+
remediationSteps.push(`Documentation: ${f.Remediation.Recommendation.Url}`);
|
|
2744
2757
|
}
|
|
2745
|
-
|
|
2746
|
-
|
|
2758
|
+
const recText = f.Remediation?.Recommendation?.Text ?? "";
|
|
2759
|
+
if (recText && !["See References", "None Provided", ""].includes(recText.trim())) {
|
|
2760
|
+
remediationSteps.push(recText);
|
|
2747
2761
|
}
|
|
2748
2762
|
findings.push({
|
|
2749
2763
|
severity,
|
|
@@ -2896,10 +2910,11 @@ var GuardDutyFindingsScanner = class {
|
|
|
2896
2910
|
impact: `GuardDuty threat type: ${gdf.Type ?? "unknown"} (severity ${gdSeverity})`,
|
|
2897
2911
|
riskScore: score,
|
|
2898
2912
|
remediationSteps: [
|
|
2899
|
-
|
|
2900
|
-
`
|
|
2901
|
-
"
|
|
2902
|
-
|
|
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),
|
|
2903
2918
|
priority: priorityFromSeverity(severity),
|
|
2904
2919
|
module: this.moduleName,
|
|
2905
2920
|
accountId: gdf.AccountId ?? accountId
|
|
@@ -2990,18 +3005,44 @@ var InspectorFindingsScanner = class {
|
|
|
2990
3005
|
const resourceType = f.resources?.[0]?.type ?? "AWS::Unknown";
|
|
2991
3006
|
const resourceArn = resourceId.startsWith("arn:") ? resourceId : `arn:${partition}:inspector2:${region}:${accountId}:finding/${f.findingArn ?? "unknown"}`;
|
|
2992
3007
|
const remediationSteps = [];
|
|
2993
|
-
|
|
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) {
|
|
2994
3018
|
remediationSteps.push(f.remediation.recommendation.text);
|
|
2995
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
|
+
}
|
|
2996
3040
|
if (f.remediation?.recommendation?.Url) {
|
|
2997
|
-
remediationSteps.push(`
|
|
3041
|
+
remediationSteps.push(`Documentation: ${f.remediation.recommendation.Url}`);
|
|
2998
3042
|
}
|
|
2999
3043
|
if (f.packageVulnerabilityDetails?.referenceUrls?.length) {
|
|
3000
3044
|
remediationSteps.push(`CVE references: ${f.packageVulnerabilityDetails.referenceUrls.slice(0, 3).join(", ")}`);
|
|
3001
3045
|
}
|
|
3002
|
-
if (remediationSteps.length === 0) {
|
|
3003
|
-
remediationSteps.push("Review the finding in the Amazon Inspector console and apply the recommended patch or update.");
|
|
3004
|
-
}
|
|
3005
3046
|
const description = f.description ?? titleBase;
|
|
3006
3047
|
const impact = cveId ? `Vulnerability ${cveId} \u2014 CVSS: ${f.packageVulnerabilityDetails?.cvss?.[0]?.baseScore ?? "N/A"}` : `Inspector finding type: ${f.type ?? "unknown"}`;
|
|
3007
3048
|
findings.push({
|
|
@@ -3336,10 +3377,10 @@ var ConfigRulesFindingsScanner = class {
|
|
|
3336
3377
|
impact: `Resource is non-compliant with Config Rule: ${ruleName}`,
|
|
3337
3378
|
riskScore,
|
|
3338
3379
|
remediationSteps: [
|
|
3339
|
-
`
|
|
3340
|
-
`
|
|
3341
|
-
|
|
3342
|
-
],
|
|
3380
|
+
`Fix Config Rule violation: ${ruleName}`,
|
|
3381
|
+
annotation ? `Details: ${annotation}` : "",
|
|
3382
|
+
`Resource: ${resourceType}/${resourceId}`
|
|
3383
|
+
].filter(Boolean),
|
|
3343
3384
|
priority: priorityFromSeverity(severity),
|
|
3344
3385
|
module: this.moduleName,
|
|
3345
3386
|
accountId
|
|
@@ -3488,13 +3529,13 @@ var AccessAnalyzerFindingsScanner = class {
|
|
|
3488
3529
|
const title = buildFindingTitle(aaf);
|
|
3489
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"}`;
|
|
3490
3531
|
const remediationSteps = external ? [
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3532
|
+
`Restrict external access on ${resourceType} ${resourceId}`,
|
|
3533
|
+
"Remove or narrow the resource policy to eliminate unintended external access.",
|
|
3534
|
+
`Resource ARN: ${resourceArn}`
|
|
3494
3535
|
] : [
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3536
|
+
`Remove unused access on ${resourceType} ${resourceId}`,
|
|
3537
|
+
"Remove unused permissions, roles, or credentials to follow least-privilege.",
|
|
3538
|
+
`Resource ARN: ${resourceArn}`
|
|
3498
3539
|
];
|
|
3499
3540
|
findings.push({
|
|
3500
3541
|
severity,
|
|
@@ -3969,6 +4010,479 @@ var WafCoverageScanner = class {
|
|
|
3969
4010
|
}
|
|
3970
4011
|
};
|
|
3971
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
|
+
|
|
3972
4486
|
// src/tools/report-tool.ts
|
|
3973
4487
|
var SEVERITY_ICON = {
|
|
3974
4488
|
CRITICAL: "\u{1F534}",
|
|
@@ -3986,38 +4500,45 @@ function formatDuration(start, end) {
|
|
|
3986
4500
|
const remainSecs = secs % 60;
|
|
3987
4501
|
return `${mins}m ${remainSecs}s`;
|
|
3988
4502
|
}
|
|
3989
|
-
function
|
|
3990
|
-
const
|
|
3991
|
-
return [
|
|
3992
|
-
`#### ${f.title}`,
|
|
3993
|
-
`- **Resource:** ${f.resourceId} (\`${f.resourceArn}\`)`,
|
|
3994
|
-
`- **Description:** ${f.description}`,
|
|
3995
|
-
`- **Impact:** ${f.impact}`,
|
|
3996
|
-
`- **Risk Score:** ${f.riskScore}/10`,
|
|
3997
|
-
`- **Remediation:**`,
|
|
3998
|
-
steps,
|
|
3999
|
-
`- **Priority:** ${f.priority}`
|
|
4000
|
-
].join("\n");
|
|
4001
|
-
}
|
|
4002
|
-
function generateMarkdownReport(scanResults) {
|
|
4503
|
+
function generateMarkdownReport(scanResults, lang) {
|
|
4504
|
+
const t = getI18n(lang ?? "zh");
|
|
4003
4505
|
const { summary, modules, accountId, region, scanStart, scanEnd } = scanResults;
|
|
4004
4506
|
const date = scanStart.split("T")[0];
|
|
4005
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
|
+
}
|
|
4006
4527
|
const lines = [];
|
|
4007
|
-
lines.push(`#
|
|
4528
|
+
lines.push(`# ${t.securityReportTitle} \u2014 ${date}`);
|
|
4008
4529
|
lines.push("");
|
|
4009
|
-
lines.push(
|
|
4010
|
-
lines.push(`-
|
|
4011
|
-
lines.push(`-
|
|
4012
|
-
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}`);
|
|
4013
4534
|
lines.push(
|
|
4014
|
-
`-
|
|
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})`
|
|
4015
4536
|
);
|
|
4016
4537
|
lines.push("");
|
|
4017
4538
|
if (summary.totalFindings === 0) {
|
|
4018
|
-
lines.push(
|
|
4539
|
+
lines.push(`## ${t.findingsBySeverity}`);
|
|
4019
4540
|
lines.push("");
|
|
4020
|
-
lines.push(
|
|
4541
|
+
lines.push(`\u2705 ${t.noIssuesFound}`);
|
|
4021
4542
|
lines.push("");
|
|
4022
4543
|
} else {
|
|
4023
4544
|
const allFindings = modules.flatMap((m) => m.findings);
|
|
@@ -4028,15 +4549,15 @@ function generateMarkdownReport(scanResults) {
|
|
|
4028
4549
|
for (const f of allFindings) {
|
|
4029
4550
|
grouped.get(f.severity).push(f);
|
|
4030
4551
|
}
|
|
4031
|
-
lines.push(
|
|
4552
|
+
lines.push(`## ${t.findingsBySeverity}`);
|
|
4032
4553
|
lines.push("");
|
|
4033
4554
|
for (const sev of SEVERITY_ORDER) {
|
|
4034
4555
|
const findings = grouped.get(sev);
|
|
4035
4556
|
const icon = SEVERITY_ICON[sev];
|
|
4036
|
-
lines.push(`### ${icon} ${sev
|
|
4557
|
+
lines.push(`### ${icon} ${sevLabel[sev]}`);
|
|
4037
4558
|
lines.push("");
|
|
4038
4559
|
if (findings.length === 0) {
|
|
4039
|
-
lines.push(
|
|
4560
|
+
lines.push(t.noFindingsForSeverity(sevLabel[sev]));
|
|
4040
4561
|
lines.push("");
|
|
4041
4562
|
continue;
|
|
4042
4563
|
}
|
|
@@ -4047,9 +4568,9 @@ function generateMarkdownReport(scanResults) {
|
|
|
4047
4568
|
}
|
|
4048
4569
|
}
|
|
4049
4570
|
}
|
|
4050
|
-
lines.push(
|
|
4571
|
+
lines.push(`## ${t.scanStatistics}`);
|
|
4051
4572
|
lines.push(
|
|
4052
|
-
|
|
4573
|
+
`| ${t.module} | ${t.resources} | ${t.findings} | ${t.status} |`
|
|
4053
4574
|
);
|
|
4054
4575
|
lines.push("|--------|------------------|----------|--------|");
|
|
4055
4576
|
for (const m of modules) {
|
|
@@ -4062,7 +4583,7 @@ function generateMarkdownReport(scanResults) {
|
|
|
4062
4583
|
if (summary.totalFindings > 0) {
|
|
4063
4584
|
const allFindings = modules.flatMap((m) => m.findings);
|
|
4064
4585
|
allFindings.sort((a, b) => b.riskScore - a.riskScore);
|
|
4065
|
-
lines.push(
|
|
4586
|
+
lines.push(`## ${t.recommendations}`);
|
|
4066
4587
|
for (let i = 0; i < allFindings.length; i++) {
|
|
4067
4588
|
const f = allFindings[i];
|
|
4068
4589
|
lines.push(`${i + 1}. [${f.priority}] ${f.title}: ${f.remediationSteps[0] ?? "Review and remediate."}`);
|
|
@@ -6109,13 +6630,6 @@ var MLPS3_CATEGORY_ORDER = [
|
|
|
6109
6630
|
"\u5B89\u5168\u8BA1\u7B97\u73AF\u5883",
|
|
6110
6631
|
"\u5B89\u5168\u7BA1\u7406\u4E2D\u5FC3"
|
|
6111
6632
|
];
|
|
6112
|
-
var MLPS3_CATEGORY_SECTION = {
|
|
6113
|
-
"\u5B89\u5168\u7269\u7406\u73AF\u5883": "\u4E00\u3001\u5B89\u5168\u7269\u7406\u73AF\u5883",
|
|
6114
|
-
"\u5B89\u5168\u901A\u4FE1\u7F51\u7EDC": "\u4E8C\u3001\u5B89\u5168\u901A\u4FE1\u7F51\u7EDC",
|
|
6115
|
-
"\u5B89\u5168\u533A\u57DF\u8FB9\u754C": "\u4E09\u3001\u5B89\u5168\u533A\u57DF\u8FB9\u754C",
|
|
6116
|
-
"\u5B89\u5168\u8BA1\u7B97\u73AF\u5883": "\u56DB\u3001\u5B89\u5168\u8BA1\u7B97\u73AF\u5883",
|
|
6117
|
-
"\u5B89\u5168\u7BA1\u7406\u4E2D\u5FC3": "\u4E94\u3001\u5B89\u5168\u7BA1\u7406\u4E2D\u5FC3"
|
|
6118
|
-
};
|
|
6119
6633
|
var MLPS3_CHECK_MAPPING = [
|
|
6120
6634
|
// =========================================================================
|
|
6121
6635
|
// 安全物理环境 — L3-PES1-* (22 items) → cloud_provider
|
|
@@ -6680,9 +7194,8 @@ var MLPS3_CHECK_MAPPING = [
|
|
|
6680
7194
|
},
|
|
6681
7195
|
{
|
|
6682
7196
|
id: "L3-SMC1-09",
|
|
6683
|
-
type: "
|
|
6684
|
-
|
|
6685
|
-
findingPatterns: ["CloudWatch"]
|
|
7197
|
+
type: "manual",
|
|
7198
|
+
guidance: "\u9700\u914D\u7F6E CloudWatch \u96C6\u4E2D\u76D1\u63A7\u5E73\u53F0\uFF0C\u7ED3\u5408 SNS \u8FDB\u884C\u544A\u8B66\u901A\u77E5"
|
|
6686
7199
|
},
|
|
6687
7200
|
{
|
|
6688
7201
|
id: "L3-SMC1-10",
|
|
@@ -6776,232 +7289,68 @@ function evaluateAllFullChecks(scanResults) {
|
|
|
6776
7289
|
return evaluateFullCheck(item, mapping, allFindings, scanModules);
|
|
6777
7290
|
});
|
|
6778
7291
|
}
|
|
6779
|
-
|
|
6780
|
-
|
|
6781
|
-
|
|
6782
|
-
id: "8.1.4.1a",
|
|
6783
|
-
category: "\u8EAB\u4EFD\u9274\u522B",
|
|
6784
|
-
name: "\u5BC6\u7801\u7B56\u7565",
|
|
6785
|
-
modules: ["security_hub_findings"],
|
|
6786
|
-
findingPatterns: ["password policy", "password length", "complexity", "password expiry", "reuse prevention", "IAM.7", "IAM.10"]
|
|
6787
|
-
},
|
|
6788
|
-
{
|
|
6789
|
-
id: "8.1.4.1a",
|
|
6790
|
-
category: "\u8EAB\u4EFD\u9274\u522B",
|
|
6791
|
-
name: "\u5BC6\u94A5\u8F6E\u6362",
|
|
6792
|
-
modules: ["security_hub_findings"],
|
|
6793
|
-
findingPatterns: ["access key older", "access key rotated", "IAM.3", "IAM.4"]
|
|
6794
|
-
},
|
|
6795
|
-
{
|
|
6796
|
-
id: "8.1.4.1d",
|
|
6797
|
-
category: "\u8EAB\u4EFD\u9274\u522B",
|
|
6798
|
-
name: "\u53CC\u56E0\u7D20\u8BA4\u8BC1",
|
|
6799
|
-
modules: ["security_hub_findings"],
|
|
6800
|
-
findingPatterns: ["MFA", "IAM.5", "IAM.6"]
|
|
6801
|
-
},
|
|
6802
|
-
// 二、访问控制
|
|
6803
|
-
{
|
|
6804
|
-
id: "8.1.4.2c",
|
|
6805
|
-
category: "\u8BBF\u95EE\u63A7\u5236",
|
|
6806
|
-
name: "\u6700\u5C0F\u6743\u9650",
|
|
6807
|
-
modules: ["iam_privilege_escalation", "security_hub_findings"],
|
|
6808
|
-
findingPatterns: [
|
|
6809
|
-
"AdministratorAccess",
|
|
6810
|
-
"PowerUserAccess",
|
|
6811
|
-
"IAMFullAccess",
|
|
6812
|
-
"over-permissive",
|
|
6813
|
-
"privilege escalation",
|
|
6814
|
-
"self-grant",
|
|
6815
|
-
"iam:*",
|
|
6816
|
-
"create admin",
|
|
6817
|
-
"Lambda role passing",
|
|
6818
|
-
"CreateAccessKey",
|
|
6819
|
-
"AssumeRole"
|
|
6820
|
-
]
|
|
6821
|
-
},
|
|
6822
|
-
{
|
|
6823
|
-
id: "8.1.4.2",
|
|
6824
|
-
category: "\u8BBF\u95EE\u63A7\u5236",
|
|
6825
|
-
name: "\u5B89\u5168\u7EC4",
|
|
6826
|
-
modules: ["network_reachability", "security_hub_findings"],
|
|
6827
|
-
findingPatterns: ["allows all ports", "allows SSH", "allows RDP", "MySQL", "PostgreSQL", "MongoDB", "Redis", "high-risk port", "security group", "EC2.18", "EC2.19"]
|
|
6828
|
-
},
|
|
6829
|
-
// 三、安全审计
|
|
6830
|
-
{
|
|
6831
|
-
id: "8.1.4.3a",
|
|
6832
|
-
category: "\u5B89\u5168\u5BA1\u8BA1",
|
|
6833
|
-
name: "\u5BA1\u8BA1\u529F\u80FD",
|
|
6834
|
-
modules: ["security_hub_findings"],
|
|
6835
|
-
findingPatterns: ["CloudTrail", "not enabled", "multi-region", "not logging", "CloudTrail.1"]
|
|
6836
|
-
},
|
|
6837
|
-
{
|
|
6838
|
-
id: "8.1.4.3b",
|
|
6839
|
-
category: "\u5B89\u5168\u5BA1\u8BA1",
|
|
6840
|
-
name: "\u5BA1\u8BA1\u5B8C\u6574\u6027",
|
|
6841
|
-
modules: ["security_hub_findings"],
|
|
6842
|
-
findingPatterns: ["log file validation", "log integrity", "log validation", "CloudTrail.4", "CloudTrail.5"]
|
|
6843
|
-
},
|
|
6844
|
-
{
|
|
6845
|
-
id: "8.1.4.3c",
|
|
6846
|
-
category: "\u5B89\u5168\u5BA1\u8BA1",
|
|
6847
|
-
name: "\u5BA1\u8BA1\u4FDD\u62A4",
|
|
6848
|
-
modules: ["security_hub_findings"],
|
|
6849
|
-
findingPatterns: ["CloudTrail", "S3 bucket", "encryption", "versioning", "Block Public Access", "CloudTrail.6", "CloudTrail.7"]
|
|
6850
|
-
},
|
|
6851
|
-
// 四、入侵防范
|
|
6852
|
-
{
|
|
6853
|
-
id: "8.1.4.4a",
|
|
6854
|
-
category: "\u5165\u4FB5\u9632\u8303",
|
|
6855
|
-
name: "GuardDuty \u5A01\u80C1\u68C0\u6D4B",
|
|
6856
|
-
modules: ["service_detection", "guardduty_findings"],
|
|
6857
|
-
findingPatterns: ["GuardDuty"]
|
|
6858
|
-
},
|
|
6859
|
-
{
|
|
6860
|
-
id: "8.1.4.4a",
|
|
6861
|
-
category: "\u5165\u4FB5\u9632\u8303",
|
|
6862
|
-
name: "Inspector \u6F0F\u6D1E\u626B\u63CF",
|
|
6863
|
-
modules: ["service_detection", "inspector_findings"],
|
|
6864
|
-
findingPatterns: ["Inspector", "CVE-"]
|
|
6865
|
-
},
|
|
6866
|
-
// 五、数据安全
|
|
6867
|
-
{
|
|
6868
|
-
id: "8.1.4.5a",
|
|
6869
|
-
category: "\u6570\u636E\u5B89\u5168",
|
|
6870
|
-
name: "\u4F20\u8F93\u52A0\u5BC6",
|
|
6871
|
-
modules: ["ssl_certificate", "security_hub_findings"],
|
|
6872
|
-
findingPatterns: ["HTTPS", "TLS", "HTTP listener", "certificate", "ELB.1"]
|
|
6873
|
-
},
|
|
6874
|
-
{
|
|
6875
|
-
id: "8.1.4.5b",
|
|
6876
|
-
category: "\u6570\u636E\u5B89\u5168",
|
|
6877
|
-
name: "S3 \u5B58\u50A8\u52A0\u5BC6",
|
|
6878
|
-
modules: ["security_hub_findings"],
|
|
6879
|
-
findingPatterns: ["no default encryption", "not encrypted", "S3.4"]
|
|
6880
|
-
},
|
|
6881
|
-
{
|
|
6882
|
-
id: "8.1.4.5b",
|
|
6883
|
-
category: "\u6570\u636E\u5B89\u5168",
|
|
6884
|
-
name: "EBS \u9ED8\u8BA4\u52A0\u5BC6",
|
|
6885
|
-
modules: ["security_hub_findings"],
|
|
6886
|
-
findingPatterns: ["EBS default encryption", "EC2.7"]
|
|
6887
|
-
},
|
|
6888
|
-
{
|
|
6889
|
-
id: "8.1.4.5b",
|
|
6890
|
-
category: "\u6570\u636E\u5B89\u5168",
|
|
6891
|
-
name: "RDS \u5B58\u50A8\u52A0\u5BC6",
|
|
6892
|
-
modules: ["security_hub_findings"],
|
|
6893
|
-
findingPatterns: ["storage is not encrypted", "RDS.3"]
|
|
6894
|
-
},
|
|
6895
|
-
// 六、网络安全
|
|
6896
|
-
{
|
|
6897
|
-
id: "8.1.3.1a",
|
|
6898
|
-
category: "\u7F51\u7EDC\u5B89\u5168",
|
|
6899
|
-
name: "\u7F51\u7EDC\u67B6\u6784",
|
|
6900
|
-
modules: ["security_hub_findings"],
|
|
6901
|
-
findingPatterns: ["default VPC", "EC2.2"]
|
|
6902
|
-
},
|
|
6903
|
-
{
|
|
6904
|
-
id: "8.1.3.2a",
|
|
6905
|
-
category: "\u7F51\u7EDC\u5B89\u5168",
|
|
6906
|
-
name: "\u8FB9\u754C\u9632\u62A4",
|
|
6907
|
-
modules: ["network_reachability", "security_hub_findings"],
|
|
6908
|
-
findingPatterns: ["allows all ports", "allows SSH", "allows RDP", "security group", "EC2.18", "EC2.19"]
|
|
6909
|
-
}
|
|
6910
|
-
];
|
|
6911
|
-
var CATEGORY_ORDER = [
|
|
6912
|
-
"\u8EAB\u4EFD\u9274\u522B",
|
|
6913
|
-
"\u8BBF\u95EE\u63A7\u5236",
|
|
6914
|
-
"\u5B89\u5168\u5BA1\u8BA1",
|
|
6915
|
-
"\u5165\u4FB5\u9632\u8303",
|
|
6916
|
-
"\u6570\u636E\u5B89\u5168",
|
|
6917
|
-
"\u7F51\u7EDC\u5B89\u5168"
|
|
6918
|
-
];
|
|
6919
|
-
var CATEGORY_SECTION = {
|
|
6920
|
-
"\u8EAB\u4EFD\u9274\u522B": "\u4E00\u3001\u8EAB\u4EFD\u9274\u522B",
|
|
6921
|
-
"\u8BBF\u95EE\u63A7\u5236": "\u4E8C\u3001\u8BBF\u95EE\u63A7\u5236",
|
|
6922
|
-
"\u5B89\u5168\u5BA1\u8BA1": "\u4E09\u3001\u5B89\u5168\u5BA1\u8BA1",
|
|
6923
|
-
"\u5165\u4FB5\u9632\u8303": "\u56DB\u3001\u5165\u4FB5\u9632\u8303",
|
|
6924
|
-
"\u6570\u636E\u5B89\u5168": "\u4E94\u3001\u6570\u636E\u5B89\u5168",
|
|
6925
|
-
"\u7F51\u7EDC\u5B89\u5168": "\u516D\u3001\u7F51\u7EDC\u5B89\u5168"
|
|
6926
|
-
};
|
|
6927
|
-
function evaluateCheck(check, allFindings, scanModules) {
|
|
6928
|
-
const allModulesPresent = check.modules.every(
|
|
6929
|
-
(mod) => scanModules.some((m) => m.module === mod && m.status === "success")
|
|
6930
|
-
);
|
|
6931
|
-
if (!allModulesPresent) {
|
|
6932
|
-
return { check, status: "unknown", relatedFindings: [] };
|
|
6933
|
-
}
|
|
6934
|
-
const relatedFindings = allFindings.filter((f) => {
|
|
6935
|
-
const moduleMatch = check.modules.some((mod) => f.module === mod);
|
|
6936
|
-
if (!moduleMatch) return false;
|
|
6937
|
-
const text = `${f.title} ${f.description}`.toLowerCase();
|
|
6938
|
-
return check.findingPatterns.some(
|
|
6939
|
-
(pattern) => text.includes(pattern.toLowerCase())
|
|
6940
|
-
);
|
|
6941
|
-
});
|
|
6942
|
-
return {
|
|
6943
|
-
check,
|
|
6944
|
-
status: relatedFindings.length === 0 ? "clean" : "issues",
|
|
6945
|
-
relatedFindings
|
|
6946
|
-
};
|
|
6947
|
-
}
|
|
6948
|
-
function generateMlps3Report(scanResults) {
|
|
7292
|
+
function generateMlps3Report(scanResults, lang) {
|
|
7293
|
+
const t = getI18n(lang ?? "zh");
|
|
7294
|
+
const isEn = (lang ?? "zh") === "en";
|
|
6949
7295
|
const { accountId, region, scanStart } = scanResults;
|
|
6950
7296
|
const scanTime = scanStart.replace("T", " ").replace(/\.\d+Z$/, " UTC");
|
|
6951
|
-
const
|
|
6952
|
-
|
|
6953
|
-
);
|
|
6954
|
-
const
|
|
6955
|
-
|
|
6956
|
-
|
|
6957
|
-
|
|
6958
|
-
const
|
|
6959
|
-
|
|
6960
|
-
);
|
|
6961
|
-
const
|
|
6962
|
-
const issuesCount = results.filter((r) => r.status === "issues").length;
|
|
6963
|
-
const unknownCount = results.filter((r) => r.status === "unknown").length;
|
|
6964
|
-
const checkedTotal = cleanCount + issuesCount;
|
|
6965
|
-
const total = results.length;
|
|
7297
|
+
const results = evaluateAllFullChecks(scanResults);
|
|
7298
|
+
const autoResults = results.filter((r) => r.mapping.type === "auto");
|
|
7299
|
+
const autoClean = autoResults.filter((r) => r.status === "clean").length;
|
|
7300
|
+
const autoIssues = autoResults.filter((r) => r.status === "issues").length;
|
|
7301
|
+
const autoUnknown = autoResults.filter((r) => r.status === "unknown").length;
|
|
7302
|
+
const checkedTotal = autoClean + autoIssues;
|
|
7303
|
+
const cloudCount = results.filter((r) => r.status === "cloud_provider").length;
|
|
7304
|
+
const manualCount = results.filter((r) => r.status === "manual").length;
|
|
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;
|
|
6966
7308
|
const lines = [];
|
|
6967
|
-
lines.push(
|
|
6968
|
-
lines.push(
|
|
7309
|
+
lines.push(`# ${t.mlpsTitle}`);
|
|
7310
|
+
lines.push(`> **${t.mlpsDisclaimer}**`);
|
|
6969
7311
|
lines.push("");
|
|
6970
|
-
lines.push(
|
|
6971
|
-
lines.push(`-
|
|
7312
|
+
lines.push(`## ${t.accountInfo}`);
|
|
7313
|
+
lines.push(`- ${t.account}: ${accountId} | ${t.region}: ${region} | ${t.scanTime}: ${scanTime}`);
|
|
6972
7314
|
lines.push("");
|
|
6973
|
-
lines.push(
|
|
6974
|
-
lines.push(`-
|
|
6975
|
-
|
|
6976
|
-
|
|
6977
|
-
|
|
6978
|
-
|
|
7315
|
+
lines.push(`## ${t.preCheckOverview}`);
|
|
7316
|
+
lines.push(`- ${t.checkedCount(checkedTotal, autoClean, autoIssues)}`);
|
|
7317
|
+
if (autoUnknown > 0) {
|
|
7318
|
+
lines.push(`- ${t.uncheckedCount(autoUnknown)}`);
|
|
7319
|
+
}
|
|
7320
|
+
lines.push(`- ${t.cloudProviderCount(cloudCount)}`);
|
|
7321
|
+
lines.push(`- ${t.manualReviewCount(manualCount)}`);
|
|
7322
|
+
if (naCount > 0) {
|
|
7323
|
+
lines.push(`- ${t.naCount(naCount)}`);
|
|
6979
7324
|
}
|
|
6980
7325
|
lines.push("");
|
|
6981
|
-
for (const category of
|
|
6982
|
-
const sectionTitle =
|
|
6983
|
-
const
|
|
6984
|
-
|
|
7326
|
+
for (const category of MLPS3_CATEGORY_ORDER) {
|
|
7327
|
+
const sectionTitle = t.mlpsCategorySection[category] ?? category;
|
|
7328
|
+
const catResults = results.filter(
|
|
7329
|
+
(r) => r.item.categoryCn === category && r.status !== "not_applicable"
|
|
7330
|
+
);
|
|
7331
|
+
if (catResults.length === 0) continue;
|
|
6985
7332
|
lines.push(`## ${sectionTitle}`);
|
|
6986
7333
|
lines.push("");
|
|
6987
|
-
const
|
|
6988
|
-
for (const r of
|
|
6989
|
-
const
|
|
6990
|
-
|
|
6991
|
-
|
|
7334
|
+
const controlMap = /* @__PURE__ */ new Map();
|
|
7335
|
+
for (const r of catResults) {
|
|
7336
|
+
const key = r.item.controlCn;
|
|
7337
|
+
if (!controlMap.has(key)) controlMap.set(key, []);
|
|
7338
|
+
controlMap.get(key).push(r);
|
|
6992
7339
|
}
|
|
6993
|
-
for (const [
|
|
6994
|
-
|
|
6995
|
-
|
|
6996
|
-
|
|
6997
|
-
const
|
|
6998
|
-
|
|
7340
|
+
for (const [_controlKey, controlResults] of controlMap) {
|
|
7341
|
+
const controlName = itemControl(controlResults[0]);
|
|
7342
|
+
lines.push(`### ${controlName}`);
|
|
7343
|
+
for (const r of controlResults) {
|
|
7344
|
+
const icon = r.status === "clean" ? "\u2705" : r.status === "issues" ? "\u274C" : r.status === "unknown" ? "\u26A0\uFE0F" : r.status === "manual" ? "\u{1F4CB}" : "\u{1F3E2}";
|
|
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}`);
|
|
6999
7348
|
if (r.status === "issues" && r.relatedFindings.length > 0) {
|
|
7000
7349
|
for (const f of r.relatedFindings.slice(0, 3)) {
|
|
7001
7350
|
lines.push(` - ${f.severity}: ${f.title}`);
|
|
7002
7351
|
}
|
|
7003
7352
|
if (r.relatedFindings.length > 3) {
|
|
7004
|
-
lines.push(` -
|
|
7353
|
+
lines.push(` - ${t.andMore(r.relatedFindings.length - 3)}`);
|
|
7005
7354
|
}
|
|
7006
7355
|
}
|
|
7007
7356
|
}
|
|
@@ -7010,7 +7359,7 @@ function generateMlps3Report(scanResults) {
|
|
|
7010
7359
|
}
|
|
7011
7360
|
const failedResults = results.filter((r) => r.status === "issues");
|
|
7012
7361
|
if (failedResults.length > 0) {
|
|
7013
|
-
lines.push(
|
|
7362
|
+
lines.push(`## ${t.remediationByPriority}`);
|
|
7014
7363
|
lines.push("");
|
|
7015
7364
|
const allFailedFindings = /* @__PURE__ */ new Map();
|
|
7016
7365
|
for (const r of failedResults) {
|
|
@@ -7032,6 +7381,10 @@ function generateMlps3Report(scanResults) {
|
|
|
7032
7381
|
}
|
|
7033
7382
|
lines.push("");
|
|
7034
7383
|
}
|
|
7384
|
+
if (naCount > 0) {
|
|
7385
|
+
lines.push(`> ${t.naNote(naCount)}`);
|
|
7386
|
+
lines.push("");
|
|
7387
|
+
}
|
|
7035
7388
|
return lines.join("\n");
|
|
7036
7389
|
}
|
|
7037
7390
|
|
|
@@ -7039,6 +7392,15 @@ function generateMlps3Report(scanResults) {
|
|
|
7039
7392
|
function esc(s) {
|
|
7040
7393
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
7041
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
|
+
}
|
|
7042
7404
|
function calcScore(summary) {
|
|
7043
7405
|
const raw = 100 - summary.critical * 15 - summary.high * 5 - summary.medium * 2 - summary.low * 0.5;
|
|
7044
7406
|
return Math.max(0, Math.min(100, Math.round(raw)));
|
|
@@ -7062,50 +7424,6 @@ function scoreColor(score) {
|
|
|
7062
7424
|
if (score >= 50) return "#eab308";
|
|
7063
7425
|
return "#ef4444";
|
|
7064
7426
|
}
|
|
7065
|
-
var SERVICE_RECOMMENDATIONS = {
|
|
7066
|
-
security_hub_findings: {
|
|
7067
|
-
icon: "\u{1F534}",
|
|
7068
|
-
service: "Security Hub",
|
|
7069
|
-
impact: "\u65E0\u6CD5\u83B7\u53D6 300+ \u9879\u81EA\u52A8\u5316\u5B89\u5168\u68C0\u67E5\uFF08FSBP/CIS/PCI DSS \u6807\u51C6\uFF09",
|
|
7070
|
-
action: "\u542F\u7528 Security Hub \u83B7\u5F97\u6700\u5168\u9762\u7684\u5B89\u5168\u6001\u52BF\u8BC4\u4F30"
|
|
7071
|
-
},
|
|
7072
|
-
guardduty_findings: {
|
|
7073
|
-
icon: "\u{1F534}",
|
|
7074
|
-
service: "GuardDuty",
|
|
7075
|
-
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",
|
|
7076
|
-
action: "\u542F\u7528 GuardDuty \u83B7\u5F97\u6301\u7EED\u5A01\u80C1\u68C0\u6D4B\u80FD\u529B"
|
|
7077
|
-
},
|
|
7078
|
-
inspector_findings: {
|
|
7079
|
-
icon: "\u{1F7E1}",
|
|
7080
|
-
service: "Inspector",
|
|
7081
|
-
impact: "\u65E0\u6CD5\u626B\u63CF EC2/Lambda/\u5BB9\u5668\u7684\u8F6F\u4EF6\u6F0F\u6D1E\uFF08CVE\uFF09",
|
|
7082
|
-
action: "\u542F\u7528 Inspector \u53D1\u73B0\u5DF2\u77E5\u5B89\u5168\u6F0F\u6D1E"
|
|
7083
|
-
},
|
|
7084
|
-
trusted_advisor_findings: {
|
|
7085
|
-
icon: "\u{1F7E1}",
|
|
7086
|
-
service: "Trusted Advisor",
|
|
7087
|
-
impact: "\u65E0\u6CD5\u83B7\u53D6 AWS \u6700\u4F73\u5B9E\u8DF5\u5B89\u5168\u68C0\u67E5",
|
|
7088
|
-
action: "\u5347\u7EA7\u81F3 Business/Enterprise Support \u8BA1\u5212\u4EE5\u4F7F\u7528 Trusted Advisor \u5B89\u5168\u68C0\u67E5"
|
|
7089
|
-
},
|
|
7090
|
-
config_rules_findings: {
|
|
7091
|
-
icon: "\u{1F7E1}",
|
|
7092
|
-
service: "AWS Config",
|
|
7093
|
-
impact: "\u65E0\u6CD5\u68C0\u67E5\u8D44\u6E90\u914D\u7F6E\u5408\u89C4\u72B6\u6001",
|
|
7094
|
-
action: "\u542F\u7528 AWS Config \u5E76\u914D\u7F6E Config Rules"
|
|
7095
|
-
},
|
|
7096
|
-
access_analyzer_findings: {
|
|
7097
|
-
icon: "\u{1F7E1}",
|
|
7098
|
-
service: "IAM Access Analyzer",
|
|
7099
|
-
impact: "\u65E0\u6CD5\u68C0\u6D4B\u8D44\u6E90\u662F\u5426\u88AB\u5916\u90E8\u8D26\u53F7\u6216\u516C\u7F51\u8BBF\u95EE",
|
|
7100
|
-
action: "\u521B\u5EFA IAM Access Analyzer\uFF08\u8D26\u6237\u7EA7\u6216\u7EC4\u7EC7\u7EA7\uFF09"
|
|
7101
|
-
},
|
|
7102
|
-
patch_compliance_findings: {
|
|
7103
|
-
icon: "\u{1F7E1}",
|
|
7104
|
-
service: "SSM Patch Manager",
|
|
7105
|
-
impact: "\u65E0\u6CD5\u68C0\u67E5\u5B9E\u4F8B\u8865\u4E01\u5408\u89C4\u72B6\u6001",
|
|
7106
|
-
action: "\u5B89\u88C5 SSM Agent \u5E76\u914D\u7F6E Patch Manager"
|
|
7107
|
-
}
|
|
7108
|
-
};
|
|
7109
7427
|
var SERVICE_NOT_ENABLED_PATTERNS = [
|
|
7110
7428
|
"not enabled",
|
|
7111
7429
|
"not found",
|
|
@@ -7115,10 +7433,11 @@ var SERVICE_NOT_ENABLED_PATTERNS = [
|
|
|
7115
7433
|
"not available",
|
|
7116
7434
|
"is not enabled"
|
|
7117
7435
|
];
|
|
7118
|
-
function getDisabledServices(modules) {
|
|
7436
|
+
function getDisabledServices(modules, lang) {
|
|
7437
|
+
const t = getI18n(lang ?? "zh");
|
|
7119
7438
|
const disabled = [];
|
|
7120
7439
|
for (const mod of modules) {
|
|
7121
|
-
const rec =
|
|
7440
|
+
const rec = t.serviceRecommendations[mod.module];
|
|
7122
7441
|
if (!rec) continue;
|
|
7123
7442
|
if (!mod.warnings?.length) continue;
|
|
7124
7443
|
const hasNotEnabled = mod.warnings.some(
|
|
@@ -7130,21 +7449,22 @@ function getDisabledServices(modules) {
|
|
|
7130
7449
|
}
|
|
7131
7450
|
return disabled;
|
|
7132
7451
|
}
|
|
7133
|
-
function buildServiceReminderHtml(modules) {
|
|
7134
|
-
const
|
|
7452
|
+
function buildServiceReminderHtml(modules, lang) {
|
|
7453
|
+
const t = getI18n(lang ?? "zh");
|
|
7454
|
+
const disabled = getDisabledServices(modules, lang);
|
|
7135
7455
|
if (disabled.length === 0) return "";
|
|
7136
7456
|
const items = disabled.map((svc) => `
|
|
7137
7457
|
<div style="margin-bottom:12px">
|
|
7138
|
-
<div style="font-weight:600;font-size:15px">${esc(svc.icon)} ${esc(svc.service)}
|
|
7139
|
-
<div style="margin-left:28px;color:#cbd5e1;font-size:13px"
|
|
7140
|
-
<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>
|
|
7141
7461
|
</div>`).join("\n");
|
|
7142
7462
|
return `
|
|
7143
7463
|
<section>
|
|
7144
7464
|
<div style="background:#2d1f00;border:1px solid #b45309;border-radius:8px;padding:20px;margin-bottom:32px">
|
|
7145
|
-
<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>
|
|
7146
7466
|
${items}
|
|
7147
|
-
<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>
|
|
7148
7468
|
</div>
|
|
7149
7469
|
</section>`;
|
|
7150
7470
|
}
|
|
@@ -7346,12 +7666,12 @@ function donutChart(summary) {
|
|
|
7346
7666
|
"</svg>"
|
|
7347
7667
|
].join("\n");
|
|
7348
7668
|
}
|
|
7349
|
-
function barChart(modules) {
|
|
7669
|
+
function barChart(modules, allCleanLabel = "All modules clean") {
|
|
7350
7670
|
const withFindings = modules.filter((m) => m.findingsCount > 0).sort((a, b) => b.findingsCount - a.findingsCount).slice(0, 12);
|
|
7351
7671
|
if (withFindings.length === 0) {
|
|
7352
7672
|
return [
|
|
7353
7673
|
'<svg viewBox="0 0 400 50" width="100%">',
|
|
7354
|
-
|
|
7674
|
+
` <text x="200" y="30" text-anchor="middle" fill="#22c55e" font-size="14" font-weight="600">${esc(allCleanLabel)}</text>`,
|
|
7355
7675
|
"</svg>"
|
|
7356
7676
|
].join("\n");
|
|
7357
7677
|
}
|
|
@@ -7466,7 +7786,9 @@ function scoreTrendChart(history) {
|
|
|
7466
7786
|
"</svg>"
|
|
7467
7787
|
].join("\n");
|
|
7468
7788
|
}
|
|
7469
|
-
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";
|
|
7470
7792
|
const { summary, modules, accountId, region, scanStart, scanEnd } = scanResults;
|
|
7471
7793
|
const date = scanStart.split("T")[0];
|
|
7472
7794
|
const duration = formatDuration2(scanStart, scanEnd);
|
|
@@ -7484,23 +7806,23 @@ function generateHtmlReport(scanResults, history) {
|
|
|
7484
7806
|
<div class="top5-content">
|
|
7485
7807
|
<span class="badge badge-${esc(f.severity.toLowerCase())}">${esc(f.severity)}</span>
|
|
7486
7808
|
<div class="top5-title">${esc(f.title)}</div>
|
|
7487
|
-
<div class="top5-detail"><strong
|
|
7488
|
-
<div class="top5-detail"><strong
|
|
7489
|
-
<div class="top5-detail"><strong
|
|
7490
|
-
<h4
|
|
7491
|
-
<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>
|
|
7492
7814
|
</div>
|
|
7493
7815
|
</div>`
|
|
7494
7816
|
).join("\n");
|
|
7495
7817
|
top5Html = `
|
|
7496
7818
|
<section>
|
|
7497
|
-
<h2
|
|
7819
|
+
<h2>${esc(t.topHighestRiskFindings(top5.length))}</h2>
|
|
7498
7820
|
${cards}
|
|
7499
7821
|
</section>`;
|
|
7500
7822
|
}
|
|
7501
7823
|
let findingsHtml;
|
|
7502
7824
|
if (summary.totalFindings === 0) {
|
|
7503
|
-
findingsHtml =
|
|
7825
|
+
findingsHtml = `<div class="no-findings">${esc(t.noIssuesFound)}</div>`;
|
|
7504
7826
|
} else {
|
|
7505
7827
|
const FOLD_THRESHOLD = 20;
|
|
7506
7828
|
const renderCard = (f) => {
|
|
@@ -7509,10 +7831,10 @@ function generateHtmlReport(scanResults, history) {
|
|
|
7509
7831
|
<span class="badge badge-${esc(sev)}">${esc(f.severity)}</span>
|
|
7510
7832
|
<span class="finding-title-text">${esc(f.title)}</span>
|
|
7511
7833
|
<span class="finding-resource">${esc(f.resourceArn || f.resourceId)}</span>
|
|
7512
|
-
<details><summary
|
|
7834
|
+
<details><summary>${t.details}</summary><div class="finding-card-body">
|
|
7513
7835
|
<p>${esc(f.description)}</p>
|
|
7514
|
-
<p><strong
|
|
7515
|
-
<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>
|
|
7516
7838
|
</div></details>
|
|
7517
7839
|
</div>`;
|
|
7518
7840
|
};
|
|
@@ -7523,7 +7845,7 @@ function generateHtmlReport(scanResults, history) {
|
|
|
7523
7845
|
const first = findings.slice(0, FOLD_THRESHOLD).map(renderCard).join("\n");
|
|
7524
7846
|
const rest = findings.slice(FOLD_THRESHOLD).map(renderCard).join("\n");
|
|
7525
7847
|
return `${first}
|
|
7526
|
-
<details><summary
|
|
7848
|
+
<details><summary>${t.showRemainingFindings(findings.length - FOLD_THRESHOLD)}</summary>
|
|
7527
7849
|
${rest}
|
|
7528
7850
|
</details>`;
|
|
7529
7851
|
};
|
|
@@ -7575,13 +7897,13 @@ ${rest}
|
|
|
7575
7897
|
if (history && history.length >= 2) {
|
|
7576
7898
|
trendHtml = `
|
|
7577
7899
|
<section class="trend-section">
|
|
7578
|
-
<h2
|
|
7900
|
+
<h2>${esc(t.trendTitle)}</h2>
|
|
7579
7901
|
<div class="trend-chart">
|
|
7580
|
-
<div class="trend-title"
|
|
7902
|
+
<div class="trend-title">${esc(t.findingsBySeverity)}</div>
|
|
7581
7903
|
${findingsTrendChart(history)}
|
|
7582
7904
|
</div>
|
|
7583
7905
|
<div class="trend-chart">
|
|
7584
|
-
<div class="trend-title"
|
|
7906
|
+
<div class="trend-title">${esc(t.securityScore)}</div>
|
|
7585
7907
|
${scoreTrendChart(history)}
|
|
7586
7908
|
</div>
|
|
7587
7909
|
</section>`;
|
|
@@ -7592,16 +7914,57 @@ ${rest}
|
|
|
7592
7914
|
let recsHtml = "";
|
|
7593
7915
|
if (summary.totalFindings > 0) {
|
|
7594
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."];
|
|
7595
7921
|
for (const f of allFindings) {
|
|
7596
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
|
+
}
|
|
7597
7948
|
const existing = recMap.get(rem);
|
|
7598
7949
|
if (existing) {
|
|
7599
7950
|
existing.count++;
|
|
7951
|
+
if (!existing.url && url) existing.url = url;
|
|
7600
7952
|
if (SEVERITY_ORDER2.indexOf(f.severity) < SEVERITY_ORDER2.indexOf(existing.severity)) {
|
|
7601
7953
|
existing.severity = f.severity;
|
|
7602
7954
|
}
|
|
7603
7955
|
} else {
|
|
7604
|
-
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;
|
|
7605
7968
|
}
|
|
7606
7969
|
}
|
|
7607
7970
|
const uniqueRecs = [...recMap.values()].sort((a, b) => {
|
|
@@ -7612,60 +7975,61 @@ ${rest}
|
|
|
7612
7975
|
const renderRec = (r) => {
|
|
7613
7976
|
const sev = r.severity.toLowerCase();
|
|
7614
7977
|
const countLabel = r.count > 1 ? ` (× ${r.count})` : "";
|
|
7615
|
-
|
|
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>`;
|
|
7616
7980
|
};
|
|
7617
7981
|
const TOP_N = 10;
|
|
7618
7982
|
const topItems = uniqueRecs.slice(0, TOP_N).map(renderRec).join("\n");
|
|
7619
7983
|
const remaining = uniqueRecs.slice(TOP_N);
|
|
7620
7984
|
const moreHtml = remaining.length > 0 ? `
|
|
7621
|
-
<details><summary
|
|
7985
|
+
<details><summary>${t.showMoreCount(remaining.length)}</summary>
|
|
7622
7986
|
${remaining.map(renderRec).join("\n")}
|
|
7623
7987
|
</details>` : "";
|
|
7624
7988
|
recsHtml = `
|
|
7625
7989
|
<details class="rec-fold">
|
|
7626
|
-
<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>
|
|
7627
7991
|
<div class="rec-body">
|
|
7628
7992
|
<ol>${topItems}${moreHtml}</ol>
|
|
7629
7993
|
</div>
|
|
7630
7994
|
</details>`;
|
|
7631
7995
|
}
|
|
7632
7996
|
return `<!DOCTYPE html>
|
|
7633
|
-
<html lang="
|
|
7997
|
+
<html lang="${htmlLang}">
|
|
7634
7998
|
<head>
|
|
7635
7999
|
<meta charset="UTF-8">
|
|
7636
8000
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
7637
|
-
<title
|
|
8001
|
+
<title>${esc(t.securityReportTitle)} — ${esc(date)}</title>
|
|
7638
8002
|
<style>${sharedCss()}</style>
|
|
7639
8003
|
</head>
|
|
7640
8004
|
<body>
|
|
7641
8005
|
<div class="container">
|
|
7642
8006
|
|
|
7643
8007
|
<header>
|
|
7644
|
-
<h1>🛡️
|
|
7645
|
-
<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>
|
|
7646
8010
|
</header>
|
|
7647
8011
|
|
|
7648
8012
|
<section class="summary">
|
|
7649
8013
|
<div class="score-card">
|
|
7650
8014
|
<div class="score-value" style="color:${scoreColor(score)}">${score}</div>
|
|
7651
|
-
<div class="score-label"
|
|
8015
|
+
<div class="score-label">${esc(t.securityScore)}</div>
|
|
7652
8016
|
</div>
|
|
7653
8017
|
<div class="severity-stats">
|
|
7654
|
-
<div class="stat-card stat-critical"><div class="stat-count">${summary.critical}</div><div class="stat-label"
|
|
7655
|
-
<div class="stat-card stat-high"><div class="stat-count">${summary.high}</div><div class="stat-label"
|
|
7656
|
-
<div class="stat-card stat-medium"><div class="stat-count">${summary.medium}</div><div class="stat-label"
|
|
7657
|
-
<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>
|
|
7658
8022
|
</div>
|
|
7659
8023
|
</section>
|
|
7660
8024
|
|
|
7661
8025
|
<section class="charts">
|
|
7662
8026
|
<div class="chart-box">
|
|
7663
|
-
<div class="chart-title"
|
|
8027
|
+
<div class="chart-title">${esc(t.severityDistribution)}</div>
|
|
7664
8028
|
<div style="text-align:center">${donutChart(summary)}</div>
|
|
7665
8029
|
</div>
|
|
7666
8030
|
<div class="chart-box">
|
|
7667
|
-
<div class="chart-title"
|
|
7668
|
-
${barChart(modules)}
|
|
8031
|
+
<div class="chart-title">${esc(t.findingsByModule)}</div>
|
|
8032
|
+
${barChart(modules, t.allModulesClean)}
|
|
7669
8033
|
</div>
|
|
7670
8034
|
</section>
|
|
7671
8035
|
|
|
@@ -7673,33 +8037,35 @@ ${trendHtml}
|
|
|
7673
8037
|
|
|
7674
8038
|
${top5Html}
|
|
7675
8039
|
|
|
7676
|
-
${buildServiceReminderHtml(modules)}
|
|
8040
|
+
${buildServiceReminderHtml(modules, lang)}
|
|
7677
8041
|
|
|
7678
8042
|
<section>
|
|
7679
|
-
<h2
|
|
8043
|
+
<h2>${esc(t.scanStatistics)}</h2>
|
|
7680
8044
|
<table>
|
|
7681
|
-
<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>
|
|
7682
8046
|
<tbody>${statsRows}</tbody>
|
|
7683
8047
|
</table>
|
|
7684
8048
|
</section>
|
|
7685
8049
|
|
|
7686
8050
|
<section>
|
|
7687
|
-
<h2
|
|
8051
|
+
<h2>${esc(t.allFindings)}</h2>
|
|
7688
8052
|
${findingsHtml}
|
|
7689
8053
|
</section>
|
|
7690
8054
|
|
|
7691
8055
|
${recsHtml}
|
|
7692
8056
|
|
|
7693
8057
|
<footer>
|
|
7694
|
-
<p
|
|
7695
|
-
<p
|
|
8058
|
+
<p>${esc(t.generatedBy)} v${VERSION}</p>
|
|
8059
|
+
<p>${esc(t.informationalOnly)}</p>
|
|
7696
8060
|
</footer>
|
|
7697
8061
|
|
|
7698
8062
|
</div>
|
|
7699
8063
|
</body>
|
|
7700
8064
|
</html>`;
|
|
7701
8065
|
}
|
|
7702
|
-
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";
|
|
7703
8069
|
const { accountId, region, scanStart } = scanResults;
|
|
7704
8070
|
const date = scanStart.split("T")[0];
|
|
7705
8071
|
const scanTime = scanStart.replace("T", " ").replace(/\.\d+Z$/, " UTC");
|
|
@@ -7716,17 +8082,21 @@ function generateMlps3HtmlReport(scanResults, history) {
|
|
|
7716
8082
|
if (history && history.length >= 2) {
|
|
7717
8083
|
trendHtml = `
|
|
7718
8084
|
<section class="trend-section">
|
|
7719
|
-
<h2
|
|
8085
|
+
<h2>${esc(t.trendTitle)}</h2>
|
|
7720
8086
|
<div class="trend-chart">
|
|
7721
|
-
<div class="trend-title"
|
|
8087
|
+
<div class="trend-title">${esc(t.findingsBySeverity)}</div>
|
|
7722
8088
|
${findingsTrendChart(history)}
|
|
7723
8089
|
</div>
|
|
7724
8090
|
<div class="trend-chart">
|
|
7725
|
-
<div class="trend-title"
|
|
8091
|
+
<div class="trend-title">${esc(t.securityScore)}</div>
|
|
7726
8092
|
${scoreTrendChart(history)}
|
|
7727
8093
|
</div>
|
|
7728
8094
|
</section>`;
|
|
7729
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;
|
|
7730
8100
|
const categoryMap = /* @__PURE__ */ new Map();
|
|
7731
8101
|
for (const r of results) {
|
|
7732
8102
|
if (r.status === "not_applicable") continue;
|
|
@@ -7735,7 +8105,7 @@ function generateMlps3HtmlReport(scanResults, history) {
|
|
|
7735
8105
|
categoryMap.get(cat).push(r);
|
|
7736
8106
|
}
|
|
7737
8107
|
const categorySections = MLPS3_CATEGORY_ORDER.map((category) => {
|
|
7738
|
-
const sectionTitle =
|
|
8108
|
+
const sectionTitle = t.mlpsCategorySection[category] ?? category;
|
|
7739
8109
|
const catResults = categoryMap.get(category);
|
|
7740
8110
|
if (!catResults || catResults.length === 0) return "";
|
|
7741
8111
|
const allCloud = catResults.every((r) => r.status === "cloud_provider");
|
|
@@ -7743,11 +8113,11 @@ function generateMlps3HtmlReport(scanResults, history) {
|
|
|
7743
8113
|
return `<details class="category-fold mlps-cloud-section">
|
|
7744
8114
|
<summary>
|
|
7745
8115
|
<span class="category-title">${esc(sectionTitle)}</span>
|
|
7746
|
-
<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>
|
|
7747
8117
|
</summary>
|
|
7748
8118
|
<div class="category-body">
|
|
7749
|
-
<div class="mlps-cloud-note"
|
|
7750
|
-
${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")}
|
|
7751
8121
|
</div>
|
|
7752
8122
|
</details>`;
|
|
7753
8123
|
}
|
|
@@ -7769,31 +8139,34 @@ function generateMlps3HtmlReport(scanResults, history) {
|
|
|
7769
8139
|
if (!controlMap.has(key)) controlMap.set(key, []);
|
|
7770
8140
|
controlMap.get(key).push(r);
|
|
7771
8141
|
}
|
|
7772
|
-
const controlGroups = [...controlMap.entries()].map(([
|
|
8142
|
+
const controlGroups = [...controlMap.entries()].map(([_controlKey, controlResults]) => {
|
|
8143
|
+
const controlName = itemControl(controlResults[0]);
|
|
7773
8144
|
const cloudItems = controlResults.filter((r) => r.status === "cloud_provider");
|
|
7774
8145
|
const nonCloudItems = controlResults.filter((r) => r.status !== "cloud_provider");
|
|
7775
8146
|
let itemsHtml = "";
|
|
7776
8147
|
for (const r of nonCloudItems) {
|
|
7777
8148
|
const icon = r.status === "clean" ? "\u{1F7E2}" : r.status === "issues" ? "\u{1F534}" : r.status === "unknown" ? "\u2B1C" : r.status === "manual" ? "\u{1F4CB}" : "\u{1F3E2}";
|
|
7778
8149
|
const cls = `check-${r.status === "cloud_provider" ? "cloud" : r.status}`;
|
|
7779
|
-
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)}` : "";
|
|
7780
8151
|
let findingsDetail = "";
|
|
7781
8152
|
if (r.status === "clean") {
|
|
7782
|
-
findingsDetail = `<div class="check-detail"
|
|
8153
|
+
findingsDetail = `<div class="check-detail">${esc(t.noRelatedIssues)}</div>`;
|
|
7783
8154
|
} else if (r.status === "issues" && r.relatedFindings.length > 0) {
|
|
7784
8155
|
const fItems = r.relatedFindings.slice(0, 5).map((f) => `<li>${esc(f.severity)}: ${esc(f.title)}</li>`);
|
|
7785
8156
|
if (r.relatedFindings.length > 5) {
|
|
7786
|
-
fItems.push(`<li
|
|
8157
|
+
fItems.push(`<li>${esc(t.andMore(r.relatedFindings.length - 5))}</li>`);
|
|
7787
8158
|
}
|
|
7788
|
-
const remediationHint = r.relatedFindings[0]?.remediationSteps?.[0] ? `<p style="color:#fbbf24;font-size:12px;margin-top:4px"
|
|
7789
|
-
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>`;
|
|
7790
8161
|
}
|
|
7791
|
-
|
|
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>
|
|
7792
8164
|
${findingsDetail}`;
|
|
7793
8165
|
}
|
|
7794
8166
|
if (cloudItems.length > 0) {
|
|
7795
8167
|
for (const r of cloudItems) {
|
|
7796
|
-
|
|
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>
|
|
7797
8170
|
`;
|
|
7798
8171
|
}
|
|
7799
8172
|
}
|
|
@@ -7826,20 +8199,61 @@ ${itemsHtml}
|
|
|
7826
8199
|
let remediationHtml = "";
|
|
7827
8200
|
if (failedResults.length > 0) {
|
|
7828
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."];
|
|
7829
8206
|
for (const r of failedResults) {
|
|
7830
8207
|
for (const f of r.relatedFindings) {
|
|
7831
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
|
+
}
|
|
7832
8234
|
const existing = mlpsRecMap.get(rem);
|
|
7833
8235
|
if (existing) {
|
|
7834
8236
|
existing.count++;
|
|
8237
|
+
if (!existing.url && url) existing.url = url;
|
|
7835
8238
|
if (SEVERITY_ORDER2.indexOf(f.severity) < SEVERITY_ORDER2.indexOf(existing.severity)) {
|
|
7836
8239
|
existing.severity = f.severity;
|
|
7837
8240
|
}
|
|
7838
8241
|
} else {
|
|
7839
|
-
mlpsRecMap.set(rem, { text: rem, severity: f.severity, count: 1 });
|
|
8242
|
+
mlpsRecMap.set(rem, { text: rem, severity: f.severity, count: 1, url });
|
|
7840
8243
|
}
|
|
7841
8244
|
}
|
|
7842
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
|
+
}
|
|
7843
8257
|
const mlpsUniqueRecs = [...mlpsRecMap.values()].sort((a, b) => {
|
|
7844
8258
|
const sevDiff = SEVERITY_ORDER2.indexOf(a.severity) - SEVERITY_ORDER2.indexOf(b.severity);
|
|
7845
8259
|
if (sevDiff !== 0) return sevDiff;
|
|
@@ -7849,26 +8263,27 @@ ${itemsHtml}
|
|
|
7849
8263
|
const renderMlpsRec = (r) => {
|
|
7850
8264
|
const sev = r.severity.toLowerCase();
|
|
7851
8265
|
const countLabel = r.count > 1 ? ` (× ${r.count})` : "";
|
|
7852
|
-
|
|
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>`;
|
|
7853
8268
|
};
|
|
7854
8269
|
const MLPS_TOP_N = 10;
|
|
7855
8270
|
const mlpsTopItems = mlpsUniqueRecs.slice(0, MLPS_TOP_N).map(renderMlpsRec).join("\n");
|
|
7856
8271
|
const mlpsRemaining = mlpsUniqueRecs.slice(MLPS_TOP_N);
|
|
7857
8272
|
const mlpsMoreHtml = mlpsRemaining.length > 0 ? `
|
|
7858
|
-
<details><summary
|
|
8273
|
+
<details><summary>${esc(t.showRemaining(mlpsRemaining.length))}…</summary>
|
|
7859
8274
|
${mlpsRemaining.map(renderMlpsRec).join("\n")}
|
|
7860
8275
|
</details>` : "";
|
|
7861
8276
|
remediationHtml = `
|
|
7862
8277
|
<details class="rec-fold" open>
|
|
7863
|
-
<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>
|
|
7864
8279
|
<div class="rec-body">
|
|
7865
8280
|
<ol>${mlpsTopItems}${mlpsMoreHtml}</ol>
|
|
7866
8281
|
</div>
|
|
7867
8282
|
</details>`;
|
|
7868
8283
|
}
|
|
7869
8284
|
}
|
|
7870
|
-
const naNote = naCount > 0 ? `<p style="color:#64748b;font-size:13px;margin-top:24px"
|
|
7871
|
-
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>` : "";
|
|
7872
8287
|
const mlpsCss = `
|
|
7873
8288
|
.mlps-cloud-section>summary{color:#94a3b8}
|
|
7874
8289
|
.mlps-cloud-note{color:#94a3b8;font-size:13px;margin-bottom:12px;font-style:italic}
|
|
@@ -7890,40 +8305,40 @@ ${mlpsRemaining.map(renderMlpsRec).join("\n")}
|
|
|
7890
8305
|
.mlps-summary-card .stat-label{font-size:12px;color:#94a3b8;margin-top:2px}
|
|
7891
8306
|
`;
|
|
7892
8307
|
return `<!DOCTYPE html>
|
|
7893
|
-
<html lang="
|
|
8308
|
+
<html lang="${htmlLang}">
|
|
7894
8309
|
<head>
|
|
7895
8310
|
<meta charset="UTF-8">
|
|
7896
8311
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
7897
|
-
<title
|
|
8312
|
+
<title>${esc(t.mlpsTitle)} — ${esc(date)}</title>
|
|
7898
8313
|
<style>${sharedCss()}${mlpsCss}</style>
|
|
7899
8314
|
</head>
|
|
7900
8315
|
<body>
|
|
7901
8316
|
<div class="container">
|
|
7902
8317
|
|
|
7903
8318
|
<header>
|
|
7904
|
-
<h1>🛡️
|
|
7905
|
-
<div class="disclaimer"
|
|
7906
|
-
<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>
|
|
7907
8322
|
</header>
|
|
7908
8323
|
|
|
7909
8324
|
<section class="summary" style="display:block;text-align:center">
|
|
7910
8325
|
<div style="font-size:36px;font-weight:700;margin-bottom:12px">
|
|
7911
|
-
<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>
|
|
7912
8327
|
<span style="color:#475569;margin:0 16px">/</span>
|
|
7913
|
-
<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>
|
|
7914
8329
|
</div>
|
|
7915
8330
|
<div class="mlps-summary-cards" style="justify-content:center">
|
|
7916
|
-
<div class="mlps-summary-card"><div class="stat-count" style="color:#60a5fa">${checkedTotal}</div><div class="stat-label"
|
|
7917
|
-
<div class="mlps-summary-card"><div class="stat-count" style="color:#94a3b8">${cloudCount}</div><div class="stat-label">\u{1F3E2}
|
|
7918
|
-
<div class="mlps-summary-card"><div class="stat-count" style="color:#eab308">${manualCount}</div><div class="stat-label">\u{1F4CB}
|
|
7919
|
-
${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>` : ""}
|
|
7920
8335
|
</div>
|
|
7921
8336
|
</section>
|
|
7922
8337
|
${unknownNote}
|
|
7923
8338
|
|
|
7924
8339
|
${trendHtml}
|
|
7925
8340
|
|
|
7926
|
-
${buildServiceReminderHtml(scanResults.modules)}
|
|
8341
|
+
${buildServiceReminderHtml(scanResults.modules, lang)}
|
|
7927
8342
|
|
|
7928
8343
|
${categorySections}
|
|
7929
8344
|
|
|
@@ -7932,8 +8347,8 @@ ${remediationHtml}
|
|
|
7932
8347
|
${naNote}
|
|
7933
8348
|
|
|
7934
8349
|
<footer>
|
|
7935
|
-
<p
|
|
7936
|
-
<p
|
|
8350
|
+
<p>${esc(t.mlpsFooterGenerated(VERSION))}</p>
|
|
8351
|
+
<p>${esc(t.mlpsFooterDisclaimer)}</p>
|
|
7937
8352
|
</footer>
|
|
7938
8353
|
|
|
7939
8354
|
</div>
|
|
@@ -8148,16 +8563,14 @@ Aggregates active findings from AWS Security Hub. Replaces individual config sca
|
|
|
8148
8563
|
- INFORMATIONAL findings are skipped.
|
|
8149
8564
|
|
|
8150
8565
|
## 3. GuardDuty Findings (guardduty_findings)
|
|
8151
|
-
|
|
8152
|
-
-
|
|
8153
|
-
-
|
|
8154
|
-
- 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.
|
|
8155
8569
|
|
|
8156
8570
|
## 4. Inspector Findings (inspector_findings)
|
|
8157
|
-
|
|
8158
|
-
-
|
|
8159
|
-
-
|
|
8160
|
-
- 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.
|
|
8161
8574
|
|
|
8162
8575
|
## 5. Trusted Advisor Findings (trusted_advisor_findings)
|
|
8163
8576
|
Aggregates security checks from AWS Trusted Advisor.
|
|
@@ -8193,19 +8606,15 @@ Finds unused/idle AWS resources (unattached EBS volumes, unused EIPs, stopped in
|
|
|
8193
8606
|
Assesses disaster recovery readiness \u2014 RDS Multi-AZ & backups, EBS snapshot coverage, S3 versioning & cross-region replication.
|
|
8194
8607
|
|
|
8195
8608
|
## 15. Config Rules Findings (config_rules_findings)
|
|
8196
|
-
|
|
8197
|
-
-
|
|
8198
|
-
-
|
|
8199
|
-
- Security-related rules (encryption, IAM, public access, etc.) mapped to HIGH severity (7.5).
|
|
8200
|
-
- 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.
|
|
8201
8612
|
- Gracefully handles regions where AWS Config is not enabled.
|
|
8202
8613
|
|
|
8203
8614
|
## 16. IAM Access Analyzer Findings (access_analyzer_findings)
|
|
8204
|
-
|
|
8205
|
-
-
|
|
8206
|
-
-
|
|
8207
|
-
- Covers S3 buckets, IAM roles, SQS queues, Lambda functions, KMS keys, and more.
|
|
8208
|
-
- 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.
|
|
8209
8618
|
- Returns warning if no analyzer is configured.
|
|
8210
8619
|
|
|
8211
8620
|
## 17. SSM Patch Compliance (patch_compliance_findings)
|
|
@@ -8290,112 +8699,18 @@ var MODULE_DESCRIPTIONS = {
|
|
|
8290
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.",
|
|
8291
8700
|
disaster_recovery: "Assesses disaster recovery readiness \u2014 RDS Multi-AZ & backups, EBS snapshot coverage, S3 versioning & cross-region replication.",
|
|
8292
8701
|
security_hub_findings: "Aggregates active findings from AWS Security Hub \u2014 replaces individual config scanners with centralized compliance checks.",
|
|
8293
|
-
guardduty_findings: "
|
|
8294
|
-
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.",
|
|
8295
8704
|
trusted_advisor_findings: "Aggregates security checks from AWS Trusted Advisor \u2014 requires Business or Enterprise Support plan.",
|
|
8296
|
-
config_rules_findings: "
|
|
8297
|
-
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.",
|
|
8298
8707
|
patch_compliance_findings: "Checks SSM Patch Manager compliance \u2014 managed instances with missing or failed security and system patches.",
|
|
8299
8708
|
imdsv2_enforcement: "Checks if EC2 instances enforce IMDSv2 (HttpTokens: required) \u2014 IMDSv1 allows credential theft via SSRF.",
|
|
8300
8709
|
waf_coverage: "Checks if internet-facing ALBs have WAF Web ACL associated for protection against common web exploits."
|
|
8301
8710
|
};
|
|
8302
|
-
|
|
8303
|
-
|
|
8304
|
-
|
|
8305
|
-
\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
8306
|
-
|
|
8307
|
-
\u4EE5\u4E0B\u4E8B\u9879\u9700\u8981\u4EBA\u5DE5\u786E\u8BA4\u548C\u6267\u884C\uFF1A
|
|
8308
|
-
|
|
8309
|
-
\u26A0\uFE0F \u5E94\u6025\u9694\u79BB/\u6B62\u8840\u65B9\u6848
|
|
8310
|
-
\u25A1 \u51C6\u5907\u4E13\u7528\u9694\u79BB\u5B89\u5168\u7EC4\uFF08\u65E0 Inbound/Outbound \u89C4\u5219\uFF09
|
|
8311
|
-
\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
|
|
8312
|
-
\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
|
|
8313
|
-
\u25A1 \u660E\u786E\u5404\u9879\u76EE\u8D26\u6237\u53CA\u8D44\u6E90\u7684\u8D1F\u8D23\u4EBA\u4E0E\u8054\u7CFB\u65B9\u5F0F
|
|
8314
|
-
|
|
8315
|
-
\u26A0\uFE0F \u6D4B\u8BD5/\u5F00\u53D1\u73AF\u5883\u5904\u7F6E
|
|
8316
|
-
\u25A1 \u975E\u6838\u5FC3\u7CFB\u7EDF\u5728\u62A4\u7F51\u671F\u95F4\u5173\u95ED
|
|
8317
|
-
\u25A1 \u6D4B\u8BD5/\u5F00\u53D1\u73AF\u5883\u5173\u95ED\u6216\u4E0E\u751F\u4EA7\u4FDD\u6301\u540C\u7B49\u5B89\u5168\u57FA\u7EBF
|
|
8318
|
-
\u25A1 \u786E\u8BA4\u54EA\u4E9B\u73AF\u5883\u53EF\u4EE5\u7D27\u6025\u5173\u505C\uFF0C\u907F\u514D\u653B\u51FB\u6269\u6563
|
|
8319
|
-
|
|
8320
|
-
\u26A0\uFE0F \u503C\u5B88\u56E2\u961F\u7EC4\u5EFA
|
|
8321
|
-
\u25A1 7\xD724 \u76D1\u63A7\u5FEB\u901F\u54CD\u5E94\u56E2\u961F
|
|
8322
|
-
\u25A1 \u6280\u672F\u4E0E\u98CE\u9669\u5206\u6790\u7EC4
|
|
8323
|
-
\u25A1 \u5B89\u5168\u7B56\u7565\u4E0B\u53D1\u7EC4
|
|
8324
|
-
\u25A1 \u4E1A\u52A1\u54CD\u5E94\u7EC4
|
|
8325
|
-
\u25A1 \u660E\u786E AWS TAM/Support \u8054\u7CFB\u65B9\u5F0F\uFF08ES/EOP \u5BA2\u6237\uFF09
|
|
8326
|
-
|
|
8327
|
-
\u26A0\uFE0F \u51FA\u5165\u7AD9\u8DEF\u5F84\u67B6\u6784\u56FE
|
|
8328
|
-
\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
|
|
8329
|
-
\u25A1 \u660E\u786E\u5404 ELB/Public EC2/S3/DX \u7684\u6570\u636E\u6D41\u5411
|
|
8330
|
-
\u25A1 \u8BC6\u522B\u6240\u6709\u9762\u5411\u4E92\u8054\u7F51\u7684\u6570\u636E\u4EA4\u4E92\u63A5\u53E3
|
|
8331
|
-
|
|
8332
|
-
\u26A0\uFE0F \u4E3B\u52A8\u5F0F\u6E17\u900F\u6D4B\u8BD5
|
|
8333
|
-
\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
|
|
8334
|
-
\u25A1 \u57FA\u4E8E\u6E17\u900F\u6D4B\u8BD5\u62A5\u544A\u8FDB\u884C\u6B63\u5F0F\u62A4\u7F51\u524D\u7684\u5B89\u5168\u52A0\u56FA
|
|
8335
|
-
\u25A1 \u5173\u6CE8 AWS \u5B89\u5168\u516C\u544A\uFF08\u5DF2\u77E5\u6F0F\u6D1E\u4E0E\u8865\u4E01\uFF09
|
|
8336
|
-
|
|
8337
|
-
\u26A0\uFE0F WAR-ROOM \u5B9E\u65F6\u6C9F\u901A
|
|
8338
|
-
\u25A1 \u521B\u5EFA\u62A4\u7F51\u671F\u95F4\u4E13\u7528\u6C9F\u901A\u6E20\u9053\uFF08\u4F01\u5FAE/\u9489\u9489/\u98DE\u4E66/Chime\uFF09
|
|
8339
|
-
\u25A1 \u4E0E AWS TAM \u5EFA\u7ACB WAR-ROOM \u8054\u7CFB\uFF08\u4F01\u4E1A\u7EA7\u652F\u6301\u5BA2\u6237\uFF09
|
|
8340
|
-
\u25A1 \u7EDF\u4E00\u6848\u4F8B\u6807\u9898\u683C\u5F0F\uFF1A"\u3010\u62A4\u7F51\u3011+ \u95EE\u9898\u63CF\u8FF0"
|
|
8341
|
-
|
|
8342
|
-
\u26A0\uFE0F \u5BC6\u7801\u4E0E\u51ED\u8BC1\u7BA1\u7406
|
|
8343
|
-
\u25A1 \u6240\u6709 IAM \u7528\u6237\u7ED1\u5B9A MFA
|
|
8344
|
-
\u25A1 AKSK \u8F6E\u8F6C\u5468\u671F \u2264 90 \u5929
|
|
8345
|
-
\u25A1 \u907F\u514D\u5171\u4EAB\u8D26\u6237\u4F7F\u7528
|
|
8346
|
-
\u25A1 S3/Lambda/\u5E94\u7528\u4EE3\u7801\u4E2D\u65E0\u660E\u6587\u5BC6\u7801
|
|
8347
|
-
|
|
8348
|
-
\u26A0\uFE0F \u62A4\u7F51\u540E\u4F18\u5316
|
|
8349
|
-
\u25A1 \u9488\u5BF9\u653B\u51FB\u62A5\u544A\u9010\u9879\u5E94\u7B54\u4E0E\u4FEE\u590D
|
|
8350
|
-
\u25A1 \u4E0E\u5B89\u5168\u56E2\u961F\u5EFA\u7ACB\u5468\u671F\u6027\u5B89\u5168\u7EF4\u62A4\u6D41\u7A0B
|
|
8351
|
-
\u25A1 \u6301\u7EED\u8865\u5168\u5B89\u5168\u98CE\u9669
|
|
8352
|
-
|
|
8353
|
-
\u53C2\u8003\uFF1AAWS \u62A4\u7F51\u884C\u52A8 Standard Operation Procedure (Compliance IEM)
|
|
8354
|
-
`;
|
|
8355
|
-
var SERVICE_RECOMMENDATIONS2 = {
|
|
8356
|
-
security_hub_findings: {
|
|
8357
|
-
icon: "\u{1F534}",
|
|
8358
|
-
service: "Security Hub",
|
|
8359
|
-
impact: "\u65E0\u6CD5\u83B7\u53D6 300+ \u9879\u81EA\u52A8\u5316\u5B89\u5168\u68C0\u67E5\uFF08FSBP/CIS/PCI DSS \u6807\u51C6\uFF09",
|
|
8360
|
-
action: "\u542F\u7528 Security Hub \u83B7\u5F97\u6700\u5168\u9762\u7684\u5B89\u5168\u6001\u52BF\u8BC4\u4F30"
|
|
8361
|
-
},
|
|
8362
|
-
guardduty_findings: {
|
|
8363
|
-
icon: "\u{1F534}",
|
|
8364
|
-
service: "GuardDuty",
|
|
8365
|
-
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",
|
|
8366
|
-
action: "\u542F\u7528 GuardDuty \u83B7\u5F97\u6301\u7EED\u5A01\u80C1\u68C0\u6D4B\u80FD\u529B"
|
|
8367
|
-
},
|
|
8368
|
-
inspector_findings: {
|
|
8369
|
-
icon: "\u{1F7E1}",
|
|
8370
|
-
service: "Inspector",
|
|
8371
|
-
impact: "\u65E0\u6CD5\u626B\u63CF EC2/Lambda/\u5BB9\u5668\u7684\u8F6F\u4EF6\u6F0F\u6D1E\uFF08CVE\uFF09",
|
|
8372
|
-
action: "\u542F\u7528 Inspector \u53D1\u73B0\u5DF2\u77E5\u5B89\u5168\u6F0F\u6D1E"
|
|
8373
|
-
},
|
|
8374
|
-
trusted_advisor_findings: {
|
|
8375
|
-
icon: "\u{1F7E1}",
|
|
8376
|
-
service: "Trusted Advisor",
|
|
8377
|
-
impact: "\u65E0\u6CD5\u83B7\u53D6 AWS \u6700\u4F73\u5B9E\u8DF5\u5B89\u5168\u68C0\u67E5",
|
|
8378
|
-
action: "\u5347\u7EA7\u81F3 Business/Enterprise Support \u8BA1\u5212\u4EE5\u4F7F\u7528 Trusted Advisor \u5B89\u5168\u68C0\u67E5"
|
|
8379
|
-
},
|
|
8380
|
-
config_rules_findings: {
|
|
8381
|
-
icon: "\u{1F7E1}",
|
|
8382
|
-
service: "AWS Config",
|
|
8383
|
-
impact: "\u65E0\u6CD5\u68C0\u67E5\u8D44\u6E90\u914D\u7F6E\u5408\u89C4\u72B6\u6001",
|
|
8384
|
-
action: "\u542F\u7528 AWS Config \u5E76\u914D\u7F6E Config Rules"
|
|
8385
|
-
},
|
|
8386
|
-
access_analyzer_findings: {
|
|
8387
|
-
icon: "\u{1F7E1}",
|
|
8388
|
-
service: "IAM Access Analyzer",
|
|
8389
|
-
impact: "\u65E0\u6CD5\u68C0\u6D4B\u8D44\u6E90\u662F\u5426\u88AB\u5916\u90E8\u8D26\u53F7\u6216\u516C\u7F51\u8BBF\u95EE",
|
|
8390
|
-
action: "\u521B\u5EFA IAM Access Analyzer\uFF08\u8D26\u6237\u7EA7\u6216\u7EC4\u7EC7\u7EA7\uFF09"
|
|
8391
|
-
},
|
|
8392
|
-
patch_compliance_findings: {
|
|
8393
|
-
icon: "\u{1F7E1}",
|
|
8394
|
-
service: "SSM Patch Manager",
|
|
8395
|
-
impact: "\u65E0\u6CD5\u68C0\u67E5\u5B9E\u4F8B\u8865\u4E01\u5408\u89C4\u72B6\u6001",
|
|
8396
|
-
action: "\u5B89\u88C5 SSM Agent \u5E76\u914D\u7F6E Patch Manager"
|
|
8397
|
-
}
|
|
8398
|
-
};
|
|
8711
|
+
function getHwDefenseChecklist(lang) {
|
|
8712
|
+
return getI18n(lang ?? "zh").hwChecklist;
|
|
8713
|
+
}
|
|
8399
8714
|
var SERVICE_NOT_ENABLED_PATTERNS2 = [
|
|
8400
8715
|
"not enabled",
|
|
8401
8716
|
"not found",
|
|
@@ -8405,10 +8720,11 @@ var SERVICE_NOT_ENABLED_PATTERNS2 = [
|
|
|
8405
8720
|
"not available",
|
|
8406
8721
|
"is not enabled"
|
|
8407
8722
|
];
|
|
8408
|
-
function buildServiceReminder(modules) {
|
|
8723
|
+
function buildServiceReminder(modules, lang) {
|
|
8724
|
+
const t = getI18n(lang ?? "zh");
|
|
8409
8725
|
const disabledServices = [];
|
|
8410
8726
|
for (const mod of modules) {
|
|
8411
|
-
const rec =
|
|
8727
|
+
const rec = t.serviceRecommendations[mod.module];
|
|
8412
8728
|
if (!rec) continue;
|
|
8413
8729
|
if (!mod.warnings?.length) continue;
|
|
8414
8730
|
const hasNotEnabled = mod.warnings.some(
|
|
@@ -8421,26 +8737,26 @@ function buildServiceReminder(modules) {
|
|
|
8421
8737
|
if (disabledServices.length === 0) return "";
|
|
8422
8738
|
const lines = [
|
|
8423
8739
|
"",
|
|
8424
|
-
|
|
8740
|
+
t.serviceReminderTitle,
|
|
8425
8741
|
""
|
|
8426
8742
|
];
|
|
8427
8743
|
for (const svc of disabledServices) {
|
|
8428
|
-
lines.push(`${svc.icon} ${svc.service}
|
|
8429
|
-
lines.push(`
|
|
8430
|
-
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}`);
|
|
8431
8747
|
lines.push("");
|
|
8432
8748
|
}
|
|
8433
|
-
lines.push(
|
|
8749
|
+
lines.push(t.serviceReminderFooter);
|
|
8434
8750
|
return lines.join("\n");
|
|
8435
8751
|
}
|
|
8436
|
-
function summarizeResult(result) {
|
|
8752
|
+
function summarizeResult(result, lang) {
|
|
8437
8753
|
const { summary } = result;
|
|
8438
8754
|
const lines = [
|
|
8439
8755
|
`Scan complete for account ${result.accountId} in ${result.region}.`,
|
|
8440
8756
|
`Total findings: ${summary.totalFindings} (${summary.critical} Critical, ${summary.high} High, ${summary.medium} Medium, ${summary.low} Low)`,
|
|
8441
8757
|
`Modules: ${summary.modulesSuccess} succeeded, ${summary.modulesError} errored`
|
|
8442
8758
|
];
|
|
8443
|
-
const reminder = buildServiceReminder(result.modules);
|
|
8759
|
+
const reminder = buildServiceReminder(result.modules, lang);
|
|
8444
8760
|
if (reminder) {
|
|
8445
8761
|
lines.push(reminder);
|
|
8446
8762
|
}
|
|
@@ -8502,9 +8818,10 @@ function createServer(defaultRegion) {
|
|
|
8502
8818
|
region: z.string().optional().describe("AWS region to scan (default: server region)"),
|
|
8503
8819
|
org_mode: z.boolean().optional().describe("Enable multi-account scanning via AWS Organizations"),
|
|
8504
8820
|
role_name: z.string().optional().describe("IAM role name to assume in child accounts (default: AWSSecurityMCPAudit)"),
|
|
8505
|
-
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)")
|
|
8506
8823
|
},
|
|
8507
|
-
async ({ region, org_mode, role_name, account_ids }) => {
|
|
8824
|
+
async ({ region, org_mode, role_name, account_ids, lang }) => {
|
|
8508
8825
|
try {
|
|
8509
8826
|
const r = region ?? defaultRegion;
|
|
8510
8827
|
let result;
|
|
@@ -8519,7 +8836,7 @@ function createServer(defaultRegion) {
|
|
|
8519
8836
|
}
|
|
8520
8837
|
return {
|
|
8521
8838
|
content: [
|
|
8522
|
-
{ type: "text", text: summarizeResult(result) },
|
|
8839
|
+
{ type: "text", text: summarizeResult(result, lang ?? "zh") },
|
|
8523
8840
|
{ type: "text", text: JSON.stringify(result, null, 2) }
|
|
8524
8841
|
]
|
|
8525
8842
|
};
|
|
@@ -8580,9 +8897,10 @@ function createServer(defaultRegion) {
|
|
|
8580
8897
|
region: z.string().optional().describe("AWS region to scan (default: server region)"),
|
|
8581
8898
|
org_mode: z.boolean().optional().describe("Enable multi-account scanning via AWS Organizations"),
|
|
8582
8899
|
role_name: z.string().optional().describe("IAM role name to assume in child accounts (default: AWSSecurityMCPAudit)"),
|
|
8583
|
-
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)")
|
|
8584
8902
|
},
|
|
8585
|
-
async ({ group, region, org_mode, role_name, account_ids }) => {
|
|
8903
|
+
async ({ group, region, org_mode, role_name, account_ids, lang }) => {
|
|
8586
8904
|
try {
|
|
8587
8905
|
const groupDef = SCAN_GROUPS[group];
|
|
8588
8906
|
if (!groupDef) {
|
|
@@ -8664,7 +8982,7 @@ function createServer(defaultRegion) {
|
|
|
8664
8982
|
`Scan group: ${groupDef.name} (${group})`,
|
|
8665
8983
|
groupDef.description,
|
|
8666
8984
|
"",
|
|
8667
|
-
summarizeResult(result)
|
|
8985
|
+
summarizeResult(result, lang ?? "zh")
|
|
8668
8986
|
];
|
|
8669
8987
|
if (missingModules.length > 0) {
|
|
8670
8988
|
lines.push("");
|
|
@@ -8677,7 +8995,7 @@ function createServer(defaultRegion) {
|
|
|
8677
8995
|
if (group === "hw_defense") {
|
|
8678
8996
|
const summaryContent = content[0];
|
|
8679
8997
|
if (summaryContent && summaryContent.type === "text") {
|
|
8680
|
-
summaryContent.text += "\n\n" +
|
|
8998
|
+
summaryContent.text += "\n\n" + getHwDefenseChecklist(lang ?? "zh");
|
|
8681
8999
|
}
|
|
8682
9000
|
}
|
|
8683
9001
|
return { content };
|
|
@@ -8707,11 +9025,14 @@ function createServer(defaultRegion) {
|
|
|
8707
9025
|
server.tool(
|
|
8708
9026
|
"generate_report",
|
|
8709
9027
|
"Generate a Markdown security report from scan results. Read-only. Does not modify any AWS resources.",
|
|
8710
|
-
{
|
|
8711
|
-
|
|
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 }) => {
|
|
8712
9033
|
try {
|
|
8713
9034
|
const parsed = JSON.parse(scan_results);
|
|
8714
|
-
const report = generateMarkdownReport(parsed);
|
|
9035
|
+
const report = generateMarkdownReport(parsed, lang ?? "zh");
|
|
8715
9036
|
return { content: [{ type: "text", text: report }] };
|
|
8716
9037
|
} catch (err) {
|
|
8717
9038
|
return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
@@ -8721,11 +9042,14 @@ function createServer(defaultRegion) {
|
|
|
8721
9042
|
server.tool(
|
|
8722
9043
|
"generate_mlps3_report",
|
|
8723
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.",
|
|
8724
|
-
{
|
|
8725
|
-
|
|
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 }) => {
|
|
8726
9050
|
try {
|
|
8727
9051
|
const parsed = JSON.parse(scan_results);
|
|
8728
|
-
const report = generateMlps3Report(parsed);
|
|
9052
|
+
const report = generateMlps3Report(parsed, lang ?? "zh");
|
|
8729
9053
|
return { content: [{ type: "text", text: report }] };
|
|
8730
9054
|
} catch (err) {
|
|
8731
9055
|
return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
@@ -8737,13 +9061,14 @@ function createServer(defaultRegion) {
|
|
|
8737
9061
|
"Generate a professional HTML security report. Save the output as an .html file.",
|
|
8738
9062
|
{
|
|
8739
9063
|
scan_results: z.string().describe("JSON string of FullScanResult from scan_all"),
|
|
8740
|
-
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)")
|
|
8741
9066
|
},
|
|
8742
|
-
async ({ scan_results, history }) => {
|
|
9067
|
+
async ({ scan_results, history, lang }) => {
|
|
8743
9068
|
try {
|
|
8744
9069
|
const parsed = JSON.parse(scan_results);
|
|
8745
9070
|
const historyData = history ? JSON.parse(history) : void 0;
|
|
8746
|
-
const report = generateHtmlReport(parsed, historyData);
|
|
9071
|
+
const report = generateHtmlReport(parsed, historyData, lang ?? "zh");
|
|
8747
9072
|
return { content: [{ type: "text", text: report }] };
|
|
8748
9073
|
} catch (err) {
|
|
8749
9074
|
return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
@@ -8755,13 +9080,14 @@ function createServer(defaultRegion) {
|
|
|
8755
9080
|
"Generate a professional HTML MLPS Level 3 compliance report (\u7B49\u4FDD\u4E09\u7EA7). Save as .html file.",
|
|
8756
9081
|
{
|
|
8757
9082
|
scan_results: z.string().describe("JSON string of FullScanResult from scan_group mlps3_precheck or scan_all"),
|
|
8758
|
-
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)")
|
|
8759
9085
|
},
|
|
8760
|
-
async ({ scan_results, history }) => {
|
|
9086
|
+
async ({ scan_results, history, lang }) => {
|
|
8761
9087
|
try {
|
|
8762
9088
|
const parsed = JSON.parse(scan_results);
|
|
8763
9089
|
const historyData = history ? JSON.parse(history) : void 0;
|
|
8764
|
-
const report = generateMlps3HtmlReport(parsed, historyData);
|
|
9090
|
+
const report = generateMlps3HtmlReport(parsed, historyData, lang ?? "zh");
|
|
8765
9091
|
return { content: [{ type: "text", text: report }] };
|
|
8766
9092
|
} catch (err) {
|
|
8767
9093
|
return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
@@ -8771,7 +9097,10 @@ function createServer(defaultRegion) {
|
|
|
8771
9097
|
server.tool(
|
|
8772
9098
|
"generate_maturity_report",
|
|
8773
9099
|
"Generate a security maturity assessment report from scan_all results. Requires service_detection module output. Read-only.",
|
|
8774
|
-
{
|
|
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
|
+
},
|
|
8775
9104
|
async ({ scan_results }) => {
|
|
8776
9105
|
try {
|
|
8777
9106
|
const parsed = JSON.parse(scan_results);
|
|
@@ -9042,7 +9371,7 @@ ${finding}`
|
|
|
9042
9371
|
type: "text",
|
|
9043
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
|
|
9044
9373
|
|
|
9045
|
-
${
|
|
9374
|
+
${getHwDefenseChecklist("zh")}
|
|
9046
9375
|
|
|
9047
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`
|
|
9048
9377
|
}
|