@kryptosai/mcp-observatory 0.27.0 → 1.28.1
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/COMMERCIAL.md +28 -2
- package/CONTRIBUTORS.md +39 -0
- package/README.md +114 -22
- package/dist/src/action-receipt.d.ts +20 -0
- package/dist/src/action-receipt.js +85 -0
- package/dist/src/action-receipt.js.map +1 -0
- package/dist/src/adapters/http.js +1 -5
- package/dist/src/adapters/http.js.map +1 -1
- package/dist/src/audit.d.ts +64 -0
- package/dist/src/audit.js +359 -0
- package/dist/src/audit.js.map +1 -0
- package/dist/src/cassette.js +3 -2
- package/dist/src/cassette.js.map +1 -1
- package/dist/src/checks/attack-sim.d.ts +20 -0
- package/dist/src/checks/attack-sim.js +316 -0
- package/dist/src/checks/attack-sim.js.map +1 -0
- package/dist/src/ci-issue.js +4 -3
- package/dist/src/ci-issue.js.map +1 -1
- package/dist/src/cli.js +25 -4
- package/dist/src/cli.js.map +1 -1
- package/dist/src/commands/attack-sim.d.ts +2 -0
- package/dist/src/commands/attack-sim.js +220 -0
- package/dist/src/commands/attack-sim.js.map +1 -0
- package/dist/src/commands/audit.d.ts +2 -0
- package/dist/src/commands/audit.js +132 -0
- package/dist/src/commands/audit.js.map +1 -0
- package/dist/src/commands/init-ci.d.ts +3 -0
- package/dist/src/commands/init-ci.js +78 -13
- package/dist/src/commands/init-ci.js.map +1 -1
- package/dist/src/commands/legacy.js +8 -1
- package/dist/src/commands/legacy.js.map +1 -1
- package/dist/src/commands/receipt.d.ts +2 -0
- package/dist/src/commands/receipt.js +113 -0
- package/dist/src/commands/receipt.js.map +1 -0
- package/dist/src/commands/scan.js +30 -6
- package/dist/src/commands/scan.js.map +1 -1
- package/dist/src/commands/score.js +63 -0
- package/dist/src/commands/score.js.map +1 -1
- package/dist/src/commands/serve.js +9 -1
- package/dist/src/commands/serve.js.map +1 -1
- package/dist/src/commands/setup-ci-conversion.d.ts +3 -1
- package/dist/src/commands/setup-ci-conversion.js +23 -8
- package/dist/src/commands/setup-ci-conversion.js.map +1 -1
- package/dist/src/commands/telemetry.js +20 -14
- package/dist/src/commands/telemetry.js.map +1 -1
- package/dist/src/commands/test.js +31 -2
- package/dist/src/commands/test.js.map +1 -1
- package/dist/src/findings.d.ts +1 -0
- package/dist/src/findings.js +42 -1
- package/dist/src/findings.js.map +1 -1
- package/dist/src/history.js +3 -2
- package/dist/src/history.js.map +1 -1
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.js +4 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/integrations/smithery.js +5 -1
- package/dist/src/integrations/smithery.js.map +1 -1
- package/dist/src/receipt.d.ts +100 -0
- package/dist/src/receipt.js +375 -0
- package/dist/src/receipt.js.map +1 -0
- package/dist/src/reporters/attack-sim.d.ts +2 -0
- package/dist/src/reporters/attack-sim.js +89 -0
- package/dist/src/reporters/attack-sim.js.map +1 -0
- package/dist/src/reporters/pr-comment.js +20 -6
- package/dist/src/reporters/pr-comment.js.map +1 -1
- package/dist/src/reporters/sarif.js +11 -1
- package/dist/src/reporters/sarif.js.map +1 -1
- package/dist/src/runner.d.ts +3 -0
- package/dist/src/runner.js +5 -0
- package/dist/src/runner.js.map +1 -1
- package/dist/src/score.js +1 -1
- package/dist/src/score.js.map +1 -1
- package/dist/src/security-profiles.d.ts +13 -0
- package/dist/src/security-profiles.js +59 -0
- package/dist/src/security-profiles.js.map +1 -0
- package/dist/src/storage.js +3 -2
- package/dist/src/storage.js.map +1 -1
- package/dist/src/telemetry.d.ts +10 -0
- package/dist/src/telemetry.js +17 -1
- package/dist/src/telemetry.js.map +1 -1
- package/dist/src/types.d.ts +1 -1
- package/dist/src/utils/files.d.ts +1 -0
- package/dist/src/utils/files.js +21 -0
- package/dist/src/utils/files.js.map +1 -0
- package/dist/src/utils/ids.js +18 -5
- package/dist/src/utils/ids.js.map +1 -1
- package/dist/src/utils/url.d.ts +2 -0
- package/dist/src/utils/url.js +15 -0
- package/dist/src/utils/url.js.map +1 -0
- package/dist/src/validate.js +1 -1
- package/dist/src/validate.js.map +1 -1
- package/docs/10x-agentic-growth-sprint.md +47 -0
- package/docs/agent-ecosystem-promotion-plan.md +244 -0
- package/docs/agent-tasks.md +95 -0
- package/docs/agentic-contributor-outreach.md +52 -0
- package/docs/automatic-ci-integration.md +33 -0
- package/docs/campaign-attribution.md +58 -0
- package/docs/code-scanning-demo.md +57 -0
- package/docs/commercial-boundary.md +83 -0
- package/docs/contributor-proof-cards/README.md +33 -0
- package/docs/contributor-recognition.md +92 -0
- package/docs/examples/nsa-mcp-audit-report.md +372 -0
- package/docs/examples/nsa-mcp-results.sarif +930 -0
- package/docs/examples/nsa-mcp-score.json +13 -0
- package/docs/feishu-lark-mcp.md +1 -1
- package/docs/github-code-scanning-for-mcp.md +1 -1
- package/docs/government-enterprise-pilot.md +93 -0
- package/docs/launch.md +64 -0
- package/docs/mcp-attack-simulator.md +57 -0
- package/docs/mcp-receipts.md +109 -0
- package/docs/mcp-server-safety-index.md +29 -22
- package/docs/nsa-mcp-audit-ci.md +88 -0
- package/docs/procurement-one-pager.md +74 -0
- package/docs/proof.md +5 -2
- package/docs/public-guidance-crosswalk.md +48 -0
- package/docs/receipt-graph.md +54 -0
- package/docs/safety-index/artifacts/antv-chart-server.json +40 -22
- package/docs/safety-index/artifacts/antv-chart-server.md +22 -9
- package/docs/safety-index/artifacts/browsermcp-server.json +36 -18
- package/docs/safety-index/artifacts/browsermcp-server.md +22 -9
- package/docs/safety-index/artifacts/context7-server.json +83 -26
- package/docs/safety-index/artifacts/context7-server.md +33 -20
- package/docs/safety-index/artifacts/cyanheads-git-mcp-server.json +2438 -0
- package/docs/safety-index/artifacts/cyanheads-git-mcp-server.md +176 -0
- package/docs/safety-index/artifacts/everything-server.json +150 -57
- package/docs/safety-index/artifacts/everything-server.md +36 -23
- package/docs/safety-index/artifacts/executeautomation-playwright-server.json +162 -19
- package/docs/safety-index/artifacts/executeautomation-playwright-server.md +25 -12
- package/docs/safety-index/artifacts/filesystem-server.json +221 -19
- package/docs/safety-index/artifacts/filesystem-server.md +24 -11
- package/docs/safety-index/artifacts/kubernetes-server.attack.json +1559 -0
- package/docs/safety-index/artifacts/kubernetes-server.attack.md +43 -0
- package/docs/safety-index/artifacts/kubernetes-server.attack.sarif +2857 -0
- package/docs/safety-index/artifacts/kubernetes-server.json +1573 -0
- package/docs/safety-index/artifacts/kubernetes-server.md +176 -0
- package/docs/safety-index/artifacts/memory-server.json +93 -32
- package/docs/safety-index/artifacts/memory-server.md +45 -25
- package/docs/safety-index/artifacts/opentofu-server.json +44 -18
- package/docs/safety-index/artifacts/opentofu-server.md +23 -10
- package/docs/safety-index/artifacts/playwright-mcp-server.json +177 -21
- package/docs/safety-index/artifacts/playwright-mcp-server.md +27 -14
- package/docs/safety-index/artifacts/promptopia-server.json +45 -19
- package/docs/safety-index/artifacts/promptopia-server.md +21 -8
- package/docs/safety-index/artifacts/puppeteer-server.json +77 -22
- package/docs/safety-index/artifacts/puppeteer-server.md +28 -15
- package/docs/safety-index/artifacts/ref-tools-server.json +36 -18
- package/docs/safety-index/artifacts/ref-tools-server.md +21 -8
- package/docs/safety-index/artifacts/sequential-thinking-server.json +38 -21
- package/docs/safety-index/artifacts/sequential-thinking-server.md +22 -9
- package/docs/safety-index/targets.json +90 -14
- package/docs/security-due-diligence.md +87 -0
- package/docs/setup-ci-doctor.md +13 -1
- package/docs/target-gallery.md +49 -0
- package/docs/target-registry.md +5 -1
- package/docs/tool-call-receipts.md +115 -0
- package/examples/insecure-mcp-server/mcp-observatory.target.json +13 -0
- package/examples/insecure-mcp-server/server.mjs +62 -0
- package/package.json +31 -2
- package/schemas/run-artifact.schema.json +1 -0
- package/scripts/postinstall.mjs +138 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { extractObservatoryFindings } from "../findings.js";
|
|
3
|
+
import { runTarget } from "../index.js";
|
|
4
|
+
import { renderAttackSimulationMarkdown } from "../reporters/attack-sim.js";
|
|
5
|
+
import { renderSarif } from "../reporters/sarif.js";
|
|
6
|
+
import { buildEvent, normalizeCampaign, recordEvent } from "../telemetry.js";
|
|
7
|
+
import { validateRunArtifact } from "../validate.js";
|
|
8
|
+
import { renderActionReceipt } from "../action-receipt.js";
|
|
9
|
+
import { maybePrintCloudCta } from "../commercial.js";
|
|
10
|
+
import { ANSI, c, getBinName, quoteShell, resolveTarget, targetFromCommand, writeOutput } from "./helpers.js";
|
|
11
|
+
import { maybeConvertPassingCheckToCi } from "./setup-ci-conversion.js";
|
|
12
|
+
function extractTrailingAttackFlags(commandArgs, options) {
|
|
13
|
+
const flags = { ...options };
|
|
14
|
+
const targetArgs = [];
|
|
15
|
+
for (let i = 0; i < commandArgs.length; i += 1) {
|
|
16
|
+
const arg = commandArgs[i];
|
|
17
|
+
const assign = arg.match(/^(--baseline|--json|--mode|--output|--sarif)=(.+)$/);
|
|
18
|
+
if (arg === "--") {
|
|
19
|
+
targetArgs.push(...commandArgs.slice(i + 1));
|
|
20
|
+
break;
|
|
21
|
+
}
|
|
22
|
+
else if (assign) {
|
|
23
|
+
const [, name, value] = assign;
|
|
24
|
+
if (name === "--baseline")
|
|
25
|
+
flags.baseline = value;
|
|
26
|
+
if (name === "--json")
|
|
27
|
+
flags.json = value;
|
|
28
|
+
if (name === "--mode")
|
|
29
|
+
flags.mode = value;
|
|
30
|
+
if (name === "--output")
|
|
31
|
+
flags.output = value;
|
|
32
|
+
if (name === "--sarif")
|
|
33
|
+
flags.sarif = value;
|
|
34
|
+
}
|
|
35
|
+
else if (arg === "--baseline") {
|
|
36
|
+
const next = commandArgs[i + 1];
|
|
37
|
+
if (!next)
|
|
38
|
+
throw new Error("--baseline requires a run artifact path.");
|
|
39
|
+
flags.baseline = next;
|
|
40
|
+
i += 1;
|
|
41
|
+
}
|
|
42
|
+
else if (arg === "--fail-on-high") {
|
|
43
|
+
flags.failOnHigh = true;
|
|
44
|
+
}
|
|
45
|
+
else if (arg === "--setup-ci") {
|
|
46
|
+
flags.setupCi = true;
|
|
47
|
+
}
|
|
48
|
+
else if (arg === "--yes") {
|
|
49
|
+
flags.yes = true;
|
|
50
|
+
}
|
|
51
|
+
else if (arg === "--no-setup-ci") {
|
|
52
|
+
flags.noSetupCi = true;
|
|
53
|
+
}
|
|
54
|
+
else if (arg === "--no-ci-sarif") {
|
|
55
|
+
flags.ciSarif = false;
|
|
56
|
+
}
|
|
57
|
+
else if (arg === "--force") {
|
|
58
|
+
flags.force = true;
|
|
59
|
+
}
|
|
60
|
+
else if (arg === "--campaign") {
|
|
61
|
+
const next = commandArgs[i + 1];
|
|
62
|
+
if (!next)
|
|
63
|
+
throw new Error("--campaign requires a campaign slug.");
|
|
64
|
+
flags.campaign = normalizeCampaign(next);
|
|
65
|
+
i += 1;
|
|
66
|
+
}
|
|
67
|
+
else if (arg === "--no-color") {
|
|
68
|
+
// Commander handles the color option; keep it out of the server command.
|
|
69
|
+
}
|
|
70
|
+
else if (arg === "--json") {
|
|
71
|
+
const next = commandArgs[i + 1];
|
|
72
|
+
if (!next)
|
|
73
|
+
throw new Error("--json requires an output file.");
|
|
74
|
+
flags.json = next;
|
|
75
|
+
i += 1;
|
|
76
|
+
}
|
|
77
|
+
else if (arg === "--mode") {
|
|
78
|
+
const next = commandArgs[i + 1];
|
|
79
|
+
if (!next)
|
|
80
|
+
throw new Error("--mode requires a value.");
|
|
81
|
+
flags.mode = next;
|
|
82
|
+
i += 1;
|
|
83
|
+
}
|
|
84
|
+
else if (arg === "--output") {
|
|
85
|
+
const next = commandArgs[i + 1];
|
|
86
|
+
if (!next)
|
|
87
|
+
throw new Error("--output requires an output file.");
|
|
88
|
+
flags.output = next;
|
|
89
|
+
i += 1;
|
|
90
|
+
}
|
|
91
|
+
else if (arg === "--sarif") {
|
|
92
|
+
const next = commandArgs[i + 1];
|
|
93
|
+
if (!next)
|
|
94
|
+
throw new Error("--sarif requires an output file.");
|
|
95
|
+
flags.sarif = next;
|
|
96
|
+
i += 1;
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
targetArgs.push(arg);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return { commandArgs: targetArgs, flags };
|
|
103
|
+
}
|
|
104
|
+
async function readBaseline(path) {
|
|
105
|
+
if (!path)
|
|
106
|
+
return undefined;
|
|
107
|
+
return validateRunArtifact(JSON.parse(await readFile(path, "utf8")));
|
|
108
|
+
}
|
|
109
|
+
function reproductionCommand(options, commandArgs) {
|
|
110
|
+
const parts = ["npx @kryptosai/mcp-observatory", "attack-sim"];
|
|
111
|
+
if (options.target)
|
|
112
|
+
parts.push("--target", quoteShell(options.target));
|
|
113
|
+
if (options.baseline)
|
|
114
|
+
parts.push("--baseline", quoteShell(options.baseline));
|
|
115
|
+
if (!options.target && commandArgs.length > 0)
|
|
116
|
+
parts.push(...commandArgs.map(quoteShell));
|
|
117
|
+
return parts.join(" ");
|
|
118
|
+
}
|
|
119
|
+
function topAttackFindings(artifact) {
|
|
120
|
+
const severityRank = { high: 3, medium: 2, low: 1, info: 0 };
|
|
121
|
+
return extractObservatoryFindings(artifact)
|
|
122
|
+
.filter((finding) => finding.checkId === "attack-sim")
|
|
123
|
+
.sort((a, b) => severityRank[b.severity] - severityRank[a.severity])
|
|
124
|
+
.slice(0, 3);
|
|
125
|
+
}
|
|
126
|
+
export function registerAttackSimCommands(program) {
|
|
127
|
+
program
|
|
128
|
+
.command("attack-sim")
|
|
129
|
+
.passThroughOptions()
|
|
130
|
+
.description("Run safe MCP attack-readiness simulations for Safety Index evidence.")
|
|
131
|
+
.argument("[command...]", "Server command and arguments to run.")
|
|
132
|
+
.option("--target <config>", "Path to a target config JSON file.")
|
|
133
|
+
.option("--baseline <artifact>", "Baseline run artifact for contract drift simulation.")
|
|
134
|
+
.option("--output <file>", "Write the attack simulation Markdown report.")
|
|
135
|
+
.option("--json <file>", "Write the run artifact with attack-sim evidence.")
|
|
136
|
+
.option("--sarif <file>", "Write attack-sim findings as SARIF for GitHub Code Scanning.")
|
|
137
|
+
.option("--mode <mode>", "Simulation mode. Only 'safe' is supported.", "safe")
|
|
138
|
+
.option("--fail-on-high", "Exit nonzero when high-risk attack findings exist.", false)
|
|
139
|
+
.option("--setup-ci", "Offer CI conversion after a successful simulation; use with --yes in non-interactive runs to write files.", false)
|
|
140
|
+
.option("--yes", "Confirm CI conversion without prompting. Only writes when used with --setup-ci.", false)
|
|
141
|
+
.option("--no-setup-ci", "Suppress the post-success CI conversion prompt and hint.")
|
|
142
|
+
.option("--no-ci-sarif", "Generate post-check CI without GitHub Code Scanning SARIF upload.")
|
|
143
|
+
.option("--force", "Overwrite existing generated CI adoption files.", false)
|
|
144
|
+
.option("--campaign <slug>", "Attach a safe campaign/source slug to telemetry for attribution.")
|
|
145
|
+
.option("--no-color", "Disable colored output.")
|
|
146
|
+
.action(async (commandArgs, options) => {
|
|
147
|
+
const extracted = extractTrailingAttackFlags(commandArgs, options);
|
|
148
|
+
commandArgs = extracted.commandArgs;
|
|
149
|
+
options = extracted.flags;
|
|
150
|
+
if (options.campaign)
|
|
151
|
+
options.campaign = normalizeCampaign(options.campaign);
|
|
152
|
+
if (options.mode !== "safe") {
|
|
153
|
+
throw new Error("attack-sim supports only --mode safe.");
|
|
154
|
+
}
|
|
155
|
+
const startedAt = Date.now();
|
|
156
|
+
if (!options.target && commandArgs.length === 0) {
|
|
157
|
+
throw new Error("Provide --target <config.json> or use: mcp-observatory attack-sim <command> [args...]");
|
|
158
|
+
}
|
|
159
|
+
const target = options.target ? await resolveTarget({ target: options.target }) : targetFromCommand(commandArgs);
|
|
160
|
+
const baseline = await readBaseline(options.baseline);
|
|
161
|
+
process.stdout.write(` ${c(ANSI.dim, "⟳")} Running safe attack simulation for ${c(ANSI.bold, target.targetId)}...`);
|
|
162
|
+
const artifact = await runTarget(target, {
|
|
163
|
+
attackSimulation: { baseline },
|
|
164
|
+
securityCheck: true,
|
|
165
|
+
});
|
|
166
|
+
const repro = reproductionCommand(options, commandArgs);
|
|
167
|
+
const attackCheck = artifact.checks.find((check) => check.id === "attack-sim");
|
|
168
|
+
const findings = topAttackFindings(artifact);
|
|
169
|
+
const highCount = extractObservatoryFindings(artifact)
|
|
170
|
+
.filter((finding) => finding.checkId === "attack-sim" && finding.severity === "high").length;
|
|
171
|
+
const icon = attackCheck?.status === "fail" ? c(ANSI.red, "✗") : attackCheck?.status === "partial" ? c(ANSI.yellow, "!") : c(ANSI.green, "✓");
|
|
172
|
+
process.stdout.write(`\r ${icon} ${c(ANSI.bold, target.targetId)} attack-sim: ${attackCheck?.status ?? "skipped"}${" ".repeat(12)}\n`);
|
|
173
|
+
if (attackCheck) {
|
|
174
|
+
process.stdout.write(` ${attackCheck.message}\n`);
|
|
175
|
+
}
|
|
176
|
+
for (const finding of findings) {
|
|
177
|
+
process.stdout.write(` ${c(ANSI.dim, "→")} [${finding.severity}] ${finding.message}\n`);
|
|
178
|
+
}
|
|
179
|
+
process.stdout.write(` ${c(ANSI.dim, "→")} ${renderActionReceipt(artifact).split("\n")[0]}\n`);
|
|
180
|
+
process.stdout.write(`\n ${c(ANSI.bold, "Reproduce:")} ${c(ANSI.cyan, repro)}\n\n`);
|
|
181
|
+
if (options.json) {
|
|
182
|
+
await writeOutput(JSON.stringify(artifact, null, 2), "json", options.json);
|
|
183
|
+
}
|
|
184
|
+
if (options.output) {
|
|
185
|
+
await writeOutput(renderAttackSimulationMarkdown(artifact, repro), "markdown", options.output);
|
|
186
|
+
}
|
|
187
|
+
if (options.sarif) {
|
|
188
|
+
await writeOutput(renderSarif(artifact, { artifactUri: options.json }), "sarif", options.sarif);
|
|
189
|
+
}
|
|
190
|
+
recordEvent(buildEvent("command_complete", "attack-sim", "cli", {
|
|
191
|
+
targetIds: [target.targetId],
|
|
192
|
+
gateResult: attackCheck?.status ?? "skipped",
|
|
193
|
+
securityFindingCount: attackCheck?.evidence[0]?.itemCount ?? 0,
|
|
194
|
+
checkStatuses: attackCheck ? { "attack-sim": attackCheck.status } : undefined,
|
|
195
|
+
executionMs: Date.now() - startedAt,
|
|
196
|
+
campaign: options.campaign,
|
|
197
|
+
}));
|
|
198
|
+
if (artifact.gate === "pass") {
|
|
199
|
+
await maybeConvertPassingCheckToCi({
|
|
200
|
+
artifact,
|
|
201
|
+
bin: getBinName(),
|
|
202
|
+
target,
|
|
203
|
+
targetPath: options.target,
|
|
204
|
+
setupCi: options.setupCi,
|
|
205
|
+
yes: options.yes,
|
|
206
|
+
noSetupCi: options.noSetupCi,
|
|
207
|
+
ciSarif: options.ciSarif,
|
|
208
|
+
force: options.force,
|
|
209
|
+
campaign: options.campaign,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
else if (highCount > 0) {
|
|
213
|
+
maybePrintCloudCta("security");
|
|
214
|
+
}
|
|
215
|
+
if (options.failOnHigh && highCount > 0) {
|
|
216
|
+
process.exitCode = 1;
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=attack-sim.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attack-sim.js","sourceRoot":"","sources":["../../../src/commands/attack-sim.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { auditScore, renderAuditMarkdown, renderAuditSarif, resolveAuditTarget, runAudit, } from "../audit.js";
|
|
4
|
+
import { buildMcpReceipt, receiptFormatFromPath, renderReceipt } from "../receipt.js";
|
|
5
|
+
import { buildEvent, recordEvent } from "../telemetry.js";
|
|
6
|
+
import { ANSI, c } from "./helpers.js";
|
|
7
|
+
function extractTrailingAuditFlags(args, options) {
|
|
8
|
+
const targetArgs = [];
|
|
9
|
+
const nextOptions = { ...options };
|
|
10
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
11
|
+
const arg = args[i];
|
|
12
|
+
if (arg === "--profile") {
|
|
13
|
+
const next = args[i + 1];
|
|
14
|
+
if (!next)
|
|
15
|
+
throw new Error("--profile requires a value.");
|
|
16
|
+
nextOptions.profile = next;
|
|
17
|
+
i += 1;
|
|
18
|
+
}
|
|
19
|
+
else if (arg === "--format") {
|
|
20
|
+
const next = args[i + 1];
|
|
21
|
+
if (!next)
|
|
22
|
+
throw new Error("--format requires a value.");
|
|
23
|
+
nextOptions.format = next;
|
|
24
|
+
i += 1;
|
|
25
|
+
}
|
|
26
|
+
else if (arg === "--output") {
|
|
27
|
+
const next = args[i + 1];
|
|
28
|
+
if (!next)
|
|
29
|
+
throw new Error("--output requires a value.");
|
|
30
|
+
nextOptions.output = next;
|
|
31
|
+
i += 1;
|
|
32
|
+
}
|
|
33
|
+
else if (arg === "--receipt") {
|
|
34
|
+
const next = args[i + 1];
|
|
35
|
+
if (!next)
|
|
36
|
+
throw new Error("--receipt requires a value.");
|
|
37
|
+
nextOptions.receipt = next;
|
|
38
|
+
i += 1;
|
|
39
|
+
}
|
|
40
|
+
else if (arg === "--fail-on-high") {
|
|
41
|
+
nextOptions.failOnHigh = true;
|
|
42
|
+
}
|
|
43
|
+
else if (arg === "--fail-on-critical") {
|
|
44
|
+
nextOptions.failOnCritical = true;
|
|
45
|
+
}
|
|
46
|
+
else if (arg === "--no-color") {
|
|
47
|
+
// Commander handles color globally; keep it out of the target command.
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
targetArgs.push(arg);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return { targetArgs, options: nextOptions };
|
|
54
|
+
}
|
|
55
|
+
async function writeMaybe(filePath, content) {
|
|
56
|
+
if (!filePath) {
|
|
57
|
+
process.stdout.write(`${content}\n`);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
await mkdir(path.dirname(filePath), { recursive: true });
|
|
61
|
+
await writeFile(filePath, `${content}\n`, "utf8");
|
|
62
|
+
process.stdout.write(`Wrote audit ${path.extname(filePath).slice(1) || "report"} to ${filePath}\n`);
|
|
63
|
+
}
|
|
64
|
+
export function registerAuditCommands(program) {
|
|
65
|
+
program
|
|
66
|
+
.command("audit")
|
|
67
|
+
.passThroughOptions()
|
|
68
|
+
.description("Run a profile-mapped MCP security audit.")
|
|
69
|
+
.argument("<target...>", "Target directory/config path or server command.")
|
|
70
|
+
.option("--profile <profile>", "Security profile to apply.", "nsa-mcp")
|
|
71
|
+
.option("--format <format>", "markdown, json, or sarif.", "markdown")
|
|
72
|
+
.option("--output <file>", "Write report to a file instead of stdout.")
|
|
73
|
+
.option("--receipt <file>", "Also write a portable MCP receipt (.json or .md).")
|
|
74
|
+
.option("--fail-on-high", "Exit nonzero when high or critical findings are present.", false)
|
|
75
|
+
.option("--fail-on-critical", "Exit nonzero when critical findings are present.", false)
|
|
76
|
+
.option("--no-color", "Disable colored output.")
|
|
77
|
+
.action(async (targetArgs, options) => {
|
|
78
|
+
const extracted = extractTrailingAuditFlags(targetArgs, options);
|
|
79
|
+
targetArgs = extracted.targetArgs;
|
|
80
|
+
options = extracted.options;
|
|
81
|
+
const startedAt = Date.now();
|
|
82
|
+
const target = await resolveAuditTarget(targetArgs);
|
|
83
|
+
if (options.output) {
|
|
84
|
+
process.stdout.write(`${c(ANSI.dim, "⟳")} Auditing ${c(ANSI.bold, target.targetId)} with profile ${c(ANSI.bold, options.profile)}...\n`);
|
|
85
|
+
}
|
|
86
|
+
const report = await runAudit(target, options.profile);
|
|
87
|
+
const format = options.format;
|
|
88
|
+
if (format === "json") {
|
|
89
|
+
await writeMaybe(options.output, JSON.stringify(report, null, 2));
|
|
90
|
+
}
|
|
91
|
+
else if (format === "sarif") {
|
|
92
|
+
await writeMaybe(options.output, renderAuditSarif(report, options.output ? path.basename(options.output).replace(/\.sarif$/i, ".json") : "mcp-observatory-audit.json"));
|
|
93
|
+
}
|
|
94
|
+
else if (format === "markdown") {
|
|
95
|
+
await writeMaybe(options.output, renderAuditMarkdown(report));
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
throw new Error("Unsupported audit format. Use markdown, json, or sarif.");
|
|
99
|
+
}
|
|
100
|
+
if (options.receipt) {
|
|
101
|
+
const receipt = await buildMcpReceipt(report, target, {
|
|
102
|
+
commandInvoked: `mcp-observatory audit ${targetArgs.join(" ")} --profile ${options.profile} --format ${format}${options.output ? ` --output ${options.output}` : ""} --receipt ${options.receipt}`,
|
|
103
|
+
jsonReportPath: format === "json" ? options.output : undefined,
|
|
104
|
+
markdownReportPath: format === "markdown" ? options.output : undefined,
|
|
105
|
+
sarifPath: format === "sarif" ? options.output : undefined,
|
|
106
|
+
});
|
|
107
|
+
const receiptFormat = receiptFormatFromPath(options.receipt, "json");
|
|
108
|
+
await writeMaybe(options.receipt, renderReceipt(receipt, receiptFormat));
|
|
109
|
+
}
|
|
110
|
+
if (options.output) {
|
|
111
|
+
const score = auditScore(report);
|
|
112
|
+
process.stdout.write(`Trust status: ${score.status} (${score.score}/100), findings: ${score.finding_count}\n`);
|
|
113
|
+
}
|
|
114
|
+
recordEvent(buildEvent("command_complete", "audit", "cli", {
|
|
115
|
+
serversScanned: 1,
|
|
116
|
+
gateResult: report.summary.trust_status,
|
|
117
|
+
executionMs: Date.now() - startedAt,
|
|
118
|
+
securityFlag: true,
|
|
119
|
+
securityFindingCount: report.summary.finding_count,
|
|
120
|
+
targetIds: [target.targetId],
|
|
121
|
+
checkStatuses: Object.fromEntries(report.artifact.checks.map((check) => [check.id, check.status])),
|
|
122
|
+
receiptGenerated: Boolean(options.receipt),
|
|
123
|
+
receiptFormat: options.receipt ? receiptFormatFromPath(options.receipt, "json") : undefined,
|
|
124
|
+
receiptProfile: options.profile,
|
|
125
|
+
}));
|
|
126
|
+
if ((options.failOnCritical === true && report.summary.severity_counts.critical > 0) ||
|
|
127
|
+
(options.failOnHigh === true && (report.summary.severity_counts.critical > 0 || report.summary.severity_counts.high > 0))) {
|
|
128
|
+
process.exitCode = 1;
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../../src/commands/audit.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,11 +12,14 @@ export interface InitCiOptions {
|
|
|
12
12
|
commentOnPr?: boolean;
|
|
13
13
|
setStatus?: boolean;
|
|
14
14
|
sarif?: boolean;
|
|
15
|
+
schedule?: string | boolean;
|
|
15
16
|
actionRef?: string;
|
|
16
17
|
all?: boolean;
|
|
17
18
|
force?: boolean;
|
|
18
19
|
doctor?: boolean;
|
|
20
|
+
fix?: boolean;
|
|
19
21
|
fromLastRun?: boolean;
|
|
22
|
+
campaign?: string;
|
|
20
23
|
}
|
|
21
24
|
export interface InitCiResult {
|
|
22
25
|
workflowStatus: string;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { access, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { buildEvent, recordEvent } from "../telemetry.js";
|
|
3
|
+
import { buildEvent, normalizeCampaign, recordEvent } from "../telemetry.js";
|
|
4
4
|
import { defaultRunsDirectory, findLatestSuccessfulRunArtifact, readArtifact } from "../storage.js";
|
|
5
|
+
import { TOOL_VERSION } from "../version.js";
|
|
5
6
|
import { quoteShell } from "./helpers.js";
|
|
6
7
|
const DEFAULT_WORKFLOW_PATH = ".github/workflows/mcp-observatory.yml";
|
|
7
8
|
const DEFAULT_BADGE_PATH = "docs/mcp-observatory-badge.md";
|
|
@@ -9,7 +10,8 @@ const DEFAULT_TARGET_CONFIG_PATH = "mcp-observatory.target.json";
|
|
|
9
10
|
const DEFAULT_PR_BODY_PATH = "docs/mcp-observatory-pr-body.md";
|
|
10
11
|
const DEFAULT_ISSUE_BODY_PATH = "docs/mcp-observatory-issue.md";
|
|
11
12
|
const DEFAULT_SCORE_BADGE_PATH = "docs/mcp-observatory-score-badge.md";
|
|
12
|
-
const DEFAULT_ACTION_REF =
|
|
13
|
+
const DEFAULT_ACTION_REF = `v${TOOL_VERSION}`;
|
|
14
|
+
const DEFAULT_WEEKLY_CRON = "0 9 * * 1";
|
|
13
15
|
async function exists(filePath) {
|
|
14
16
|
try {
|
|
15
17
|
await access(filePath);
|
|
@@ -19,12 +21,23 @@ async function exists(filePath) {
|
|
|
19
21
|
return false;
|
|
20
22
|
}
|
|
21
23
|
}
|
|
24
|
+
function normalizeSchedule(schedule) {
|
|
25
|
+
if (schedule === undefined || schedule === false)
|
|
26
|
+
return undefined;
|
|
27
|
+
if (schedule === true || schedule === "weekly")
|
|
28
|
+
return DEFAULT_WEEKLY_CRON;
|
|
29
|
+
if (schedule === "daily")
|
|
30
|
+
return "0 9 * * *";
|
|
31
|
+
const value = schedule.trim();
|
|
32
|
+
return value.length > 0 ? value : DEFAULT_WEEKLY_CRON;
|
|
33
|
+
}
|
|
22
34
|
function workflowYaml(options) {
|
|
23
35
|
const command = options.command?.trim();
|
|
24
36
|
const target = options.target?.trim();
|
|
25
37
|
const commentsEnabled = options.commentOnPr === true;
|
|
26
38
|
const statusEnabled = options.setStatus === true;
|
|
27
39
|
const sarifEnabled = options.sarif === true;
|
|
40
|
+
const schedule = normalizeSchedule(options.schedule);
|
|
28
41
|
const actionRef = options.actionRef?.trim() || DEFAULT_ACTION_REF;
|
|
29
42
|
const lines = [
|
|
30
43
|
"name: MCP Observatory",
|
|
@@ -33,10 +46,11 @@ function workflowYaml(options) {
|
|
|
33
46
|
" pull_request:",
|
|
34
47
|
" push:",
|
|
35
48
|
" branches: [main]",
|
|
36
|
-
"",
|
|
37
|
-
"permissions:",
|
|
38
|
-
" contents: read",
|
|
39
49
|
];
|
|
50
|
+
if (schedule) {
|
|
51
|
+
lines.push(" schedule:", ` - cron: ${JSON.stringify(schedule)}`);
|
|
52
|
+
}
|
|
53
|
+
lines.push("", "permissions:", " contents: read");
|
|
40
54
|
if (commentsEnabled)
|
|
41
55
|
lines.push(" pull-requests: write");
|
|
42
56
|
if (statusEnabled)
|
|
@@ -206,17 +220,42 @@ function checkFile(id, label, filePath, content, expectedText) {
|
|
|
206
220
|
}
|
|
207
221
|
function setupCommand(options) {
|
|
208
222
|
const sarif = options.sarif ? " --sarif" : "";
|
|
223
|
+
const schedule = normalizeSchedule(options.schedule)
|
|
224
|
+
? ` --schedule ${quoteShell(String(options.schedule === true ? "weekly" : options.schedule))}`
|
|
225
|
+
: "";
|
|
209
226
|
if (options.target)
|
|
210
|
-
return `npx @kryptosai/mcp-observatory setup-ci --all --target ${options.target}${sarif}`;
|
|
227
|
+
return `npx @kryptosai/mcp-observatory setup-ci --all --target ${options.target}${sarif}${schedule}`;
|
|
211
228
|
if (options.command)
|
|
212
|
-
return `npx @kryptosai/mcp-observatory setup-ci --all --command "${options.command.replaceAll("\"", "\\\"")}"${sarif}`;
|
|
213
|
-
return `npx @kryptosai/mcp-observatory setup-ci --all --command "npx -y <server-package>"${sarif}`;
|
|
229
|
+
return `npx @kryptosai/mcp-observatory setup-ci --all --command "${options.command.replaceAll("\"", "\\\"")}"${sarif}${schedule}`;
|
|
230
|
+
return `npx @kryptosai/mcp-observatory setup-ci --all --command "npx -y <server-package>"${sarif}${schedule}`;
|
|
214
231
|
}
|
|
215
|
-
function initCiOptionsFromLastRunArtifact(artifact, force, sarif) {
|
|
232
|
+
function initCiOptionsFromLastRunArtifact(artifact, force, sarif, schedule) {
|
|
216
233
|
if (artifact.target.adapter !== "local-process")
|
|
217
234
|
return undefined;
|
|
218
235
|
const command = [artifact.target.command, ...artifact.target.args].map(quoteShell).join(" ");
|
|
219
|
-
return { all: true, command, force, sarif };
|
|
236
|
+
return { all: true, command, force, sarif, schedule };
|
|
237
|
+
}
|
|
238
|
+
function inferWorkflowTargetOptions(workflow) {
|
|
239
|
+
const target = workflow?.match(/(^|\n)\s+target:\s+(.+)/)?.[2]?.trim();
|
|
240
|
+
if (target)
|
|
241
|
+
return { target };
|
|
242
|
+
const command = workflow?.match(/(^|\n)\s+command:\s+(.+)/)?.[2]?.trim();
|
|
243
|
+
if (command)
|
|
244
|
+
return { command };
|
|
245
|
+
return {};
|
|
246
|
+
}
|
|
247
|
+
function doctorFixOptions(options, workflow) {
|
|
248
|
+
const inferred = options.command || options.target ? {} : inferWorkflowTargetOptions(workflow);
|
|
249
|
+
return {
|
|
250
|
+
...options,
|
|
251
|
+
...inferred,
|
|
252
|
+
all: true,
|
|
253
|
+
force: true,
|
|
254
|
+
sarif: true,
|
|
255
|
+
schedule: options.schedule ?? "weekly",
|
|
256
|
+
doctor: false,
|
|
257
|
+
fix: false,
|
|
258
|
+
};
|
|
220
259
|
}
|
|
221
260
|
export async function doctorSetupCi(options = {}) {
|
|
222
261
|
const workflowPath = options.workflow ?? DEFAULT_WORKFLOW_PATH;
|
|
@@ -364,14 +403,19 @@ function addInitCiOptions(command) {
|
|
|
364
403
|
.option("--comment-on-pr", "Allow the generated workflow to post PR comments. This adds pull-requests: write permission.", false)
|
|
365
404
|
.option("--set-status", "Allow the generated workflow to set commit statuses. This adds statuses: write permission.", false)
|
|
366
405
|
.option("--sarif", "Upload normalized findings to GitHub Code Scanning. This adds security-events: write permission.", false)
|
|
406
|
+
.option("--schedule [cadence]", "Also run the workflow on a schedule: weekly, daily, or a cron expression.")
|
|
367
407
|
.option("--action-ref <ref>", "Git ref for KryptosAI/mcp-observatory/action. Use a full commit SHA for strict third-party action pinning.", DEFAULT_ACTION_REF)
|
|
368
408
|
.option("--all", "Write the full adoption kit: workflow, badge, target config, PR body, issue body, and score badge instructions.", false)
|
|
369
409
|
.option("--force", "Overwrite existing files.", false)
|
|
370
410
|
.option("--doctor", "Inspect the current repository's MCP Observatory CI adoption state.", false)
|
|
371
|
-
.option("--
|
|
411
|
+
.option("--fix", "With --doctor, repair the adoption kit with deep, security, SARIF, and weekly scheduled checks.", false)
|
|
412
|
+
.option("--from-last-run", "Generate the adoption kit from the latest successful local run artifact.", false)
|
|
413
|
+
.option("--campaign <slug>", "Attach a safe campaign/source slug to telemetry for attribution.");
|
|
372
414
|
}
|
|
373
415
|
function initCiAction(commandName) {
|
|
374
416
|
return async (options) => {
|
|
417
|
+
if (options.campaign)
|
|
418
|
+
options.campaign = normalizeCampaign(options.campaign);
|
|
375
419
|
if (options.doctor) {
|
|
376
420
|
const result = await doctorSetupCi(options);
|
|
377
421
|
process.stdout.write("MCP Observatory CI doctor\n\n");
|
|
@@ -382,14 +426,34 @@ function initCiAction(commandName) {
|
|
|
382
426
|
process.stdout.write(` fix: ${check.fix}\n`);
|
|
383
427
|
}
|
|
384
428
|
process.stdout.write(`\nNext best action:\n ${result.nextCommand}\n`);
|
|
385
|
-
if (
|
|
429
|
+
if (options.fix) {
|
|
430
|
+
const workflow = await readOptional(options.workflow ?? DEFAULT_WORKFLOW_PATH);
|
|
431
|
+
const fixed = await initCi(doctorFixOptions(options, workflow));
|
|
432
|
+
process.stdout.write("\nApplied repair:\n");
|
|
433
|
+
process.stdout.write(`${fixed.workflowStatus}: ${fixed.workflowPath}\n`);
|
|
434
|
+
if (fixed.badgePath && fixed.badgeStatus)
|
|
435
|
+
process.stdout.write(`${fixed.badgeStatus}: ${fixed.badgePath}\n`);
|
|
436
|
+
if (fixed.targetConfigPath && fixed.targetConfigStatus)
|
|
437
|
+
process.stdout.write(`${fixed.targetConfigStatus}: ${fixed.targetConfigPath}\n`);
|
|
438
|
+
if (fixed.prBodyPath && fixed.prBodyStatus)
|
|
439
|
+
process.stdout.write(`${fixed.prBodyStatus}: ${fixed.prBodyPath}\n`);
|
|
440
|
+
if (fixed.issueBodyPath && fixed.issueBodyStatus)
|
|
441
|
+
process.stdout.write(`${fixed.issueBodyStatus}: ${fixed.issueBodyPath}\n`);
|
|
442
|
+
if (fixed.scoreBadgePath && fixed.scoreBadgeStatus)
|
|
443
|
+
process.stdout.write(`${fixed.scoreBadgeStatus}: ${fixed.scoreBadgePath}\n`);
|
|
444
|
+
process.stdout.write("\nAutomated checks: pull requests, pushes to main, and weekly scheduled runs.\n");
|
|
445
|
+
}
|
|
446
|
+
else if (!result.ready) {
|
|
386
447
|
process.exitCode = 1;
|
|
448
|
+
}
|
|
387
449
|
recordEvent(buildEvent("command_complete", commandName, "cli", {
|
|
388
450
|
ciProvider: "github-actions",
|
|
389
451
|
setupCiDoctor: true,
|
|
390
452
|
setupCiReady: result.ready,
|
|
391
453
|
setupCiFailCount: result.checks.filter((check) => check.status === "fail").length,
|
|
392
454
|
setupCiWarnCount: result.checks.filter((check) => check.status === "warn").length,
|
|
455
|
+
setupCiFixApplied: options.fix === true,
|
|
456
|
+
campaign: options.campaign,
|
|
393
457
|
}));
|
|
394
458
|
return;
|
|
395
459
|
}
|
|
@@ -405,7 +469,7 @@ function initCiAction(commandName) {
|
|
|
405
469
|
if (artifact.artifactType !== "run") {
|
|
406
470
|
throw new Error(`Latest artifact is not a run artifact: ${latestPath}`);
|
|
407
471
|
}
|
|
408
|
-
const fromRunOptions = initCiOptionsFromLastRunArtifact(artifact, options.force, options.sarif);
|
|
472
|
+
const fromRunOptions = initCiOptionsFromLastRunArtifact(artifact, options.force, options.sarif, options.schedule);
|
|
409
473
|
if (!fromRunOptions) {
|
|
410
474
|
throw new Error(`Latest successful run does not contain enough target information for setup-ci: ${latestPath}`);
|
|
411
475
|
}
|
|
@@ -441,6 +505,7 @@ function initCiAction(commandName) {
|
|
|
441
505
|
recordEvent(buildEvent("command_complete", commandName, "cli", {
|
|
442
506
|
ciProvider: "github-actions",
|
|
443
507
|
commitStatusSet: !skipped,
|
|
508
|
+
campaign: options.campaign,
|
|
444
509
|
}));
|
|
445
510
|
};
|
|
446
511
|
}
|