autotel-cli 0.9.1 → 0.11.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/dist/index.js +86 -0
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -4506,6 +4506,7 @@ const INVESTIGATE_COMMANDS = [
|
|
|
4506
4506
|
investigateCmd("security", "Security telemetry (parent)"),
|
|
4507
4507
|
investigateCmd("security summary", "Security posture: events by severity/category, probe signals, denied responses"),
|
|
4508
4508
|
investigateCmd("security events", "List spans carrying security.* events"),
|
|
4509
|
+
investigateCmd("security mcp", "MCP protocol-boundary security: injection verdicts, output-budget breaches, untrusted-content tool calls"),
|
|
4509
4510
|
investigateCmd("semconv", "Semantic conventions lookup (parent)", { static: true }),
|
|
4510
4511
|
investigateCmd("semconv list", "List semconv namespaces", {
|
|
4511
4512
|
static: true,
|
|
@@ -6073,6 +6074,83 @@ async function runSecurityEvents(flags) {
|
|
|
6073
6074
|
};
|
|
6074
6075
|
});
|
|
6075
6076
|
}
|
|
6077
|
+
/**
|
|
6078
|
+
* `autotel security mcp` — MCP protocol-boundary security signals emitted by
|
|
6079
|
+
* `autotel-mcp-instrumentation`:
|
|
6080
|
+
* - `mcp.security.injection.*` — pluggable classifier verdicts on tool args/results
|
|
6081
|
+
* - `mcp.security.budget.exceeded` — tool output over the configured char budget
|
|
6082
|
+
* - `mcp.tool.untrusted_content` — tools flagged via the WebMCP untrustedContentHint
|
|
6083
|
+
*
|
|
6084
|
+
* Keep these field literals in sync with `MCP_SEMCONV` in
|
|
6085
|
+
* `autotel-mcp-instrumentation/semantic-conventions`.
|
|
6086
|
+
*/
|
|
6087
|
+
const MCP_TOOL_NAME = "gen_ai.tool.name";
|
|
6088
|
+
const MCP_INJECTION_VERDICT = "mcp.security.injection.verdict";
|
|
6089
|
+
const MCP_INJECTION_SOURCE = "mcp.security.injection.source";
|
|
6090
|
+
const MCP_BUDGET_EXCEEDED = "mcp.security.budget.exceeded";
|
|
6091
|
+
const MCP_UNTRUSTED_CONTENT = "mcp.tool.untrusted_content";
|
|
6092
|
+
async function runSecurityMcp(flags) {
|
|
6093
|
+
await runInvestigate("security mcp", flags, async (backend) => {
|
|
6094
|
+
const base = toSpanSearchQuery({
|
|
6095
|
+
serviceName: flags.serviceName,
|
|
6096
|
+
lookbackMinutes: flags.lookbackMinutes,
|
|
6097
|
+
from: flags.from,
|
|
6098
|
+
to: flags.to,
|
|
6099
|
+
limit: flags.limit ?? DEFAULT_LIMIT
|
|
6100
|
+
});
|
|
6101
|
+
const [injection, budget, untrusted] = await Promise.all([
|
|
6102
|
+
backend.searchSpans({
|
|
6103
|
+
...base,
|
|
6104
|
+
filters: [{
|
|
6105
|
+
field: MCP_INJECTION_VERDICT,
|
|
6106
|
+
operator: "exists"
|
|
6107
|
+
}]
|
|
6108
|
+
}),
|
|
6109
|
+
backend.searchSpans({
|
|
6110
|
+
...base,
|
|
6111
|
+
filters: [{
|
|
6112
|
+
field: MCP_BUDGET_EXCEEDED,
|
|
6113
|
+
operator: "exists"
|
|
6114
|
+
}]
|
|
6115
|
+
}),
|
|
6116
|
+
backend.searchSpans({
|
|
6117
|
+
...base,
|
|
6118
|
+
filters: [{
|
|
6119
|
+
field: MCP_UNTRUSTED_CONTENT,
|
|
6120
|
+
operator: "exists"
|
|
6121
|
+
}]
|
|
6122
|
+
})
|
|
6123
|
+
]);
|
|
6124
|
+
const suspected = injection.items.filter((span) => span.tags[MCP_INJECTION_VERDICT] !== void 0 && String(span.tags[MCP_INJECTION_VERDICT]) !== "clean");
|
|
6125
|
+
const untrustedTrue = untrusted.items.filter((span) => String(span.tags[MCP_UNTRUSTED_CONTENT]) === "true");
|
|
6126
|
+
return {
|
|
6127
|
+
window: {
|
|
6128
|
+
lookbackMinutes: flags.lookbackMinutes ?? 60,
|
|
6129
|
+
from: flags.from,
|
|
6130
|
+
to: flags.to,
|
|
6131
|
+
limitPerQuery: flags.limit ?? DEFAULT_LIMIT
|
|
6132
|
+
},
|
|
6133
|
+
injection: {
|
|
6134
|
+
scanned: injection.items.length,
|
|
6135
|
+
suspected: suspected.length,
|
|
6136
|
+
byVerdict: countBy(injection.items, MCP_INJECTION_VERDICT),
|
|
6137
|
+
bySource: countBy(suspected, MCP_INJECTION_SOURCE),
|
|
6138
|
+
byTool: topEntries(countBy(suspected, MCP_TOOL_NAME), 10),
|
|
6139
|
+
sampleTraceIds: sampleTraceIds(suspected)
|
|
6140
|
+
},
|
|
6141
|
+
budgetBreaches: {
|
|
6142
|
+
total: budget.items.length,
|
|
6143
|
+
byTool: topEntries(countBy(budget.items, MCP_TOOL_NAME), 10),
|
|
6144
|
+
byService: countByService(budget.items),
|
|
6145
|
+
sampleTraceIds: sampleTraceIds(budget.items)
|
|
6146
|
+
},
|
|
6147
|
+
untrustedContent: {
|
|
6148
|
+
toolCalls: untrustedTrue.length,
|
|
6149
|
+
byTool: topEntries(countBy(untrustedTrue, MCP_TOOL_NAME), 10)
|
|
6150
|
+
}
|
|
6151
|
+
};
|
|
6152
|
+
});
|
|
6153
|
+
}
|
|
6076
6154
|
function csvIntArg(value) {
|
|
6077
6155
|
return value.split(",").map((part) => Number.parseInt(part.trim(), 10)).filter((n) => !Number.isNaN(n));
|
|
6078
6156
|
}
|
|
@@ -6095,9 +6173,17 @@ function registerSecurityCommands(program) {
|
|
|
6095
6173
|
severity: o.severity
|
|
6096
6174
|
});
|
|
6097
6175
|
});
|
|
6176
|
+
const mcpCmd = addTimeWindowFlags(new Command("mcp")).description("MCP protocol-boundary security: prompt-injection verdicts, output-budget breaches, untrusted-content tool calls").action(async function() {
|
|
6177
|
+
const o = this.optsWithGlobals();
|
|
6178
|
+
await runSecurityMcp({
|
|
6179
|
+
...backendFlagsFromOpts(o),
|
|
6180
|
+
...windowFlagsFromOpts(o)
|
|
6181
|
+
});
|
|
6182
|
+
});
|
|
6098
6183
|
addBackendFlags(securityCmd);
|
|
6099
6184
|
securityCmd.addCommand(summaryCmd);
|
|
6100
6185
|
securityCmd.addCommand(eventsCmd);
|
|
6186
|
+
securityCmd.addCommand(mcpCmd);
|
|
6101
6187
|
program.addCommand(securityCmd);
|
|
6102
6188
|
}
|
|
6103
6189
|
//#endregion
|