aws-security-mcp 0.6.2 → 0.7.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/dashboard/dist/assets/index-AKJ_-GfD.js +46 -0
- package/dashboard/dist/assets/index-UN8P_PO6.css +2 -0
- package/dashboard/dist/data.json +293 -105
- package/dashboard/dist/index.html +2 -2
- package/dist/bin/aws-security-mcp.js +116 -25
- package/dist/bin/aws-security-mcp.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +116 -25
- package/dist/src/index.js.map +1 -1
- package/package.json +1 -1
- package/dashboard/dist/assets/index-BYE-UdjR.js +0 -46
- package/dashboard/dist/assets/index-CQyERuqT.css +0 -2
|
@@ -237,7 +237,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
237
237
|
import { z } from "zod";
|
|
238
238
|
|
|
239
239
|
// src/version.ts
|
|
240
|
-
var VERSION = "0.
|
|
240
|
+
var VERSION = "0.7.0";
|
|
241
241
|
|
|
242
242
|
// src/utils/aws-client.ts
|
|
243
243
|
import { STSClient, GetCallerIdentityCommand } from "@aws-sdk/client-sts";
|
|
@@ -2867,6 +2867,22 @@ import {
|
|
|
2867
2867
|
SecurityHubClient as SecurityHubClient2,
|
|
2868
2868
|
GetFindingsCommand
|
|
2869
2869
|
} from "@aws-sdk/client-securityhub";
|
|
2870
|
+
|
|
2871
|
+
// src/utils/sh-source.ts
|
|
2872
|
+
function getSecurityHubSource(finding) {
|
|
2873
|
+
const impact = finding.impact ?? "";
|
|
2874
|
+
const match = impact.match(/^Source:\s*([^(]+)/);
|
|
2875
|
+
if (!match) return "Other";
|
|
2876
|
+
const product = match[1].trim();
|
|
2877
|
+
if (product === "Security Hub" || product.includes("Foundational")) return "FSBP";
|
|
2878
|
+
if (product === "Inspector" || product.includes("Inspector")) return "Inspector";
|
|
2879
|
+
if (product === "GuardDuty" || product.includes("GuardDuty")) return "GuardDuty";
|
|
2880
|
+
if (product === "Config" || product.includes("Config")) return "Config";
|
|
2881
|
+
if (product === "IAM Access Analyzer" || product.includes("Access Analyzer")) return "Access Analyzer";
|
|
2882
|
+
return "Other";
|
|
2883
|
+
}
|
|
2884
|
+
|
|
2885
|
+
// src/scanners/security-hub-findings.ts
|
|
2870
2886
|
function shSeverityToScore(label) {
|
|
2871
2887
|
switch (label) {
|
|
2872
2888
|
case "CRITICAL":
|
|
@@ -2936,7 +2952,7 @@ var SecurityHubFindingsScanner = class {
|
|
|
2936
2952
|
if (recText && !["See References", "None Provided", ""].includes(recText.trim())) {
|
|
2937
2953
|
remediationSteps.push(recText);
|
|
2938
2954
|
}
|
|
2939
|
-
|
|
2955
|
+
const finding = {
|
|
2940
2956
|
severity,
|
|
2941
2957
|
title: f.Title ?? "Security Hub Finding",
|
|
2942
2958
|
resourceType,
|
|
@@ -2950,7 +2966,9 @@ var SecurityHubFindingsScanner = class {
|
|
|
2950
2966
|
priority: priorityFromSeverity(severity),
|
|
2951
2967
|
module: this.moduleName,
|
|
2952
2968
|
accountId: f.AwsAccountId ?? accountId
|
|
2953
|
-
}
|
|
2969
|
+
};
|
|
2970
|
+
finding.source = getSecurityHubSource(finding);
|
|
2971
|
+
findings.push(finding);
|
|
2954
2972
|
}
|
|
2955
2973
|
nextToken = resp.NextToken;
|
|
2956
2974
|
} while (nextToken);
|
|
@@ -3874,6 +3892,12 @@ var zhI18n = {
|
|
|
3874
3892
|
trendTitle: "30\u65E5\u8D8B\u52BF",
|
|
3875
3893
|
findingsBySeverity: "\u6309\u4E25\u91CD\u6027\u5206\u7C7B\u7684\u53D1\u73B0",
|
|
3876
3894
|
showMoreCount: (n) => `\u663E\u793A\u5269\u4F59 ${n} \u9879\u2026`,
|
|
3895
|
+
// Filter toolbar
|
|
3896
|
+
filterSeverity: "\u4E25\u91CD\u6027\uFF1A",
|
|
3897
|
+
filterModule: "\u6A21\u5757\uFF1A",
|
|
3898
|
+
filterAll: "\u5168\u90E8",
|
|
3899
|
+
filterAllModules: "\u5168\u90E8\u6A21\u5757",
|
|
3900
|
+
filterCountTpl: "\u663E\u793A {shown} / {total} \u4E2A\u53D1\u73B0",
|
|
3877
3901
|
// Extended — MLPS extras
|
|
3878
3902
|
// Markdown report
|
|
3879
3903
|
executiveSummary: "\u6267\u884C\u6458\u8981",
|
|
@@ -4144,6 +4168,12 @@ var enI18n = {
|
|
|
4144
4168
|
trendTitle: "30-Day Trends",
|
|
4145
4169
|
findingsBySeverity: "Findings by Severity",
|
|
4146
4170
|
showMoreCount: (n) => `Show ${n} more\u2026`,
|
|
4171
|
+
// Filter toolbar
|
|
4172
|
+
filterSeverity: "Severity:",
|
|
4173
|
+
filterModule: "Module:",
|
|
4174
|
+
filterAll: "All",
|
|
4175
|
+
filterAllModules: "All Modules",
|
|
4176
|
+
filterCountTpl: "Showing {shown} / {total} findings",
|
|
4147
4177
|
// Extended \u2014 MLPS extras
|
|
4148
4178
|
// Markdown report
|
|
4149
4179
|
executiveSummary: "Executive Summary",
|
|
@@ -7262,18 +7292,6 @@ var SEVERITY_ORDER2 = ["CRITICAL", "HIGH", "MEDIUM", "LOW"];
|
|
|
7262
7292
|
function getRecommendationTemplate(rem) {
|
|
7263
7293
|
return rem.replace(/\b(i-[0-9a-f]+)\b/g, "{instance}").replace(/\b(vol-[0-9a-f]+)\b/g, "{volume}").replace(/\b(sg-[0-9a-f]+)\b/g, "{sg}").replace(/\b(eipalloc-[0-9a-f]+)\b/g, "{eip}").replace(/\b(arn:aws[-\w]*:[^"\s]+)\b/g, "{arn}").replace(/"[^"]+"/g, "{name}").replace(/bucket \S+/g, "bucket {name}").replace(/instance \S+/g, "instance {id}").replace(/volume \S+/g, "volume {id}").replace(/rule \S+/g, "rule {name}");
|
|
7264
7294
|
}
|
|
7265
|
-
function getSecurityHubSource(finding) {
|
|
7266
|
-
const impact = finding.impact ?? "";
|
|
7267
|
-
const match = impact.match(/^Source:\s*([^(]+)/);
|
|
7268
|
-
if (!match) return "Other";
|
|
7269
|
-
const product = match[1].trim();
|
|
7270
|
-
if (product === "Security Hub" || product.includes("Foundational")) return "FSBP";
|
|
7271
|
-
if (product === "Inspector" || product.includes("Inspector")) return "Inspector";
|
|
7272
|
-
if (product === "GuardDuty" || product.includes("GuardDuty")) return "GuardDuty";
|
|
7273
|
-
if (product === "Config" || product.includes("Config")) return "Config";
|
|
7274
|
-
if (product === "IAM Access Analyzer" || product.includes("Access Analyzer")) return "Access Analyzer";
|
|
7275
|
-
return "Other";
|
|
7276
|
-
}
|
|
7277
7295
|
var SECURITY_HUB_SUB_CAT_ORDER = ["FSBP", "Inspector", "GuardDuty", "Config", "Access Analyzer", "Other"];
|
|
7278
7296
|
function scoreColor(score) {
|
|
7279
7297
|
if (score >= 80) return "#22c55e";
|
|
@@ -7459,7 +7477,17 @@ function sharedCss() {
|
|
|
7459
7477
|
.rec-body ol{padding-left:24px}
|
|
7460
7478
|
.rec-body li{margin-bottom:8px;color:#cbd5e1;font-size:13px}
|
|
7461
7479
|
.rec-body .badge{margin-right:6px;vertical-align:middle}
|
|
7480
|
+
.filter-toolbar{display:flex;flex-wrap:wrap;gap:16px;align-items:center;margin-bottom:20px;padding:12px 16px;background:#1e293b;border:1px solid #334155;border-radius:8px}
|
|
7481
|
+
.filter-group{display:flex;align-items:center;gap:8px}
|
|
7482
|
+
.filter-label{color:#94a3b8;font-size:13px}
|
|
7483
|
+
.filter-btn{padding:4px 12px;border-radius:4px;border:1px solid #475569;background:transparent;color:#cbd5e1;cursor:pointer;font-size:13px}
|
|
7484
|
+
.filter-btn:hover{background:#334155}
|
|
7485
|
+
.filter-btn.active{background:#3b82f6;border-color:#3b82f6;color:#fff}
|
|
7486
|
+
.filter-select{padding:4px 8px;border-radius:4px;border:1px solid #475569;background:#0f172a;color:#cbd5e1;font-size:13px}
|
|
7487
|
+
.filter-count{color:#64748b;font-size:13px;margin-left:auto}
|
|
7462
7488
|
@media print{
|
|
7489
|
+
.filter-toolbar{display:none !important}
|
|
7490
|
+
.finding-card,.module-fold{display:block !important}
|
|
7463
7491
|
body{background:#fff;color:#1e293b;-webkit-print-color-adjust:exact;print-color-adjust:exact}
|
|
7464
7492
|
.container{max-width:100%;padding:20px}
|
|
7465
7493
|
.card,.score-card,.stat-card,.chart-box,.finding-fold,.top5-card,.trend-chart,.category-fold,.module-fold,.finding-card,.rec-fold{background:#fff;border:1px solid #e2e8f0}
|
|
@@ -7718,13 +7746,14 @@ function generateHtmlReport(scanResults, history, lang) {
|
|
|
7718
7746
|
</section>`;
|
|
7719
7747
|
}
|
|
7720
7748
|
let findingsHtml;
|
|
7749
|
+
let filterToolbarHtml = "";
|
|
7721
7750
|
if (summary.totalFindings === 0) {
|
|
7722
7751
|
findingsHtml = `<div class="no-findings">${esc(t.noIssuesFound)}</div>`;
|
|
7723
7752
|
} else {
|
|
7724
7753
|
const FOLD_THRESHOLD = 20;
|
|
7725
|
-
const renderCard = (f) => {
|
|
7754
|
+
const renderCard = (f, moduleKey) => {
|
|
7726
7755
|
const sev = f.severity.toLowerCase();
|
|
7727
|
-
return `<div class="finding-card sev-${esc(sev)}">
|
|
7756
|
+
return `<div class="finding-card sev-${esc(sev)}" data-severity="${esc(f.severity)}" data-module="${esc(moduleKey)}">
|
|
7728
7757
|
<span class="badge badge-${esc(sev)}">${esc(f.severity)}</span>
|
|
7729
7758
|
<span class="finding-title-text">${esc(f.title)}</span>
|
|
7730
7759
|
<span class="finding-resource">${esc(f.resourceArn || f.resourceId)}</span>
|
|
@@ -7735,12 +7764,12 @@ function generateHtmlReport(scanResults, history, lang) {
|
|
|
7735
7764
|
</div></details>
|
|
7736
7765
|
</div>`;
|
|
7737
7766
|
};
|
|
7738
|
-
const renderCards = (findings) => {
|
|
7767
|
+
const renderCards = (findings, moduleKey) => {
|
|
7739
7768
|
if (findings.length <= FOLD_THRESHOLD) {
|
|
7740
|
-
return findings.map(renderCard).join("\n");
|
|
7769
|
+
return findings.map((f) => renderCard(f, moduleKey)).join("\n");
|
|
7741
7770
|
}
|
|
7742
|
-
const first = findings.slice(0, FOLD_THRESHOLD).map(renderCard).join("\n");
|
|
7743
|
-
const rest = findings.slice(FOLD_THRESHOLD).map(renderCard).join("\n");
|
|
7771
|
+
const first = findings.slice(0, FOLD_THRESHOLD).map((f) => renderCard(f, moduleKey)).join("\n");
|
|
7772
|
+
const rest = findings.slice(FOLD_THRESHOLD).map((f) => renderCard(f, moduleKey)).join("\n");
|
|
7744
7773
|
return `${first}
|
|
7745
7774
|
<details><summary>${t.showRemainingFindings(findings.length - FOLD_THRESHOLD)}</summary>
|
|
7746
7775
|
${rest}
|
|
@@ -7775,7 +7804,7 @@ ${rest}
|
|
|
7775
7804
|
if (aHasCritHigh !== bHasCritHigh) return aHasCritHigh ? -1 : 1;
|
|
7776
7805
|
return b[1].length - a[1].length;
|
|
7777
7806
|
});
|
|
7778
|
-
const renderSeverityGroups = (findings) => {
|
|
7807
|
+
const renderSeverityGroups = (findings, moduleKey) => {
|
|
7779
7808
|
return SEVERITY_ORDER2.map((sev) => {
|
|
7780
7809
|
const sevFindings = findings.filter((f) => f.severity === sev);
|
|
7781
7810
|
if (sevFindings.length === 0) return "";
|
|
@@ -7784,7 +7813,7 @@ ${rest}
|
|
|
7784
7813
|
const label = sev.charAt(0) + sev.slice(1).toLowerCase();
|
|
7785
7814
|
return `<details class="severity-group-fold">
|
|
7786
7815
|
<summary><h4>${emoji} ${label} (${sevFindings.length})</h4></summary>
|
|
7787
|
-
${renderCards(sevFindings)}
|
|
7816
|
+
${renderCards(sevFindings, moduleKey)}
|
|
7788
7817
|
</details>`;
|
|
7789
7818
|
}).filter(Boolean).join("\n");
|
|
7790
7819
|
};
|
|
@@ -7796,16 +7825,38 @@ ${rest}
|
|
|
7796
7825
|
findingsHtml = moduleEntries.map(([modName, modFindings, subCatLabel]) => {
|
|
7797
7826
|
const badges = renderModuleBadges(modFindings);
|
|
7798
7827
|
const displayName = subCatLabel ?? (t.moduleNames[modName] ?? modName);
|
|
7799
|
-
return `<details class="module-fold">
|
|
7828
|
+
return `<details class="module-fold" data-module="${esc(modName)}">
|
|
7800
7829
|
<summary>
|
|
7801
7830
|
<h3>🔒 ${esc(displayName)} (${modFindings.length})</h3>
|
|
7802
7831
|
<span class="module-badges">${badges}</span>
|
|
7803
7832
|
</summary>
|
|
7804
7833
|
<div class="module-body">
|
|
7805
|
-
${renderSeverityGroups(modFindings)}
|
|
7834
|
+
${renderSeverityGroups(modFindings, modName)}
|
|
7806
7835
|
</div>
|
|
7807
7836
|
</details>`;
|
|
7808
7837
|
}).join("\n");
|
|
7838
|
+
const moduleOptions = moduleEntries.map(([modKey, , subCatLabel]) => {
|
|
7839
|
+
const label = subCatLabel ?? (t.moduleNames[modKey] ?? modKey);
|
|
7840
|
+
return `<option value="${esc(modKey)}">${esc(label)}</option>`;
|
|
7841
|
+
}).join("\n ");
|
|
7842
|
+
filterToolbarHtml = `<div class="filter-toolbar" id="filterBar">
|
|
7843
|
+
<div class="filter-group">
|
|
7844
|
+
<span class="filter-label">${esc(t.filterSeverity)}</span>
|
|
7845
|
+
<button class="filter-btn active" data-severity="ALL">${esc(t.filterAll)}</button>
|
|
7846
|
+
<button class="filter-btn" data-severity="CRITICAL">Critical</button>
|
|
7847
|
+
<button class="filter-btn" data-severity="HIGH">High</button>
|
|
7848
|
+
<button class="filter-btn" data-severity="MEDIUM">Medium</button>
|
|
7849
|
+
<button class="filter-btn" data-severity="LOW">Low</button>
|
|
7850
|
+
</div>
|
|
7851
|
+
<div class="filter-group">
|
|
7852
|
+
<span class="filter-label">${esc(t.filterModule)}</span>
|
|
7853
|
+
<select class="filter-select" id="moduleFilter">
|
|
7854
|
+
<option value="ALL">${esc(t.filterAllModules)}</option>
|
|
7855
|
+
${moduleOptions}
|
|
7856
|
+
</select>
|
|
7857
|
+
</div>
|
|
7858
|
+
<div class="filter-count" id="filterCount" data-tpl="${esc(t.filterCountTpl)}"></div>
|
|
7859
|
+
</div>`;
|
|
7809
7860
|
}
|
|
7810
7861
|
let trendHtml = "";
|
|
7811
7862
|
if (history && history.length >= 2) {
|
|
@@ -7961,6 +8012,44 @@ ${remaining.map(renderRec).join("\n")}
|
|
|
7961
8012
|
</div>
|
|
7962
8013
|
</details>`;
|
|
7963
8014
|
}
|
|
8015
|
+
const filterScript = summary.totalFindings > 0 ? `<script>
|
|
8016
|
+
(function(){
|
|
8017
|
+
var activeSev='ALL',activeMod='ALL';
|
|
8018
|
+
var countEl=document.getElementById('filterCount');
|
|
8019
|
+
var tpl=countEl?countEl.getAttribute('data-tpl'):'';
|
|
8020
|
+
function apply(){
|
|
8021
|
+
var cards=document.querySelectorAll('.finding-card[data-severity]');
|
|
8022
|
+
var shown=0,total=cards.length;
|
|
8023
|
+
cards.forEach(function(c){
|
|
8024
|
+
var sevOk=activeSev==='ALL'||c.getAttribute('data-severity')===activeSev;
|
|
8025
|
+
var modOk=activeMod==='ALL'||c.getAttribute('data-module')===activeMod;
|
|
8026
|
+
c.style.display=(sevOk&&modOk)?'':'none';
|
|
8027
|
+
if(sevOk&&modOk)shown++;
|
|
8028
|
+
});
|
|
8029
|
+
if(countEl)countEl.textContent=tpl.replace('{shown}',shown).replace('{total}',total);
|
|
8030
|
+
document.querySelectorAll('.module-fold').forEach(function(f){
|
|
8031
|
+
var mod=f.getAttribute('data-module');
|
|
8032
|
+
if(activeMod!=='ALL'&&mod!==activeMod){f.style.display='none';return;}
|
|
8033
|
+
var hasVisible=f.querySelectorAll('.finding-card:not([style*="display: none"])').length>0;
|
|
8034
|
+
f.style.display=hasVisible?'':'none';
|
|
8035
|
+
});
|
|
8036
|
+
document.querySelectorAll('.severity-group-fold').forEach(function(g){
|
|
8037
|
+
g.style.display=g.querySelectorAll('.finding-card:not([style*="display: none"])').length?'':'none';
|
|
8038
|
+
});
|
|
8039
|
+
}
|
|
8040
|
+
document.querySelectorAll('.filter-btn[data-severity]').forEach(function(b){
|
|
8041
|
+
b.addEventListener('click',function(){
|
|
8042
|
+
document.querySelectorAll('.filter-btn[data-severity]').forEach(function(x){x.classList.remove('active')});
|
|
8043
|
+
b.classList.add('active');
|
|
8044
|
+
activeSev=b.getAttribute('data-severity');
|
|
8045
|
+
apply();
|
|
8046
|
+
});
|
|
8047
|
+
});
|
|
8048
|
+
var sel=document.getElementById('moduleFilter');
|
|
8049
|
+
if(sel)sel.addEventListener('change',function(){activeMod=sel.value;apply();});
|
|
8050
|
+
apply();
|
|
8051
|
+
})();
|
|
8052
|
+
</script>` : "";
|
|
7964
8053
|
return `<!DOCTYPE html>
|
|
7965
8054
|
<html lang="${htmlLang}">
|
|
7966
8055
|
<head>
|
|
@@ -8017,6 +8106,7 @@ ${buildServiceReminderHtml(modules, lang)}
|
|
|
8017
8106
|
|
|
8018
8107
|
<section>
|
|
8019
8108
|
<h2>${esc(t.allFindings)}</h2>
|
|
8109
|
+
${filterToolbarHtml}
|
|
8020
8110
|
${findingsHtml}
|
|
8021
8111
|
</section>
|
|
8022
8112
|
|
|
@@ -8028,6 +8118,7 @@ ${recsHtml}
|
|
|
8028
8118
|
</footer>
|
|
8029
8119
|
|
|
8030
8120
|
</div>
|
|
8121
|
+
${filterScript}
|
|
8031
8122
|
</body>
|
|
8032
8123
|
</html>`;
|
|
8033
8124
|
}
|