@runsec/mcp 1.0.87 → 1.0.88
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 +124 -40
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6066,6 +6066,19 @@ function localhostAlternateUploadUrl(url) {
|
|
|
6066
6066
|
return null;
|
|
6067
6067
|
}
|
|
6068
6068
|
}
|
|
6069
|
+
function hubUploadUrlCandidates(primaryUrl) {
|
|
6070
|
+
const urls = [];
|
|
6071
|
+
const add = (candidate) => {
|
|
6072
|
+
const trimmed = candidate?.trim();
|
|
6073
|
+
if (trimmed && !urls.includes(trimmed)) urls.push(trimmed);
|
|
6074
|
+
};
|
|
6075
|
+
add(primaryUrl);
|
|
6076
|
+
const direct = process.env.RUNSEC_HUB_DIRECT_ORIGIN?.trim();
|
|
6077
|
+
if (direct) add(appendUploadPath(direct));
|
|
6078
|
+
add(localhostAlternateUploadUrl(primaryUrl));
|
|
6079
|
+
add(dockerInternalAlternateUploadUrl(primaryUrl));
|
|
6080
|
+
return urls;
|
|
6081
|
+
}
|
|
6069
6082
|
function dockerInternalAlternateUploadUrl(url) {
|
|
6070
6083
|
if (!isDockerRuntime()) return null;
|
|
6071
6084
|
try {
|
|
@@ -6195,7 +6208,7 @@ function formatHubSyncErrorMessage(error, targetUrl, response, responseBody) {
|
|
|
6195
6208
|
message += `. Body: ${responseBody.trim().slice(0, 300)}`;
|
|
6196
6209
|
}
|
|
6197
6210
|
if (status === "n/a" && /fetch failed|ECONNRESET|ECONNREFUSED|ENOTFOUND|ETIMEDOUT|CERT|SSL|TLS/i.test(err.message + code)) {
|
|
6198
|
-
message += ". Hint:
|
|
6211
|
+
message += ". Hint: if Hub uses Cloudflare Tunnel, set HTTP Host Header to runsec.io (not localhost:3000) in the tunnel public hostname settings, or set RUNSEC_HUB_DIRECT_ORIGIN=http://YOUR_SERVER_IP:3000 in MCP env to bypass the tunnel.";
|
|
6199
6212
|
}
|
|
6200
6213
|
return message;
|
|
6201
6214
|
}
|
|
@@ -6213,12 +6226,17 @@ function slimFindingForHubUpload(finding) {
|
|
|
6213
6226
|
suppression_reason: finding.suppression_reason
|
|
6214
6227
|
};
|
|
6215
6228
|
}
|
|
6216
|
-
function buildSuppressedSummaryForHub(findings) {
|
|
6229
|
+
function buildSuppressedSummaryForHub(findings, includeLocations) {
|
|
6217
6230
|
const byReason = {};
|
|
6218
|
-
const locations = [];
|
|
6219
6231
|
for (const f of findings) {
|
|
6220
6232
|
const reason = String(f.suppression_reason ?? "cognitive_suppressed");
|
|
6221
6233
|
byReason[reason] = (byReason[reason] ?? 0) + 1;
|
|
6234
|
+
}
|
|
6235
|
+
const summary = { total: findings.length, by_reason: byReason };
|
|
6236
|
+
if (!includeLocations) return summary;
|
|
6237
|
+
const locations = [];
|
|
6238
|
+
for (const f of findings) {
|
|
6239
|
+
const reason = String(f.suppression_reason ?? "cognitive_suppressed");
|
|
6222
6240
|
locations.push({
|
|
6223
6241
|
file_path: f.file_path,
|
|
6224
6242
|
line: f.line ?? 0,
|
|
@@ -6228,7 +6246,8 @@ function buildSuppressedSummaryForHub(findings) {
|
|
|
6228
6246
|
severity: f.severity
|
|
6229
6247
|
});
|
|
6230
6248
|
}
|
|
6231
|
-
|
|
6249
|
+
summary.locations = locations;
|
|
6250
|
+
return summary;
|
|
6232
6251
|
}
|
|
6233
6252
|
function slimEngineSummaryForHub(engineSummary) {
|
|
6234
6253
|
if (!engineSummary) return void 0;
|
|
@@ -6252,9 +6271,43 @@ function slimReportMetricsForHub(reportMetrics) {
|
|
|
6252
6271
|
engine_summary: slimEngineSummaryForHub(engine_summary)
|
|
6253
6272
|
};
|
|
6254
6273
|
}
|
|
6274
|
+
function minimalRunsecJsonForHubUpload(result, workspacePath, verdict, metrics, compliance, complianceASVS) {
|
|
6275
|
+
return {
|
|
6276
|
+
source: "runsec_mcp",
|
|
6277
|
+
transport: "minimal",
|
|
6278
|
+
standard: result.standard,
|
|
6279
|
+
workspace_path: workspacePath,
|
|
6280
|
+
verdict,
|
|
6281
|
+
metrics,
|
|
6282
|
+
compliance,
|
|
6283
|
+
complianceASVS,
|
|
6284
|
+
duration_ms: result.duration_ms,
|
|
6285
|
+
findings_count: result.findings_count,
|
|
6286
|
+
cognitive_suppressed_count: result.cognitive_suppressed_count,
|
|
6287
|
+
findings_suppressed_summary: buildSuppressedSummaryForHub(result.findings_suppressed, false),
|
|
6288
|
+
cognitive: result.cognitive,
|
|
6289
|
+
engine_summary: slimEngineSummaryForHub(result.engine_summary),
|
|
6290
|
+
verdict_detail: {
|
|
6291
|
+
status: result.verdict?.status,
|
|
6292
|
+
blocking_findings_count: result.verdict?.blocking_findings_count,
|
|
6293
|
+
primary_findings_count: result.verdict?.primary_findings_count
|
|
6294
|
+
}
|
|
6295
|
+
};
|
|
6296
|
+
}
|
|
6255
6297
|
function slimRunsecJsonForHubUpload(result, reportMetrics, workspacePath, verdict, metrics, compliance, complianceASVS) {
|
|
6298
|
+
if (process.env.RUNSEC_HUB_FULL_UPLOAD !== "1") {
|
|
6299
|
+
return minimalRunsecJsonForHubUpload(
|
|
6300
|
+
result,
|
|
6301
|
+
workspacePath,
|
|
6302
|
+
verdict,
|
|
6303
|
+
metrics,
|
|
6304
|
+
compliance,
|
|
6305
|
+
complianceASVS
|
|
6306
|
+
);
|
|
6307
|
+
}
|
|
6256
6308
|
return {
|
|
6257
6309
|
source: "runsec_mcp",
|
|
6310
|
+
transport: "full",
|
|
6258
6311
|
standard: result.standard,
|
|
6259
6312
|
workspace_path: workspacePath,
|
|
6260
6313
|
verdict,
|
|
@@ -6263,7 +6316,7 @@ function slimRunsecJsonForHubUpload(result, reportMetrics, workspacePath, verdic
|
|
|
6263
6316
|
complianceASVS,
|
|
6264
6317
|
report_metrics: slimReportMetricsForHub(reportMetrics),
|
|
6265
6318
|
findings: result.findings.map(slimFindingForHubUpload),
|
|
6266
|
-
findings_suppressed_summary: buildSuppressedSummaryForHub(result.findings_suppressed),
|
|
6319
|
+
findings_suppressed_summary: buildSuppressedSummaryForHub(result.findings_suppressed, true),
|
|
6267
6320
|
findings_suppressed_count: result.findings_suppressed.length,
|
|
6268
6321
|
duration_ms: result.duration_ms,
|
|
6269
6322
|
findings_count: result.findings_count,
|
|
@@ -6274,6 +6327,32 @@ function slimRunsecJsonForHubUpload(result, reportMetrics, workspacePath, verdic
|
|
|
6274
6327
|
verdict_detail: result.verdict
|
|
6275
6328
|
};
|
|
6276
6329
|
}
|
|
6330
|
+
function toMinimalHubPayload(payload) {
|
|
6331
|
+
const runsecJson = payload.runsecJson ?? {};
|
|
6332
|
+
const minimalJson = {
|
|
6333
|
+
source: "runsec_mcp",
|
|
6334
|
+
transport: "minimal",
|
|
6335
|
+
workspace_path: runsecJson.workspace_path,
|
|
6336
|
+
standard: runsecJson.standard,
|
|
6337
|
+
verdict: payload.verdict,
|
|
6338
|
+
metrics: payload.metrics,
|
|
6339
|
+
compliance: runsecJson.compliance,
|
|
6340
|
+
complianceASVS: runsecJson.complianceASVS,
|
|
6341
|
+
cognitive: runsecJson.cognitive,
|
|
6342
|
+
findings_suppressed_summary: runsecJson.findings_suppressed_summary,
|
|
6343
|
+
findings_suppressed_count: runsecJson.findings_suppressed_count,
|
|
6344
|
+
duration_ms: runsecJson.duration_ms,
|
|
6345
|
+
findings_count: runsecJson.findings_count,
|
|
6346
|
+
engine_summary: runsecJson.engine_summary,
|
|
6347
|
+
verdict_detail: runsecJson.verdict_detail
|
|
6348
|
+
};
|
|
6349
|
+
return {
|
|
6350
|
+
projectId: payload.projectId,
|
|
6351
|
+
verdict: payload.verdict,
|
|
6352
|
+
metrics: payload.metrics,
|
|
6353
|
+
runsecJson: minimalJson
|
|
6354
|
+
};
|
|
6355
|
+
}
|
|
6277
6356
|
function logHubSyncFailure(message, error, targetUrl) {
|
|
6278
6357
|
if (error != null && targetUrl) {
|
|
6279
6358
|
dumpHubNetworkError(error, targetUrl);
|
|
@@ -6427,39 +6506,44 @@ async function uploadScanResultsToHub(payload, apiKey) {
|
|
|
6427
6506
|
console.warn("[runsec] Hub telemetry skipped: API key missing");
|
|
6428
6507
|
return { success: false, message: "Sync failed: API key missing" };
|
|
6429
6508
|
}
|
|
6430
|
-
const
|
|
6431
|
-
const
|
|
6432
|
-
|
|
6433
|
-
|
|
6434
|
-
|
|
6435
|
-
|
|
6436
|
-
|
|
6437
|
-
|
|
6438
|
-
const
|
|
6439
|
-
if (
|
|
6440
|
-
|
|
6441
|
-
|
|
6442
|
-
|
|
6443
|
-
|
|
6444
|
-
|
|
6445
|
-
|
|
6446
|
-
|
|
6447
|
-
|
|
6448
|
-
} catch (err) {
|
|
6449
|
-
lastError = err;
|
|
6450
|
-
errors.push(err);
|
|
6451
|
-
dumpHubNetworkError(err, tryUrl);
|
|
6452
|
-
}
|
|
6453
|
-
}
|
|
6454
|
-
throw lastError;
|
|
6455
|
-
};
|
|
6509
|
+
const primaryUrl = resolveHubUploadUrl();
|
|
6510
|
+
const uploadUrls = hubUploadUrlCandidates(primaryUrl);
|
|
6511
|
+
const payloadVariants = [
|
|
6512
|
+
{ label: "minimal", body: payload },
|
|
6513
|
+
{ label: "minimal-fallback", body: toMinimalHubPayload(payload) }
|
|
6514
|
+
];
|
|
6515
|
+
const seenPayloads = /* @__PURE__ */ new Set();
|
|
6516
|
+
const uniqueVariants = payloadVariants.filter((variant) => {
|
|
6517
|
+
const key = JSON.stringify(variant.body);
|
|
6518
|
+
if (seenPayloads.has(key)) return false;
|
|
6519
|
+
seenPayloads.add(key);
|
|
6520
|
+
return true;
|
|
6521
|
+
});
|
|
6522
|
+
console.error(
|
|
6523
|
+
`[runsec] Hub telemetry upload targets: ${uploadUrls.join(" \u2192 ")} (${uniqueVariants[0] ? Buffer.byteLength(JSON.stringify(uniqueVariants[0].body), "utf8") : 0} bytes, mode=${String(uniqueVariants[0]?.body.runsecJson.transport ?? "minimal")})`
|
|
6524
|
+
);
|
|
6525
|
+
let lastNetworkError = new Error("No upload attempts made");
|
|
6526
|
+
let lastTryUrl = primaryUrl;
|
|
6456
6527
|
try {
|
|
6457
6528
|
let response;
|
|
6458
|
-
|
|
6459
|
-
|
|
6460
|
-
|
|
6461
|
-
|
|
6462
|
-
|
|
6529
|
+
outer: for (const variant of uniqueVariants) {
|
|
6530
|
+
for (const tryUrl of uploadUrls) {
|
|
6531
|
+
lastTryUrl = tryUrl;
|
|
6532
|
+
const bytes = Buffer.byteLength(JSON.stringify(variant.body), "utf8");
|
|
6533
|
+
console.error(`[runsec] Hub upload attempt (${variant.label}, ${bytes} bytes) \u2192 ${tryUrl}`);
|
|
6534
|
+
try {
|
|
6535
|
+
response = await postHubUpload(tryUrl, trimmedKey, variant.body);
|
|
6536
|
+
break outer;
|
|
6537
|
+
} catch (err) {
|
|
6538
|
+
lastNetworkError = err;
|
|
6539
|
+
dumpHubNetworkError(err, tryUrl);
|
|
6540
|
+
if (!isRetryableHubNetworkError(err)) break;
|
|
6541
|
+
}
|
|
6542
|
+
}
|
|
6543
|
+
}
|
|
6544
|
+
if (!response) {
|
|
6545
|
+
const message = formatHubSyncErrorMessage(lastNetworkError, lastTryUrl);
|
|
6546
|
+
logHubSyncFailure(message, lastNetworkError, lastTryUrl);
|
|
6463
6547
|
return { success: false, message };
|
|
6464
6548
|
}
|
|
6465
6549
|
if (!response.ok) {
|
|
@@ -6469,11 +6553,11 @@ async function uploadScanResultsToHub(payload, apiKey) {
|
|
|
6469
6553
|
const httpMessage = detailSnippet || statusLabel;
|
|
6470
6554
|
const message = formatHubSyncErrorMessage(
|
|
6471
6555
|
new Error(httpMessage),
|
|
6472
|
-
|
|
6556
|
+
lastTryUrl,
|
|
6473
6557
|
response,
|
|
6474
6558
|
detailSnippet
|
|
6475
6559
|
);
|
|
6476
|
-
logHubSyncFailure(message, new Error(httpMessage),
|
|
6560
|
+
logHubSyncFailure(message, new Error(httpMessage), lastTryUrl);
|
|
6477
6561
|
return {
|
|
6478
6562
|
success: false,
|
|
6479
6563
|
message
|
|
@@ -6492,8 +6576,8 @@ async function uploadScanResultsToHub(payload, apiKey) {
|
|
|
6492
6576
|
projectId
|
|
6493
6577
|
};
|
|
6494
6578
|
} catch (error) {
|
|
6495
|
-
const message = formatHubSyncErrorMessage(error,
|
|
6496
|
-
logHubSyncFailure(message, error,
|
|
6579
|
+
const message = formatHubSyncErrorMessage(error, lastTryUrl);
|
|
6580
|
+
logHubSyncFailure(message, error, lastTryUrl);
|
|
6497
6581
|
return { success: false, message };
|
|
6498
6582
|
}
|
|
6499
6583
|
}
|