defense-mcp-server 0.9.2 → 0.9.4
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/build/core/auto-installer.js +31 -31
- package/build/core/command-allowlist.js +1 -1
- package/build/core/dependency-validator.js +9 -9
- package/build/core/distro-adapter.js +0 -7
- package/build/core/distro.js +0 -48
- package/build/core/encrypted-state.js +0 -7
- package/build/core/logger.js +1 -1
- package/build/core/pam-utils.js +1 -1
- package/build/core/parsers.js +1 -1
- package/build/core/preflight.js +13 -13
- package/build/core/progress.js +20 -20
- package/build/core/run-command.js +46 -0
- package/build/core/sudo-guard.js +4 -4
- package/build/core/third-party-installer.js +4 -4
- package/build/core/tool-wrapper.js +3 -3
- package/build/tools/access-control.js +6 -6
- package/build/tools/api-security.js +5 -51
- package/build/tools/app-hardening.js +23 -25
- package/build/tools/cloud-security.js +5 -51
- package/build/tools/compliance.js +9 -13
- package/build/tools/container-security.js +51 -52
- package/build/tools/deception.js +8 -54
- package/build/tools/dns-security.js +2 -48
- package/build/tools/encryption.js +86 -87
- package/build/tools/firewall.js +324 -30
- package/build/tools/hardening.js +12 -13
- package/build/tools/incident-response.js +3 -3
- package/build/tools/logging.js +17 -59
- package/build/tools/malware.js +2 -2
- package/build/tools/meta.js +86 -165
- package/build/tools/network-defense.js +3 -3
- package/build/tools/patch-management.js +8 -8
- package/build/tools/process-security.js +38 -92
- package/build/tools/sudo-management.js +36 -36
- package/build/tools/threat-intel.js +2 -48
- package/build/tools/vulnerability-management.js +3 -49
- package/build/tools/waf.js +47 -93
- package/build/tools/wireless-security.js +9 -55
- package/package.json +5 -3
- package/build/core/auto-installer.d.ts +0 -102
- package/build/core/auto-installer.d.ts.map +0 -1
- package/build/core/backup-manager.d.ts +0 -63
- package/build/core/backup-manager.d.ts.map +0 -1
- package/build/core/changelog.d.ts +0 -119
- package/build/core/changelog.d.ts.map +0 -1
- package/build/core/command-allowlist.d.ts +0 -129
- package/build/core/command-allowlist.d.ts.map +0 -1
- package/build/core/config.d.ts +0 -107
- package/build/core/config.d.ts.map +0 -1
- package/build/core/dependency-validator.d.ts +0 -106
- package/build/core/dependency-validator.d.ts.map +0 -1
- package/build/core/distro-adapter.d.ts +0 -177
- package/build/core/distro-adapter.d.ts.map +0 -1
- package/build/core/distro.d.ts +0 -68
- package/build/core/distro.d.ts.map +0 -1
- package/build/core/encrypted-state.d.ts +0 -76
- package/build/core/encrypted-state.d.ts.map +0 -1
- package/build/core/executor.d.ts +0 -65
- package/build/core/executor.d.ts.map +0 -1
- package/build/core/installer.d.ts +0 -129
- package/build/core/installer.d.ts.map +0 -1
- package/build/core/logger.d.ts +0 -118
- package/build/core/logger.d.ts.map +0 -1
- package/build/core/metrics.d.ts +0 -74
- package/build/core/metrics.d.ts.map +0 -1
- package/build/core/metrics.js +0 -97
- package/build/core/output-redactor.d.ts +0 -26
- package/build/core/output-redactor.d.ts.map +0 -1
- package/build/core/pam-utils.d.ts +0 -356
- package/build/core/pam-utils.d.ts.map +0 -1
- package/build/core/parsers.d.ts +0 -191
- package/build/core/parsers.d.ts.map +0 -1
- package/build/core/policy-engine.d.ts +0 -170
- package/build/core/policy-engine.d.ts.map +0 -1
- package/build/core/preflight.d.ts +0 -157
- package/build/core/preflight.d.ts.map +0 -1
- package/build/core/privilege-manager.d.ts +0 -108
- package/build/core/privilege-manager.d.ts.map +0 -1
- package/build/core/progress.d.ts +0 -99
- package/build/core/progress.d.ts.map +0 -1
- package/build/core/rate-limiter.d.ts +0 -101
- package/build/core/rate-limiter.d.ts.map +0 -1
- package/build/core/rollback.d.ts +0 -73
- package/build/core/rollback.d.ts.map +0 -1
- package/build/core/safeguards.d.ts +0 -58
- package/build/core/safeguards.d.ts.map +0 -1
- package/build/core/sanitizer.d.ts +0 -118
- package/build/core/sanitizer.d.ts.map +0 -1
- package/build/core/secure-fs.d.ts +0 -67
- package/build/core/secure-fs.d.ts.map +0 -1
- package/build/core/spawn-safe.d.ts +0 -55
- package/build/core/spawn-safe.d.ts.map +0 -1
- package/build/core/sudo-guard.d.ts +0 -167
- package/build/core/sudo-guard.d.ts.map +0 -1
- package/build/core/sudo-session.d.ts +0 -143
- package/build/core/sudo-session.d.ts.map +0 -1
- package/build/core/third-party-installer.d.ts +0 -58
- package/build/core/third-party-installer.d.ts.map +0 -1
- package/build/core/third-party-manifest.d.ts +0 -48
- package/build/core/third-party-manifest.d.ts.map +0 -1
- package/build/core/tool-annotations.d.ts +0 -13
- package/build/core/tool-annotations.d.ts.map +0 -1
- package/build/core/tool-dependencies.d.ts +0 -60
- package/build/core/tool-dependencies.d.ts.map +0 -1
- package/build/core/tool-durations.d.ts +0 -71
- package/build/core/tool-durations.d.ts.map +0 -1
- package/build/core/tool-registry.d.ts +0 -112
- package/build/core/tool-registry.d.ts.map +0 -1
- package/build/core/tool-wrapper.d.ts +0 -73
- package/build/core/tool-wrapper.d.ts.map +0 -1
- package/build/index.d.ts +0 -3
- package/build/index.d.ts.map +0 -1
- package/build/tools/access-control.d.ts +0 -11
- package/build/tools/access-control.d.ts.map +0 -1
- package/build/tools/api-security.d.ts +0 -12
- package/build/tools/api-security.d.ts.map +0 -1
- package/build/tools/app-hardening.d.ts +0 -11
- package/build/tools/app-hardening.d.ts.map +0 -1
- package/build/tools/backup.d.ts +0 -8
- package/build/tools/backup.d.ts.map +0 -1
- package/build/tools/cloud-security.d.ts +0 -17
- package/build/tools/cloud-security.d.ts.map +0 -1
- package/build/tools/compliance.d.ts +0 -11
- package/build/tools/compliance.d.ts.map +0 -1
- package/build/tools/container-security.d.ts +0 -14
- package/build/tools/container-security.d.ts.map +0 -1
- package/build/tools/deception.d.ts +0 -13
- package/build/tools/deception.d.ts.map +0 -1
- package/build/tools/dns-security.d.ts +0 -93
- package/build/tools/dns-security.d.ts.map +0 -1
- package/build/tools/ebpf-security.d.ts +0 -15
- package/build/tools/ebpf-security.d.ts.map +0 -1
- package/build/tools/encryption.d.ts +0 -12
- package/build/tools/encryption.d.ts.map +0 -1
- package/build/tools/firewall.d.ts +0 -9
- package/build/tools/firewall.d.ts.map +0 -1
- package/build/tools/hardening.d.ts +0 -8
- package/build/tools/hardening.d.ts.map +0 -1
- package/build/tools/incident-response.d.ts +0 -11
- package/build/tools/incident-response.d.ts.map +0 -1
- package/build/tools/integrity.d.ts +0 -15
- package/build/tools/integrity.d.ts.map +0 -1
- package/build/tools/logging.d.ts +0 -21
- package/build/tools/logging.d.ts.map +0 -1
- package/build/tools/malware.d.ts +0 -10
- package/build/tools/malware.d.ts.map +0 -1
- package/build/tools/meta.d.ts +0 -13
- package/build/tools/meta.d.ts.map +0 -1
- package/build/tools/network-defense.d.ts +0 -11
- package/build/tools/network-defense.d.ts.map +0 -1
- package/build/tools/patch-management.d.ts +0 -3
- package/build/tools/patch-management.d.ts.map +0 -1
- package/build/tools/process-security.d.ts +0 -12
- package/build/tools/process-security.d.ts.map +0 -1
- package/build/tools/secrets.d.ts +0 -8
- package/build/tools/secrets.d.ts.map +0 -1
- package/build/tools/sudo-management.d.ts +0 -17
- package/build/tools/sudo-management.d.ts.map +0 -1
- package/build/tools/supply-chain-security.d.ts +0 -8
- package/build/tools/supply-chain-security.d.ts.map +0 -1
- package/build/tools/threat-intel.d.ts +0 -22
- package/build/tools/threat-intel.d.ts.map +0 -1
- package/build/tools/vulnerability-management.d.ts +0 -11
- package/build/tools/vulnerability-management.d.ts.map +0 -1
- package/build/tools/waf.d.ts +0 -12
- package/build/tools/waf.d.ts.map +0 -1
- package/build/tools/wireless-security.d.ts +0 -19
- package/build/tools/wireless-security.d.ts.map +0 -1
- package/build/tools/zero-trust-network.d.ts +0 -8
- package/build/tools/zero-trust-network.d.ts.map +0 -1
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared async command runner using spawnSafe.
|
|
3
|
+
*
|
|
4
|
+
* Replaces the duplicate runCommand() implementations found in 11 tool files.
|
|
5
|
+
* Wraps spawnSafe in a Promise that collects stdout/stderr and handles
|
|
6
|
+
* timeouts, spawn errors, and process errors gracefully.
|
|
7
|
+
*/
|
|
8
|
+
import { spawnSafe } from "./spawn-safe.js";
|
|
9
|
+
export async function runCommand(command, args, timeoutMs = 30_000) {
|
|
10
|
+
return new Promise((resolve) => {
|
|
11
|
+
let child;
|
|
12
|
+
try {
|
|
13
|
+
child = spawnSafe(command, args);
|
|
14
|
+
}
|
|
15
|
+
catch (err) {
|
|
16
|
+
resolve({ stdout: "", stderr: err instanceof Error ? err.message : String(err), exitCode: -1 });
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
let stdout = "";
|
|
20
|
+
let stderr = "";
|
|
21
|
+
let resolved = false;
|
|
22
|
+
const timer = setTimeout(() => {
|
|
23
|
+
if (!resolved) {
|
|
24
|
+
resolved = true;
|
|
25
|
+
child.kill("SIGTERM");
|
|
26
|
+
resolve({ stdout, stderr: stderr + "\n[TIMEOUT]", exitCode: -1 });
|
|
27
|
+
}
|
|
28
|
+
}, timeoutMs);
|
|
29
|
+
child.stdout?.on("data", (data) => { stdout += data.toString(); });
|
|
30
|
+
child.stderr?.on("data", (data) => { stderr += data.toString(); });
|
|
31
|
+
child.on("close", (code) => {
|
|
32
|
+
if (!resolved) {
|
|
33
|
+
resolved = true;
|
|
34
|
+
clearTimeout(timer);
|
|
35
|
+
resolve({ stdout, stderr, exitCode: code ?? -1 });
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
child.on("error", (err) => {
|
|
39
|
+
if (!resolved) {
|
|
40
|
+
resolved = true;
|
|
41
|
+
clearTimeout(timer);
|
|
42
|
+
resolve({ stdout, stderr: err.message, exitCode: -1 });
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
}
|
package/build/core/sudo-guard.js
CHANGED
|
@@ -188,14 +188,14 @@ export class SudoGuard {
|
|
|
188
188
|
const reasonText = reason ?? "This tool requires elevated (root) privileges to function.";
|
|
189
189
|
// Build the prompt message for AI clients.
|
|
190
190
|
const lines = [];
|
|
191
|
-
lines.push("
|
|
192
|
-
lines.push("
|
|
191
|
+
lines.push("Sudo session required");
|
|
192
|
+
lines.push("");
|
|
193
193
|
lines.push("");
|
|
194
194
|
lines.push(`Tool: ${toolName}`);
|
|
195
195
|
lines.push(`Reason: ${reasonText}`);
|
|
196
196
|
lines.push("");
|
|
197
197
|
if (status.elevated && status.remainingSeconds !== null && status.remainingSeconds <= 0) {
|
|
198
|
-
lines.push("
|
|
198
|
+
lines.push("WARNING: Your sudo session has expired.");
|
|
199
199
|
lines.push("");
|
|
200
200
|
lines.push("ACTION: Call sudo_session with action=elevate_gui to re-authenticate,");
|
|
201
201
|
lines.push("or action=extend to extend an active session.");
|
|
@@ -210,7 +210,7 @@ export class SudoGuard {
|
|
|
210
210
|
lines.push("stored in a zeroable memory buffer, and never visible to the AI.");
|
|
211
211
|
if (originalError) {
|
|
212
212
|
lines.push("");
|
|
213
|
-
lines.push("
|
|
213
|
+
lines.push("");
|
|
214
214
|
lines.push("Original error:");
|
|
215
215
|
lines.push(originalError.substring(0, 500));
|
|
216
216
|
}
|
|
@@ -471,7 +471,7 @@ async function installGithubRelease(entry) {
|
|
|
471
471
|
dryRun: false,
|
|
472
472
|
success: true,
|
|
473
473
|
}));
|
|
474
|
-
console.error(`[third-party-installer]
|
|
474
|
+
console.error(`[third-party-installer] OK ${entry.name} v${entry.version} installed successfully`);
|
|
475
475
|
return {
|
|
476
476
|
binary: entry.binary,
|
|
477
477
|
success: true,
|
|
@@ -585,7 +585,7 @@ async function installAptRepo(entry) {
|
|
|
585
585
|
console.error(`[third-party-installer] Running apt-get update...`);
|
|
586
586
|
const updateResult = execWithSudo(["apt-get", "update"], { timeoutMs: 120_000 });
|
|
587
587
|
if (!updateResult.success) {
|
|
588
|
-
console.error(`[third-party-installer]
|
|
588
|
+
console.error(`[third-party-installer] WARNING: apt-get update had issues: ${updateResult.stderr.slice(0, 200)}`);
|
|
589
589
|
}
|
|
590
590
|
const packages = entry.aptPinnedPackages ?? [entry.binary];
|
|
591
591
|
for (const pkg of packages) {
|
|
@@ -609,7 +609,7 @@ async function installAptRepo(entry) {
|
|
|
609
609
|
dryRun: false,
|
|
610
610
|
success: true,
|
|
611
611
|
}));
|
|
612
|
-
console.error(`[third-party-installer]
|
|
612
|
+
console.error(`[third-party-installer] OK ${entry.name} v${entry.version} installed successfully via APT`);
|
|
613
613
|
return {
|
|
614
614
|
binary: entry.binary,
|
|
615
615
|
success: true,
|
|
@@ -658,7 +658,7 @@ async function installNpmLocal(entry) {
|
|
|
658
658
|
dryRun: false,
|
|
659
659
|
success: true,
|
|
660
660
|
}));
|
|
661
|
-
console.error(`[third-party-installer]
|
|
661
|
+
console.error(`[third-party-installer] OK ${entry.name} v${entry.version} installed via npm`);
|
|
662
662
|
return {
|
|
663
663
|
binary: entry.binary,
|
|
664
664
|
success: true,
|
|
@@ -195,7 +195,7 @@ function createWrappedHandler(toolName, originalHandler, ctx) {
|
|
|
195
195
|
content: [
|
|
196
196
|
{
|
|
197
197
|
type: "text",
|
|
198
|
-
text:
|
|
198
|
+
text: `WARNING: Rate limit exceeded\n\n${rateLimitResult.reason}`,
|
|
199
199
|
},
|
|
200
200
|
],
|
|
201
201
|
isError: true,
|
|
@@ -269,12 +269,12 @@ function createWrappedHandler(toolName, originalHandler, ctx) {
|
|
|
269
269
|
catch (err) {
|
|
270
270
|
// Pre-flight itself threw — return error instead of running without dependency checking
|
|
271
271
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
272
|
-
console.error(`[preflight]
|
|
272
|
+
console.error(`[preflight] WARNING: Pre-flight failed unexpectedly for '${toolName}': ${errMsg}`);
|
|
273
273
|
return {
|
|
274
274
|
content: [
|
|
275
275
|
{
|
|
276
276
|
type: "text",
|
|
277
|
-
text:
|
|
277
|
+
text: `WARNING: Pre-flight internal error for '${toolName}'\n\nThe pre-flight dependency checking system encountered an unexpected error and the tool was not executed.\n\nError: ${errMsg}\n\nPlease retry the operation or check the pre-flight configuration. If this persists, the tool's dependency manifest may need attention.`,
|
|
278
278
|
},
|
|
279
279
|
],
|
|
280
280
|
isError: true,
|
|
@@ -879,8 +879,8 @@ export function registerAccessControlTools(server) {
|
|
|
879
879
|
? "CRITICAL: Weak SSH algorithms detected. Apply Mozilla Modern SSH configuration immediately."
|
|
880
880
|
: warnCount > 0
|
|
881
881
|
? "WARNING: SSH algorithms not explicitly configured. Set explicit algorithms in sshd_config."
|
|
882
|
-
: "
|
|
883
|
-
}
|
|
882
|
+
: "SSH cryptographic configuration meets modern standards.",
|
|
883
|
+
}))],
|
|
884
884
|
};
|
|
885
885
|
}
|
|
886
886
|
catch (error) {
|
|
@@ -1154,7 +1154,7 @@ export function registerAccessControlTools(server) {
|
|
|
1154
1154
|
content: [
|
|
1155
1155
|
createTextContent(`[DRY-RUN] Would write the following to ${targetFile}:\n\n` +
|
|
1156
1156
|
configLines.map((l) => ` ${l}`).join("\n") +
|
|
1157
|
-
(pwqSanityWarnings ? `\n\
|
|
1157
|
+
(pwqSanityWarnings ? `\n\nWARNING: Sanity warnings:\n${pwqSanityWarnings}` : "")),
|
|
1158
1158
|
],
|
|
1159
1159
|
};
|
|
1160
1160
|
}
|
|
@@ -1200,7 +1200,7 @@ export function registerAccessControlTools(server) {
|
|
|
1200
1200
|
content: [
|
|
1201
1201
|
createTextContent(`pam_pwquality configured in ${targetFile}:\n\n` +
|
|
1202
1202
|
configLines.map((l) => ` ${l}`).join("\n") +
|
|
1203
|
-
(pwqSanityWarnings ? `\n\
|
|
1203
|
+
(pwqSanityWarnings ? `\n\nWARNING: Sanity warnings:\n${pwqSanityWarnings}` : "")),
|
|
1204
1204
|
],
|
|
1205
1205
|
};
|
|
1206
1206
|
}
|
|
@@ -1276,7 +1276,7 @@ export function registerAccessControlTools(server) {
|
|
|
1276
1276
|
createTextContent(`[DRY-RUN] Would add/update pam_faillock.so in ${targetFile}:\n\n` +
|
|
1277
1277
|
` ${preLine}\n ${authLine}\n\n` +
|
|
1278
1278
|
`Settings: ${JSON.stringify(merged)}` +
|
|
1279
|
-
(flSanityWarnings ? `\n\
|
|
1279
|
+
(flSanityWarnings ? `\n\nWARNING: Sanity warnings:\n${flSanityWarnings}` : "")),
|
|
1280
1280
|
],
|
|
1281
1281
|
};
|
|
1282
1282
|
}
|
|
@@ -1332,7 +1332,7 @@ export function registerAccessControlTools(server) {
|
|
|
1332
1332
|
` ${preLine}\n ${authLine}\n\n` +
|
|
1333
1333
|
`Settings: ${JSON.stringify(merged)}\n` +
|
|
1334
1334
|
`Backup: ${backupEntry.backupPath}` +
|
|
1335
|
-
(flSanityWarnings ? `\n\
|
|
1335
|
+
(flSanityWarnings ? `\n\nWARNING: Sanity warnings:\n${flSanityWarnings}` : "")),
|
|
1336
1336
|
],
|
|
1337
1337
|
};
|
|
1338
1338
|
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* verification, TLS configuration checking, and CORS policy analysis.
|
|
9
9
|
*/
|
|
10
10
|
import { z } from "zod";
|
|
11
|
-
import {
|
|
11
|
+
import { runCommand } from "../core/run-command.js";
|
|
12
12
|
import { createTextContent, createErrorContent, formatToolOutput, } from "../core/parsers.js";
|
|
13
13
|
// ── Constants ──────────────────────────────────────────────────────────────────
|
|
14
14
|
const DEFAULT_PORT_RANGE = "80,443,3000,4000,5000,8000,8080,8443,9000";
|
|
@@ -21,53 +21,7 @@ const COMMON_API_PATHS = [
|
|
|
21
21
|
"/openapi.json",
|
|
22
22
|
"/.well-known/openid-configuration",
|
|
23
23
|
];
|
|
24
|
-
|
|
25
|
-
* Run a command via spawnSafe and collect output as a promise.
|
|
26
|
-
* Handles errors gracefully — returns error info instead of throwing.
|
|
27
|
-
*/
|
|
28
|
-
async function runCommand(command, args, timeoutMs = 30_000) {
|
|
29
|
-
return new Promise((resolve) => {
|
|
30
|
-
let child;
|
|
31
|
-
try {
|
|
32
|
-
child = spawnSafe(command, args);
|
|
33
|
-
}
|
|
34
|
-
catch (err) {
|
|
35
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
36
|
-
resolve({ stdout: "", stderr: msg, exitCode: -1 });
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
let stdout = "";
|
|
40
|
-
let stderr = "";
|
|
41
|
-
let resolved = false;
|
|
42
|
-
const timer = setTimeout(() => {
|
|
43
|
-
if (!resolved) {
|
|
44
|
-
resolved = true;
|
|
45
|
-
child.kill("SIGTERM");
|
|
46
|
-
resolve({ stdout, stderr: stderr + "\n[TIMEOUT]", exitCode: -1 });
|
|
47
|
-
}
|
|
48
|
-
}, timeoutMs);
|
|
49
|
-
child.stdout?.on("data", (data) => {
|
|
50
|
-
stdout += data.toString();
|
|
51
|
-
});
|
|
52
|
-
child.stderr?.on("data", (data) => {
|
|
53
|
-
stderr += data.toString();
|
|
54
|
-
});
|
|
55
|
-
child.on("close", (code) => {
|
|
56
|
-
if (!resolved) {
|
|
57
|
-
resolved = true;
|
|
58
|
-
clearTimeout(timer);
|
|
59
|
-
resolve({ stdout, stderr, exitCode: code ?? -1 });
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
child.on("error", (err) => {
|
|
63
|
-
if (!resolved) {
|
|
64
|
-
resolved = true;
|
|
65
|
-
clearTimeout(timer);
|
|
66
|
-
resolve({ stdout, stderr: err.message, exitCode: -1 });
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
}
|
|
24
|
+
// ── Helpers ────────────────────────────────────────────────────────────────────
|
|
71
25
|
/**
|
|
72
26
|
* Validate and normalize a target URL.
|
|
73
27
|
* Returns the normalized URL or null if invalid.
|
|
@@ -692,7 +646,7 @@ export function registerApiSecurityTools(server) {
|
|
|
692
646
|
text += `Auth Type: ${authResult.authType}\n`;
|
|
693
647
|
text += `Status without auth: ${authResult.statusWithoutAuth}\n`;
|
|
694
648
|
text += `Status with auth: ${authResult.statusWithAuth}\n`;
|
|
695
|
-
text += `Verbose Errors: ${authResult.verboseErrors ? "YES
|
|
649
|
+
text += `Verbose Errors: ${authResult.verboseErrors ? "YES WARNING" : "no"}\n`;
|
|
696
650
|
if (authResult.errorDetails.length > 0) {
|
|
697
651
|
text += `\nError Details:\n`;
|
|
698
652
|
for (const detail of authResult.errorDetails) {
|
|
@@ -864,8 +818,8 @@ export function registerApiSecurityTools(server) {
|
|
|
864
818
|
text += `Allow-Origin: ${corsResult.allowOrigin}\n`;
|
|
865
819
|
text += `Allow-Credentials: ${corsResult.allowCredentials}\n`;
|
|
866
820
|
text += `Allow-Methods: ${corsResult.allowMethods || "not specified"}\n`;
|
|
867
|
-
text += `Wildcard Origin: ${corsResult.wildcardOrigin ? "YES
|
|
868
|
-
text += `Origin Reflection: ${corsResult.originReflection ? "YES
|
|
821
|
+
text += `Wildcard Origin: ${corsResult.wildcardOrigin ? "YES WARNING" : "no"}\n`;
|
|
822
|
+
text += `Origin Reflection: ${corsResult.originReflection ? "YES WARNING" : "no"}\n`;
|
|
869
823
|
}
|
|
870
824
|
if (corsResult.criticalIssues.length > 0) {
|
|
871
825
|
text += `\nCritical Issues:\n`;
|
|
@@ -454,8 +454,8 @@ export function registerAppHardeningTools(server) {
|
|
|
454
454
|
case "audit": {
|
|
455
455
|
try {
|
|
456
456
|
const sections = [];
|
|
457
|
-
sections.push("
|
|
458
|
-
sections.push("
|
|
457
|
+
sections.push("Application Security Audit");
|
|
458
|
+
sections.push("");
|
|
459
459
|
const apps = await detectRunningApps();
|
|
460
460
|
if (apps.length === 0) {
|
|
461
461
|
sections.push("\nNo known applications detected.");
|
|
@@ -467,9 +467,7 @@ export function registerAppHardeningTools(server) {
|
|
|
467
467
|
apps.sort((a, b) => riskOrder[a.profile.riskLevel] - riskOrder[b.profile.riskLevel]);
|
|
468
468
|
let totalRisks = 0;
|
|
469
469
|
for (const app of apps) {
|
|
470
|
-
|
|
471
|
-
app.profile.riskLevel === "high" ? "🔴" : app.profile.riskLevel === "medium" ? "🟡" : "🟢";
|
|
472
|
-
sections.push(`── ${riskIcon} ${app.profile.name} ──`);
|
|
470
|
+
sections.push(`── ${app.profile.name} ──`);
|
|
473
471
|
sections.push(` Category: ${app.profile.category}`);
|
|
474
472
|
sections.push(` Risk Level: ${app.profile.riskLevel.toUpperCase()}`);
|
|
475
473
|
sections.push(` Running as: ${app.user}`);
|
|
@@ -480,17 +478,17 @@ export function registerAppHardeningTools(server) {
|
|
|
480
478
|
sections.push(` Listening Ports:`);
|
|
481
479
|
for (const lp of app.listenPorts) {
|
|
482
480
|
const external = !lp.address.includes("127.0.0.1") && !lp.address.includes("::1");
|
|
483
|
-
sections.push(` ${lp.protocol}/${lp.port} on ${lp.address} [${external ? "
|
|
481
|
+
sections.push(` ${lp.protocol}/${lp.port} on ${lp.address} [${external ? "WARNING: EXTERNAL" : "localhost"}]`);
|
|
484
482
|
}
|
|
485
483
|
}
|
|
486
484
|
sections.push(` Security Concerns:`);
|
|
487
485
|
for (const concern of app.profile.securityConcerns) {
|
|
488
|
-
sections.push(`
|
|
486
|
+
sections.push(` WARNING: ${concern}`);
|
|
489
487
|
totalRisks++;
|
|
490
488
|
}
|
|
491
489
|
sections.push(` Top Recommendations:`);
|
|
492
490
|
for (const rec of app.profile.recommendations.slice(0, 3)) {
|
|
493
|
-
sections.push(`
|
|
491
|
+
sections.push(` ${rec}`);
|
|
494
492
|
}
|
|
495
493
|
if (app.profile.recommendations.length > 3) {
|
|
496
494
|
sections.push(` ... +${app.profile.recommendations.length - 3} more (use action=recommend)`);
|
|
@@ -530,24 +528,24 @@ export function registerAppHardeningTools(server) {
|
|
|
530
528
|
};
|
|
531
529
|
}
|
|
532
530
|
const sections = [];
|
|
533
|
-
sections.push(
|
|
534
|
-
sections.push("
|
|
531
|
+
sections.push(`Hardening Guide: ${profile.name}`);
|
|
532
|
+
sections.push("");
|
|
535
533
|
sections.push(`Category: ${profile.category} | Risk: ${profile.riskLevel.toUpperCase()}`);
|
|
536
534
|
sections.push("\n── Security Concerns ──");
|
|
537
535
|
for (const concern of profile.securityConcerns) {
|
|
538
|
-
sections.push(`
|
|
536
|
+
sections.push(` WARNING: ${concern}`);
|
|
539
537
|
}
|
|
540
538
|
sections.push("\n── Network Hardening ──");
|
|
541
539
|
if (profile.requiredPorts.length > 0) {
|
|
542
540
|
sections.push(" Ports that MUST remain open (core functionality):");
|
|
543
541
|
for (const p of profile.requiredPorts) {
|
|
544
|
-
sections.push(`
|
|
542
|
+
sections.push(` ${p.protocol}/${p.port} — ${p.purpose}`);
|
|
545
543
|
}
|
|
546
544
|
}
|
|
547
545
|
if (profile.localhostOnlyPorts.length > 0) {
|
|
548
546
|
sections.push(" Ports to restrict to localhost/LAN:");
|
|
549
547
|
for (const p of profile.localhostOnlyPorts) {
|
|
550
|
-
sections.push(`
|
|
548
|
+
sections.push(` ${p.protocol}/${p.port} — ${p.purpose}`);
|
|
551
549
|
}
|
|
552
550
|
}
|
|
553
551
|
sections.push(" Firewall strategy:");
|
|
@@ -571,11 +569,11 @@ export function registerAppHardeningTools(server) {
|
|
|
571
569
|
sections.push("\n── Filesystem Permissions ──");
|
|
572
570
|
sections.push(" Writable paths (required for operation):");
|
|
573
571
|
for (const p of profile.writablePaths) {
|
|
574
|
-
sections.push(`
|
|
572
|
+
sections.push(` ${p}`);
|
|
575
573
|
}
|
|
576
574
|
sections.push(" Read-only paths:");
|
|
577
575
|
for (const p of profile.readablePaths) {
|
|
578
|
-
sections.push(`
|
|
576
|
+
sections.push(` ${p}`);
|
|
579
577
|
}
|
|
580
578
|
return { content: [createTextContent(sections.join("\n"))] };
|
|
581
579
|
}
|
|
@@ -600,8 +598,8 @@ export function registerAppHardeningTools(server) {
|
|
|
600
598
|
}
|
|
601
599
|
const effectiveDryRun = dry_run ?? getConfig().dryRun;
|
|
602
600
|
const sections = [];
|
|
603
|
-
sections.push(
|
|
604
|
-
sections.push("
|
|
601
|
+
sections.push(`Firewall Rules for ${profile.name}`);
|
|
602
|
+
sections.push("");
|
|
605
603
|
sections.push(`LAN CIDR: ${lan_cidr}`);
|
|
606
604
|
sections.push(effectiveDryRun ? "\n[DRY RUN] Rules that would be applied:\n" : "\nApplying rules:\n");
|
|
607
605
|
const rules = [];
|
|
@@ -632,7 +630,7 @@ export function registerAppHardeningTools(server) {
|
|
|
632
630
|
else
|
|
633
631
|
failed++;
|
|
634
632
|
}
|
|
635
|
-
sections.push(`\
|
|
633
|
+
sections.push(`\nApplied ${applied} rules, ${failed} failed`);
|
|
636
634
|
}
|
|
637
635
|
sections.push("\n── Additional Recommendations ──");
|
|
638
636
|
sections.push(" • Consider using nftables for more granular control");
|
|
@@ -690,10 +688,10 @@ export function registerAppHardeningTools(server) {
|
|
|
690
688
|
}
|
|
691
689
|
}
|
|
692
690
|
const sections = [];
|
|
693
|
-
sections.push(
|
|
694
|
-
sections.push("
|
|
691
|
+
sections.push(`Systemd Hardening: ${profile.name}`);
|
|
692
|
+
sections.push("");
|
|
695
693
|
if (!svcName) {
|
|
696
|
-
sections.push(`\
|
|
694
|
+
sections.push(`\nWARNING: No running systemd service found for ${profile.name}.`);
|
|
697
695
|
sections.push("Provide service_name manually if the service uses a different name.");
|
|
698
696
|
sections.push("\nRecommended override content for when the service is configured:\n");
|
|
699
697
|
}
|
|
@@ -726,12 +724,12 @@ export function registerAppHardeningTools(server) {
|
|
|
726
724
|
});
|
|
727
725
|
if (writeResult.exitCode === 0) {
|
|
728
726
|
await executeCommand({ toolName: "app_hardening", command: "sudo", args: ["systemctl", "daemon-reload"], timeout: 10000 });
|
|
729
|
-
sections.push(`\
|
|
730
|
-
sections.push("
|
|
731
|
-
sections.push(`\
|
|
727
|
+
sections.push(`\nOverride written to ${overridePath}`);
|
|
728
|
+
sections.push("systemd daemon reloaded");
|
|
729
|
+
sections.push(`\nRestart the service to apply: sudo systemctl restart ${svcName}`);
|
|
732
730
|
}
|
|
733
731
|
else {
|
|
734
|
-
sections.push(`\
|
|
732
|
+
sections.push(`\nFailed to write override: ${writeResult.stderr}`);
|
|
735
733
|
}
|
|
736
734
|
}
|
|
737
735
|
sections.push("\n── What These Directives Do ──");
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* exposure checking, storage audit, and IMDS security assessment for AWS/GCP/Azure.
|
|
9
9
|
*/
|
|
10
10
|
import { z } from "zod";
|
|
11
|
-
import {
|
|
11
|
+
import { runCommand } from "../core/run-command.js";
|
|
12
12
|
import { createTextContent, createErrorContent, formatToolOutput, } from "../core/parsers.js";
|
|
13
13
|
import { existsSync } from "node:fs";
|
|
14
14
|
/** Sensitive environment variable names for cloud credentials */
|
|
@@ -30,53 +30,7 @@ const CREDENTIAL_FILE_PATHS = [
|
|
|
30
30
|
{ provider: "gcp", path: "~/.config/gcloud/application_default_credentials.json" },
|
|
31
31
|
{ provider: "azure", path: "~/.azure/accessTokens.json" },
|
|
32
32
|
];
|
|
33
|
-
|
|
34
|
-
* Run a command via spawnSafe and collect output as a promise.
|
|
35
|
-
* Handles errors gracefully — returns error info instead of throwing.
|
|
36
|
-
*/
|
|
37
|
-
async function runCommand(command, args, timeoutMs = 30_000) {
|
|
38
|
-
return new Promise((resolve) => {
|
|
39
|
-
let child;
|
|
40
|
-
try {
|
|
41
|
-
child = spawnSafe(command, args);
|
|
42
|
-
}
|
|
43
|
-
catch (err) {
|
|
44
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
45
|
-
resolve({ stdout: "", stderr: msg, exitCode: -1 });
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
let stdout = "";
|
|
49
|
-
let stderr = "";
|
|
50
|
-
let resolved = false;
|
|
51
|
-
const timer = setTimeout(() => {
|
|
52
|
-
if (!resolved) {
|
|
53
|
-
resolved = true;
|
|
54
|
-
child.kill("SIGTERM");
|
|
55
|
-
resolve({ stdout, stderr: stderr + "\n[TIMEOUT]", exitCode: -1 });
|
|
56
|
-
}
|
|
57
|
-
}, timeoutMs);
|
|
58
|
-
child.stdout?.on("data", (data) => {
|
|
59
|
-
stdout += data.toString();
|
|
60
|
-
});
|
|
61
|
-
child.stderr?.on("data", (data) => {
|
|
62
|
-
stderr += data.toString();
|
|
63
|
-
});
|
|
64
|
-
child.on("close", (code) => {
|
|
65
|
-
if (!resolved) {
|
|
66
|
-
resolved = true;
|
|
67
|
-
clearTimeout(timer);
|
|
68
|
-
resolve({ stdout, stderr, exitCode: code ?? -1 });
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
child.on("error", (err) => {
|
|
72
|
-
if (!resolved) {
|
|
73
|
-
resolved = true;
|
|
74
|
-
clearTimeout(timer);
|
|
75
|
-
resolve({ stdout, stderr: err.message, exitCode: -1 });
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
}
|
|
33
|
+
// ── Helpers ────────────────────────────────────────────────────────────────────
|
|
80
34
|
// ── Credential masking ─────────────────────────────────────────────────────────
|
|
81
35
|
/**
|
|
82
36
|
* Mask a credential value, showing first 4 chars + asterisks.
|
|
@@ -611,7 +565,7 @@ export function registerCloudSecurityTools(server) {
|
|
|
611
565
|
text += "Credential Files:\n";
|
|
612
566
|
for (const file of creds.credentialFiles) {
|
|
613
567
|
const status = file.exists
|
|
614
|
-
? `EXISTS (permissions: ${file.permissions || "unknown"})${file.permWarning ? `
|
|
568
|
+
? `EXISTS (permissions: ${file.permissions || "unknown"})${file.permWarning ? ` WARNING: ${file.permWarning}` : ""}`
|
|
615
569
|
: "not found";
|
|
616
570
|
text += ` • ${file.path}: ${status}\n`;
|
|
617
571
|
}
|
|
@@ -706,10 +660,10 @@ export function registerCloudSecurityTools(server) {
|
|
|
706
660
|
return { content: [formatToolOutput(output)] };
|
|
707
661
|
}
|
|
708
662
|
let text = "Cloud Security — IMDS Security Check\n\n";
|
|
709
|
-
text += `IMDSv1 (unauthenticated): ${imds.v1Accessible ? "ACCESSIBLE
|
|
663
|
+
text += `IMDSv1 (unauthenticated): ${imds.v1Accessible ? "ACCESSIBLE WARNING" : "not accessible OK"}\n`;
|
|
710
664
|
text += `IMDSv2 (token-based): ${imds.v2Accessible ? "accessible" : "not accessible"}\n`;
|
|
711
665
|
text += `IMDSv2 Token Endpoint: ${imds.v2TokenWorks ? "working" : "not working"}\n`;
|
|
712
|
-
text += `Iptables IMDS Rules: ${imds.iptablesBlocked ? "BLOCKED
|
|
666
|
+
text += `Iptables IMDS Rules: ${imds.iptablesBlocked ? "BLOCKED OK" : imds.iptablesRules.length > 0 ? "rules found" : "no rules"}\n`;
|
|
713
667
|
text += `Hop Limit: ${imds.hopLimit}\n`;
|
|
714
668
|
text += `\nSeverity: ${imds.severity}\n`;
|
|
715
669
|
text += `Security Score: ${imds.securityScore}/100\n`;
|
|
@@ -14,7 +14,7 @@ import { generateDurationBanner, generateTimingSummary, startTiming, } from "../
|
|
|
14
14
|
import { logChange, createChangeEntry } from "../core/changelog.js";
|
|
15
15
|
import { getDistroAdapter } from "../core/distro-adapter.js";
|
|
16
16
|
import { sanitizeArgs } from "../core/sanitizer.js";
|
|
17
|
-
import { readFileSync } from "node:fs";
|
|
17
|
+
import { readFileSync, statSync } from "node:fs";
|
|
18
18
|
import { loadPolicy, evaluatePolicy, getBuiltinPolicies, } from "../core/policy-engine.js";
|
|
19
19
|
async function runCisCheck(command, args, id, title, level, expectPattern) {
|
|
20
20
|
try {
|
|
@@ -668,7 +668,7 @@ export function registerComplianceTools(server) {
|
|
|
668
668
|
}
|
|
669
669
|
function filePermCheckLocal(filePath, maxPerm) {
|
|
670
670
|
try {
|
|
671
|
-
|
|
671
|
+
// statSync imported at module level
|
|
672
672
|
const stat = statSync(filePath);
|
|
673
673
|
const mode = (stat.mode & 0o777).toString(8);
|
|
674
674
|
return { passed: parseInt(mode, 8) <= parseInt(maxPerm, 8), detail: `${filePath}: ${mode} (max: ${maxPerm})` };
|
|
@@ -943,21 +943,17 @@ export function registerComplianceTools(server) {
|
|
|
943
943
|
for (const section of report.sections) {
|
|
944
944
|
md += `## ${section.name}\n\n`;
|
|
945
945
|
md += `**Score:** ${section.score}/100\n\n`;
|
|
946
|
-
md += `\`\`\`json\n${JSON.stringify(section.details
|
|
946
|
+
md += `\`\`\`json\n${JSON.stringify(section.details)}\n\`\`\`\n\n`;
|
|
947
947
|
}
|
|
948
948
|
return { content: [createTextContent(md)] };
|
|
949
949
|
}
|
|
950
950
|
// Text format
|
|
951
|
-
let text =
|
|
952
|
-
text += `
|
|
953
|
-
text += `
|
|
954
|
-
text += ` Overall Score: ${report.overallScore}/100\n`;
|
|
955
|
-
text += `${"=".repeat(60)}\n\n`;
|
|
951
|
+
let text = `COMPLIANCE REPORT\n`;
|
|
952
|
+
text += `Generated: ${report.timestamp}\n`;
|
|
953
|
+
text += `Overall Score: ${report.overallScore}/100\n\n`;
|
|
956
954
|
for (const section of report.sections) {
|
|
957
|
-
text += `${
|
|
958
|
-
text +=
|
|
959
|
-
text += `${"─".repeat(50)}\n`;
|
|
960
|
-
text += `${JSON.stringify(section.details, null, 2)}\n\n`;
|
|
955
|
+
text += `${section.name} — Score: ${section.score}/100\n`;
|
|
956
|
+
text += `${JSON.stringify(section.details)}\n\n`;
|
|
961
957
|
}
|
|
962
958
|
return { content: [createTextContent(text)] };
|
|
963
959
|
}
|
|
@@ -981,7 +977,7 @@ export function registerComplianceTools(server) {
|
|
|
981
977
|
const currentUser = process.env.USER || process.env.LOGNAME;
|
|
982
978
|
if (currentUser && currentUser !== "root" && !allowed_users.includes(currentUser)) {
|
|
983
979
|
allowed_users = [...allowed_users, currentUser];
|
|
984
|
-
changes.push(
|
|
980
|
+
changes.push(`WARNING: Auto-included current user '${currentUser}' in allowed_users to prevent self-lockout`);
|
|
985
981
|
}
|
|
986
982
|
// Validate usernames
|
|
987
983
|
for (const user of allowed_users) {
|