aws-security-mcp 0.1.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 +235 -0
- package/dashboard/dist/assets/index-DCplBiuM.css +2 -0
- package/dashboard/dist/assets/index-jFq0Af8S.js +46 -0
- package/dashboard/dist/data.json +926 -0
- package/dashboard/dist/favicon.svg +1 -0
- package/dashboard/dist/icons.svg +24 -0
- package/dashboard/dist/index.html +14 -0
- package/dist/bin/aws-security-mcp.d.ts +1 -0
- package/dist/bin/aws-security-mcp.js +16057 -0
- package/dist/bin/aws-security-mcp.js.map +1 -0
- package/dist/src/commands/dashboard.d.ts +3 -0
- package/dist/src/commands/dashboard.js +90 -0
- package/dist/src/commands/dashboard.js.map +1 -0
- package/dist/src/commands/deploy-dashboard.d.ts +3 -0
- package/dist/src/commands/deploy-dashboard.js +117 -0
- package/dist/src/commands/deploy-dashboard.js.map +1 -0
- package/dist/src/index.d.ts +96 -0
- package/dist/src/index.js +15765 -0
- package/dist/src/index.js.map +1 -0
- package/package.json +65 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// src/commands/dashboard.ts
|
|
2
|
+
import { createServer } from "http";
|
|
3
|
+
import { readFile } from "fs/promises";
|
|
4
|
+
import { join, extname, resolve } from "path";
|
|
5
|
+
import { existsSync, copyFileSync } from "fs";
|
|
6
|
+
import { fileURLToPath } from "url";
|
|
7
|
+
import { exec } from "child_process";
|
|
8
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
var __dirname = join(__filename, "..");
|
|
10
|
+
var MIME_TYPES = {
|
|
11
|
+
".html": "text/html",
|
|
12
|
+
".js": "text/javascript",
|
|
13
|
+
".css": "text/css",
|
|
14
|
+
".json": "application/json",
|
|
15
|
+
".svg": "image/svg+xml",
|
|
16
|
+
".png": "image/png",
|
|
17
|
+
".ico": "image/x-icon",
|
|
18
|
+
".woff": "font/woff",
|
|
19
|
+
".woff2": "font/woff2"
|
|
20
|
+
};
|
|
21
|
+
function startDashboard(port = 3e3) {
|
|
22
|
+
const dashboardDir = join(__dirname, "../../dashboard/dist");
|
|
23
|
+
if (!existsSync(dashboardDir)) {
|
|
24
|
+
console.error(
|
|
25
|
+
`Dashboard not built. Run "npm run build:dashboard" first.
|
|
26
|
+
Expected: ${dashboardDir}`
|
|
27
|
+
);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
const dataSource = join(
|
|
31
|
+
process.env.HOME || process.env.USERPROFILE || "~",
|
|
32
|
+
".aws-security/dashboard/data.json"
|
|
33
|
+
);
|
|
34
|
+
const dataDest = join(dashboardDir, "data.json");
|
|
35
|
+
if (existsSync(dataSource)) {
|
|
36
|
+
copyFileSync(dataSource, dataDest);
|
|
37
|
+
console.log(`Loaded scan data from ${dataSource}`);
|
|
38
|
+
} else {
|
|
39
|
+
console.log(
|
|
40
|
+
"No scan data found at ~/.aws-security/dashboard/data.json \u2014 using bundled sample data"
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
const resolvedBase = resolve(dashboardDir);
|
|
44
|
+
const server = createServer(async (req, res) => {
|
|
45
|
+
const url = req.url?.split("?")[0] ?? "/";
|
|
46
|
+
let filePath = resolve(
|
|
47
|
+
join(dashboardDir, url === "/" ? "index.html" : url)
|
|
48
|
+
);
|
|
49
|
+
if (!filePath.startsWith(resolvedBase + "/") && filePath !== resolvedBase) {
|
|
50
|
+
res.writeHead(403);
|
|
51
|
+
res.end("Forbidden");
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (!existsSync(filePath)) {
|
|
55
|
+
filePath = join(dashboardDir, "index.html");
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
const content = await readFile(filePath);
|
|
59
|
+
const ext = extname(filePath);
|
|
60
|
+
res.writeHead(200, {
|
|
61
|
+
"Content-Type": MIME_TYPES[ext] || "application/octet-stream"
|
|
62
|
+
});
|
|
63
|
+
res.end(content);
|
|
64
|
+
} catch {
|
|
65
|
+
res.writeHead(404);
|
|
66
|
+
res.end("Not found");
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
server.listen(port, () => {
|
|
70
|
+
const url = `http://localhost:${port}`;
|
|
71
|
+
console.log(`
|
|
72
|
+
AWS Security Dashboard: ${url}
|
|
73
|
+
`);
|
|
74
|
+
console.log("Press Ctrl+C to stop.\n");
|
|
75
|
+
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
76
|
+
exec(`${cmd} ${url}`, () => {
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
server.on("error", (err) => {
|
|
80
|
+
if (err.code === "EADDRINUSE") {
|
|
81
|
+
console.error(`Port ${port} is already in use. Try --port <other>`);
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
throw err;
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
export {
|
|
88
|
+
startDashboard
|
|
89
|
+
};
|
|
90
|
+
//# sourceMappingURL=dashboard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/dashboard.ts"],"sourcesContent":["import { createServer } from \"node:http\";\nimport { readFile } from \"node:fs/promises\";\nimport { join, extname, resolve, normalize } from \"node:path\";\nimport { existsSync, copyFileSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport { exec } from \"node:child_process\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = join(__filename, \"..\");\n\nconst MIME_TYPES: Record<string, string> = {\n \".html\": \"text/html\",\n \".js\": \"text/javascript\",\n \".css\": \"text/css\",\n \".json\": \"application/json\",\n \".svg\": \"image/svg+xml\",\n \".png\": \"image/png\",\n \".ico\": \"image/x-icon\",\n \".woff\": \"font/woff\",\n \".woff2\": \"font/woff2\",\n};\n\nexport function startDashboard(port = 3000): void {\n // dashboard/dist is two levels up from dist/src/commands/\n const dashboardDir = join(__dirname, \"../../dashboard/dist\");\n\n if (!existsSync(dashboardDir)) {\n console.error(\n `Dashboard not built. Run \"npm run build:dashboard\" first.\\n` +\n `Expected: ${dashboardDir}`,\n );\n process.exit(1);\n }\n\n // Copy real data.json if available\n const dataSource = join(\n process.env.HOME || process.env.USERPROFILE || \"~\",\n \".aws-security/dashboard/data.json\",\n );\n const dataDest = join(dashboardDir, \"data.json\");\n if (existsSync(dataSource)) {\n copyFileSync(dataSource, dataDest);\n console.log(`Loaded scan data from ${dataSource}`);\n } else {\n console.log(\n \"No scan data found at ~/.aws-security/dashboard/data.json — using bundled sample data\",\n );\n }\n\n const resolvedBase = resolve(dashboardDir);\n\n const server = createServer(async (req, res) => {\n const url = req.url?.split(\"?\")[0] ?? \"/\";\n let filePath = resolve(\n join(dashboardDir, url === \"/\" ? \"index.html\" : url),\n );\n\n // Ensure the resolved path is within dashboardDir\n if (!filePath.startsWith(resolvedBase + \"/\") && filePath !== resolvedBase) {\n res.writeHead(403);\n res.end(\"Forbidden\");\n return;\n }\n\n // SPA fallback: if file doesn't exist and isn't a static asset, serve index.html\n if (!existsSync(filePath)) {\n filePath = join(dashboardDir, \"index.html\");\n }\n\n try {\n const content = await readFile(filePath);\n const ext = extname(filePath);\n res.writeHead(200, {\n \"Content-Type\": MIME_TYPES[ext] || \"application/octet-stream\",\n });\n res.end(content);\n } catch {\n res.writeHead(404);\n res.end(\"Not found\");\n }\n });\n\n server.listen(port, () => {\n const url = `http://localhost:${port}`;\n console.log(`\\nAWS Security Dashboard: ${url}\\n`);\n console.log(\"Press Ctrl+C to stop.\\n\");\n\n // Try to open browser\n const cmd =\n process.platform === \"darwin\"\n ? \"open\"\n : process.platform === \"win32\"\n ? \"start\"\n : \"xdg-open\";\n exec(`${cmd} ${url}`, () => {\n // ignore errors — browser open is best-effort\n });\n });\n\n server.on(\"error\", (err: NodeJS.ErrnoException) => {\n if (err.code === \"EADDRINUSE\") {\n console.error(`Port ${port} is already in use. Try --port <other>`);\n process.exit(1);\n }\n throw err;\n });\n}\n"],"mappings":";AAAA,SAAS,oBAAoB;AAC7B,SAAS,gBAAgB;AACzB,SAAS,MAAM,SAAS,eAA0B;AAClD,SAAS,YAAY,oBAAoB;AACzC,SAAS,qBAAqB;AAC9B,SAAS,YAAY;AAErB,IAAM,aAAa,cAAc,YAAY,GAAG;AAChD,IAAM,YAAY,KAAK,YAAY,IAAI;AAEvC,IAAM,aAAqC;AAAA,EACzC,SAAS;AAAA,EACT,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,UAAU;AACZ;AAEO,SAAS,eAAe,OAAO,KAAY;AAEhD,QAAM,eAAe,KAAK,WAAW,sBAAsB;AAE3D,MAAI,CAAC,WAAW,YAAY,GAAG;AAC7B,YAAQ;AAAA,MACN;AAAA,YACe,YAAY;AAAA,IAC7B;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,aAAa;AAAA,IACjB,QAAQ,IAAI,QAAQ,QAAQ,IAAI,eAAe;AAAA,IAC/C;AAAA,EACF;AACA,QAAM,WAAW,KAAK,cAAc,WAAW;AAC/C,MAAI,WAAW,UAAU,GAAG;AAC1B,iBAAa,YAAY,QAAQ;AACjC,YAAQ,IAAI,yBAAyB,UAAU,EAAE;AAAA,EACnD,OAAO;AACL,YAAQ;AAAA,MACN;AAAA,IACF;AAAA,EACF;AAEA,QAAM,eAAe,QAAQ,YAAY;AAEzC,QAAM,SAAS,aAAa,OAAO,KAAK,QAAQ;AAC9C,UAAM,MAAM,IAAI,KAAK,MAAM,GAAG,EAAE,CAAC,KAAK;AACtC,QAAI,WAAW;AAAA,MACb,KAAK,cAAc,QAAQ,MAAM,eAAe,GAAG;AAAA,IACrD;AAGA,QAAI,CAAC,SAAS,WAAW,eAAe,GAAG,KAAK,aAAa,cAAc;AACzE,UAAI,UAAU,GAAG;AACjB,UAAI,IAAI,WAAW;AACnB;AAAA,IACF;AAGA,QAAI,CAAC,WAAW,QAAQ,GAAG;AACzB,iBAAW,KAAK,cAAc,YAAY;AAAA,IAC5C;AAEA,QAAI;AACF,YAAM,UAAU,MAAM,SAAS,QAAQ;AACvC,YAAM,MAAM,QAAQ,QAAQ;AAC5B,UAAI,UAAU,KAAK;AAAA,QACjB,gBAAgB,WAAW,GAAG,KAAK;AAAA,MACrC,CAAC;AACD,UAAI,IAAI,OAAO;AAAA,IACjB,QAAQ;AACN,UAAI,UAAU,GAAG;AACjB,UAAI,IAAI,WAAW;AAAA,IACrB;AAAA,EACF,CAAC;AAED,SAAO,OAAO,MAAM,MAAM;AACxB,UAAM,MAAM,oBAAoB,IAAI;AACpC,YAAQ,IAAI;AAAA,0BAA6B,GAAG;AAAA,CAAI;AAChD,YAAQ,IAAI,yBAAyB;AAGrC,UAAM,MACJ,QAAQ,aAAa,WACjB,SACA,QAAQ,aAAa,UACnB,UACA;AACR,SAAK,GAAG,GAAG,IAAI,GAAG,IAAI,MAAM;AAAA,IAE5B,CAAC;AAAA,EACH,CAAC;AAED,SAAO,GAAG,SAAS,CAAC,QAA+B;AACjD,QAAI,IAAI,SAAS,cAAc;AAC7B,cAAQ,MAAM,QAAQ,IAAI,wCAAwC;AAClE,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,UAAM;AAAA,EACR,CAAC;AACH;","names":[]}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// src/commands/deploy-dashboard.ts
|
|
2
|
+
import { readdirSync, readFileSync, existsSync, copyFileSync } from "fs";
|
|
3
|
+
import { join, extname, relative } from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
import {
|
|
6
|
+
S3Client,
|
|
7
|
+
PutObjectCommand,
|
|
8
|
+
PutBucketWebsiteCommand,
|
|
9
|
+
PutBucketPolicyCommand
|
|
10
|
+
} from "@aws-sdk/client-s3";
|
|
11
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
var __dirname = join(__filename, "..");
|
|
13
|
+
var CONTENT_TYPES = {
|
|
14
|
+
".html": "text/html",
|
|
15
|
+
".js": "text/javascript",
|
|
16
|
+
".css": "text/css",
|
|
17
|
+
".json": "application/json",
|
|
18
|
+
".svg": "image/svg+xml",
|
|
19
|
+
".png": "image/png",
|
|
20
|
+
".ico": "image/x-icon",
|
|
21
|
+
".woff": "font/woff",
|
|
22
|
+
".woff2": "font/woff2"
|
|
23
|
+
};
|
|
24
|
+
function collectFiles(dir) {
|
|
25
|
+
const files = [];
|
|
26
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
27
|
+
const full = join(dir, entry.name);
|
|
28
|
+
if (entry.isDirectory()) {
|
|
29
|
+
files.push(...collectFiles(full));
|
|
30
|
+
} else {
|
|
31
|
+
files.push(full);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return files;
|
|
35
|
+
}
|
|
36
|
+
async function deployDashboard(bucket, region) {
|
|
37
|
+
const dashboardDir = join(__dirname, "../../dashboard/dist");
|
|
38
|
+
if (!existsSync(dashboardDir)) {
|
|
39
|
+
console.error(
|
|
40
|
+
`Dashboard not built. Run "npm run build:dashboard" first.
|
|
41
|
+
Expected: ${dashboardDir}`
|
|
42
|
+
);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
const dataSource = join(
|
|
46
|
+
process.env.HOME || process.env.USERPROFILE || "~",
|
|
47
|
+
".aws-security/dashboard/data.json"
|
|
48
|
+
);
|
|
49
|
+
const dataDest = join(dashboardDir, "data.json");
|
|
50
|
+
if (existsSync(dataSource)) {
|
|
51
|
+
copyFileSync(dataSource, dataDest);
|
|
52
|
+
console.log(`Loaded scan data from ${dataSource}`);
|
|
53
|
+
} else {
|
|
54
|
+
console.log(
|
|
55
|
+
"No scan data at ~/.aws-security/dashboard/data.json \u2014 deploying with bundled sample data"
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
const s3 = new S3Client({ region });
|
|
59
|
+
console.log(`Configuring s3://${bucket} for static website hosting...`);
|
|
60
|
+
await s3.send(
|
|
61
|
+
new PutBucketWebsiteCommand({
|
|
62
|
+
Bucket: bucket,
|
|
63
|
+
WebsiteConfiguration: {
|
|
64
|
+
IndexDocument: { Suffix: "index.html" },
|
|
65
|
+
ErrorDocument: { Key: "index.html" }
|
|
66
|
+
// SPA fallback
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
);
|
|
70
|
+
const partition = region.startsWith("cn-") ? "aws-cn" : "aws";
|
|
71
|
+
console.log(`Setting public read bucket policy on s3://${bucket}...`);
|
|
72
|
+
await s3.send(
|
|
73
|
+
new PutBucketPolicyCommand({
|
|
74
|
+
Bucket: bucket,
|
|
75
|
+
Policy: JSON.stringify({
|
|
76
|
+
Version: "2012-10-17",
|
|
77
|
+
Statement: [
|
|
78
|
+
{
|
|
79
|
+
Sid: "PublicReadGetObject",
|
|
80
|
+
Effect: "Allow",
|
|
81
|
+
Principal: "*",
|
|
82
|
+
Action: "s3:GetObject",
|
|
83
|
+
Resource: `arn:${partition}:s3:::${bucket}/*`
|
|
84
|
+
}
|
|
85
|
+
]
|
|
86
|
+
})
|
|
87
|
+
})
|
|
88
|
+
);
|
|
89
|
+
const files = collectFiles(dashboardDir);
|
|
90
|
+
console.log(`Uploading ${files.length} files to s3://${bucket}...`);
|
|
91
|
+
for (const filePath of files) {
|
|
92
|
+
const key = relative(dashboardDir, filePath);
|
|
93
|
+
const ext = extname(filePath);
|
|
94
|
+
const contentType = CONTENT_TYPES[ext] || "application/octet-stream";
|
|
95
|
+
const body = readFileSync(filePath);
|
|
96
|
+
await s3.send(
|
|
97
|
+
new PutObjectCommand({
|
|
98
|
+
Bucket: bucket,
|
|
99
|
+
Key: key,
|
|
100
|
+
Body: body,
|
|
101
|
+
ContentType: contentType
|
|
102
|
+
})
|
|
103
|
+
);
|
|
104
|
+
console.log(` ${key}`);
|
|
105
|
+
}
|
|
106
|
+
const websiteUrl = `http://${bucket}.s3-website-${region}.amazonaws.com`;
|
|
107
|
+
console.log(`
|
|
108
|
+
Dashboard deployed successfully!`);
|
|
109
|
+
console.log(`Website URL: ${websiteUrl}`);
|
|
110
|
+
console.log(
|
|
111
|
+
"\nNote: Ensure S3 Block Public Access is disabled on this bucket for the website to be accessible.\n"
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
export {
|
|
115
|
+
deployDashboard
|
|
116
|
+
};
|
|
117
|
+
//# sourceMappingURL=deploy-dashboard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/deploy-dashboard.ts"],"sourcesContent":["import { readdirSync, readFileSync, statSync, existsSync, copyFileSync } from \"node:fs\";\nimport { join, extname, relative } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport {\n S3Client,\n PutObjectCommand,\n PutBucketWebsiteCommand,\n PutBucketPolicyCommand,\n} from \"@aws-sdk/client-s3\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = join(__filename, \"..\");\n\nconst CONTENT_TYPES: Record<string, string> = {\n \".html\": \"text/html\",\n \".js\": \"text/javascript\",\n \".css\": \"text/css\",\n \".json\": \"application/json\",\n \".svg\": \"image/svg+xml\",\n \".png\": \"image/png\",\n \".ico\": \"image/x-icon\",\n \".woff\": \"font/woff\",\n \".woff2\": \"font/woff2\",\n};\n\nfunction collectFiles(dir: string): string[] {\n const files: string[] = [];\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n const full = join(dir, entry.name);\n if (entry.isDirectory()) {\n files.push(...collectFiles(full));\n } else {\n files.push(full);\n }\n }\n return files;\n}\n\nexport async function deployDashboard(\n bucket: string,\n region: string,\n): Promise<void> {\n const dashboardDir = join(__dirname, \"../../dashboard/dist\");\n\n if (!existsSync(dashboardDir)) {\n console.error(\n `Dashboard not built. Run \"npm run build:dashboard\" first.\\n` +\n `Expected: ${dashboardDir}`,\n );\n process.exit(1);\n }\n\n // Copy real data.json if available\n const dataSource = join(\n process.env.HOME || process.env.USERPROFILE || \"~\",\n \".aws-security/dashboard/data.json\",\n );\n const dataDest = join(dashboardDir, \"data.json\");\n if (existsSync(dataSource)) {\n copyFileSync(dataSource, dataDest);\n console.log(`Loaded scan data from ${dataSource}`);\n } else {\n console.log(\n \"No scan data at ~/.aws-security/dashboard/data.json — deploying with bundled sample data\",\n );\n }\n\n const s3 = new S3Client({ region });\n\n // Configure static website hosting\n console.log(`Configuring s3://${bucket} for static website hosting...`);\n await s3.send(\n new PutBucketWebsiteCommand({\n Bucket: bucket,\n WebsiteConfiguration: {\n IndexDocument: { Suffix: \"index.html\" },\n ErrorDocument: { Key: \"index.html\" }, // SPA fallback\n },\n }),\n );\n\n // Set bucket policy for public read access\n const partition = region.startsWith(\"cn-\") ? \"aws-cn\" : \"aws\";\n console.log(`Setting public read bucket policy on s3://${bucket}...`);\n await s3.send(\n new PutBucketPolicyCommand({\n Bucket: bucket,\n Policy: JSON.stringify({\n Version: \"2012-10-17\",\n Statement: [\n {\n Sid: \"PublicReadGetObject\",\n Effect: \"Allow\",\n Principal: \"*\",\n Action: \"s3:GetObject\",\n Resource: `arn:${partition}:s3:::${bucket}/*`,\n },\n ],\n }),\n }),\n );\n\n // Upload all files\n const files = collectFiles(dashboardDir);\n console.log(`Uploading ${files.length} files to s3://${bucket}...`);\n\n for (const filePath of files) {\n const key = relative(dashboardDir, filePath);\n const ext = extname(filePath);\n const contentType = CONTENT_TYPES[ext] || \"application/octet-stream\";\n const body = readFileSync(filePath);\n\n await s3.send(\n new PutObjectCommand({\n Bucket: bucket,\n Key: key,\n Body: body,\n ContentType: contentType,\n }),\n );\n console.log(` ${key}`);\n }\n\n const websiteUrl = `http://${bucket}.s3-website-${region}.amazonaws.com`;\n console.log(`\\nDashboard deployed successfully!`);\n console.log(`Website URL: ${websiteUrl}`);\n console.log(\n \"\\nNote: Ensure S3 Block Public Access is disabled on this bucket for the website to be accessible.\\n\",\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
|
|
3
|
+
type Severity = "CRITICAL" | "HIGH" | "MEDIUM" | "LOW";
|
|
4
|
+
type Priority = "P0" | "P1" | "P2" | "P3";
|
|
5
|
+
interface Finding {
|
|
6
|
+
severity: Severity;
|
|
7
|
+
title: string;
|
|
8
|
+
resourceType: string;
|
|
9
|
+
resourceId: string;
|
|
10
|
+
resourceArn: string;
|
|
11
|
+
region: string;
|
|
12
|
+
description: string;
|
|
13
|
+
impact: string;
|
|
14
|
+
riskScore: number;
|
|
15
|
+
remediationSteps: string[];
|
|
16
|
+
priority: Priority;
|
|
17
|
+
module?: string;
|
|
18
|
+
}
|
|
19
|
+
interface ScanResult {
|
|
20
|
+
module: string;
|
|
21
|
+
status: "success" | "error";
|
|
22
|
+
error?: string;
|
|
23
|
+
warnings?: string[];
|
|
24
|
+
resourcesScanned: number;
|
|
25
|
+
findingsCount: number;
|
|
26
|
+
scanTimeMs: number;
|
|
27
|
+
findings: Finding[];
|
|
28
|
+
}
|
|
29
|
+
interface ScanContext {
|
|
30
|
+
region: string;
|
|
31
|
+
partition: string;
|
|
32
|
+
accountId: string;
|
|
33
|
+
}
|
|
34
|
+
interface FullScanResult {
|
|
35
|
+
scanStart: string;
|
|
36
|
+
scanEnd: string;
|
|
37
|
+
region: string;
|
|
38
|
+
accountId: string;
|
|
39
|
+
modules: ScanResult[];
|
|
40
|
+
summary: {
|
|
41
|
+
totalFindings: number;
|
|
42
|
+
critical: number;
|
|
43
|
+
high: number;
|
|
44
|
+
medium: number;
|
|
45
|
+
low: number;
|
|
46
|
+
modulesSuccess: number;
|
|
47
|
+
modulesError: number;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
interface DashboardHistoryEntry {
|
|
51
|
+
date: string;
|
|
52
|
+
score: number;
|
|
53
|
+
critical: number;
|
|
54
|
+
high: number;
|
|
55
|
+
medium: number;
|
|
56
|
+
low: number;
|
|
57
|
+
totalFindings: number;
|
|
58
|
+
}
|
|
59
|
+
interface DashboardData {
|
|
60
|
+
lastScan: {
|
|
61
|
+
scanStart: string;
|
|
62
|
+
scanEnd: string;
|
|
63
|
+
region: string;
|
|
64
|
+
accountId: string;
|
|
65
|
+
summary: FullScanResult["summary"];
|
|
66
|
+
modules: Array<{
|
|
67
|
+
module: string;
|
|
68
|
+
findingsCount: number;
|
|
69
|
+
status: string;
|
|
70
|
+
}>;
|
|
71
|
+
findings: Finding[];
|
|
72
|
+
};
|
|
73
|
+
history: DashboardHistoryEntry[];
|
|
74
|
+
meta: {
|
|
75
|
+
generatedAt: string;
|
|
76
|
+
version: string;
|
|
77
|
+
dataRetentionDays: number;
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
interface Scanner {
|
|
82
|
+
readonly moduleName: string;
|
|
83
|
+
scan(ctx: ScanContext): Promise<ScanResult>;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
declare function runAllScanners(scanners: Scanner[], region: string): Promise<FullScanResult>;
|
|
87
|
+
|
|
88
|
+
declare function generateMarkdownReport(scanResults: FullScanResult): string;
|
|
89
|
+
|
|
90
|
+
declare function calculateScore(summary: FullScanResult["summary"]): number;
|
|
91
|
+
declare function saveResults(scanResults: FullScanResult, outputDir?: string): string;
|
|
92
|
+
|
|
93
|
+
declare function createServer(defaultRegion: string): McpServer;
|
|
94
|
+
declare function startServer(defaultRegion: string): Promise<void>;
|
|
95
|
+
|
|
96
|
+
export { type DashboardData, type DashboardHistoryEntry, type Finding, type FullScanResult, type Priority, type ScanContext, type ScanResult, type Scanner, type Severity, calculateScore, createServer, generateMarkdownReport, runAllScanners, saveResults, startServer };
|