@runsec/mcp 1.0.79 → 1.0.80
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 +92 -7
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -435,6 +435,26 @@ var LOCKFILE_LAYOUT_RE = /(?:poetry\.lock|package-lock\.json|pnpm-lock\.yaml|yar
|
|
|
435
435
|
function isLockfileLayoutArtifactPath(relPath) {
|
|
436
436
|
return LOCKFILE_LAYOUT_RE.test(relPath.replace(/\\/g, "/"));
|
|
437
437
|
}
|
|
438
|
+
var DEV_GENERIC_CREDENTIAL_RE = /(?:postgres:postgres|root:root|root:password|admin:admin|user:password|test:test|guest:guest|changeme:changeme)/i;
|
|
439
|
+
var DEV_DB_HOST_RE = /@(?:dev_db|dev-db|localhost|127\.0\.0\.1|0\.0\.0\.0|host\.docker\.internal|\.local\b|docker\.internal)(?::|\/|$)/i;
|
|
440
|
+
var DEV_DATABASE_URI_RE = /(?:postgres(?:ql)?|mysql|mongodb(?:\+srv)?|redis):\/\/(?:[^:@\s]+:[^@\s]+@)?(?:dev_db|dev-db|localhost|127\.0\.0\.1|host\.docker\.internal)/i;
|
|
441
|
+
function blobHasDevDatabaseSecret(text) {
|
|
442
|
+
const blob = (text ?? "").trim();
|
|
443
|
+
if (!blob) return false;
|
|
444
|
+
if (DEV_GENERIC_CREDENTIAL_RE.test(blob)) return true;
|
|
445
|
+
if (DEV_DB_HOST_RE.test(blob)) return true;
|
|
446
|
+
if (DEV_DATABASE_URI_RE.test(blob)) return true;
|
|
447
|
+
return false;
|
|
448
|
+
}
|
|
449
|
+
function findingHasDevDatabaseSecret(finding) {
|
|
450
|
+
const parts = [
|
|
451
|
+
finding.match_text ?? "",
|
|
452
|
+
finding.snippet ?? "",
|
|
453
|
+
finding.description ?? "",
|
|
454
|
+
finding.title ?? ""
|
|
455
|
+
];
|
|
456
|
+
return parts.some((p) => blobHasDevDatabaseSecret(p));
|
|
457
|
+
}
|
|
438
458
|
function findingBlobHasEnvInterpolation(finding) {
|
|
439
459
|
const parts = [
|
|
440
460
|
finding.match_text ?? "",
|
|
@@ -458,6 +478,9 @@ function applyNuclearHardDrop(finding, relPath) {
|
|
|
458
478
|
if (ENV_INTERP_RE.test(matchText) || ENV_INTERP_RE.test(snippet) || findingBlobHasEnvInterpolation(finding)) {
|
|
459
479
|
return { cap: NUCLEAR_HARD_DROP_CONFIDENCE, reason: "env_variable_interpolation" };
|
|
460
480
|
}
|
|
481
|
+
if (findingHasDevDatabaseSecret(finding)) {
|
|
482
|
+
return { cap: NUCLEAR_HARD_DROP_CONFIDENCE, reason: "dev_database_placeholder" };
|
|
483
|
+
}
|
|
461
484
|
if (!findingIsVerified(finding) && (LOCKFILE_LAYOUT_RE.test(relPath) || isLockfileOrModulesPath(relPath) || isLockfileLayoutArtifactPath(relPath))) {
|
|
462
485
|
return { cap: NUCLEAR_HARD_DROP_CONFIDENCE, reason: "lockfile_or_layout_artifact" };
|
|
463
486
|
}
|
|
@@ -496,7 +519,8 @@ function isCustomRegexFinding(finding) {
|
|
|
496
519
|
}
|
|
497
520
|
function looksLikeHighEntropyRawToken(finding) {
|
|
498
521
|
const blob = `${finding.match_text ?? ""} ${finding.snippet ?? ""}`.trim();
|
|
499
|
-
if (blob.length < 24 || ENV_INTERP_RE.test(blob)) return false;
|
|
522
|
+
if (blob.length < 24 || ENV_INTERP_RE.test(blob) || findingHasDevDatabaseSecret(finding)) return false;
|
|
523
|
+
if (/^(?:postgres(?:ql)?|mysql|mongodb|redis):\/\//i.test(blob)) return false;
|
|
500
524
|
if (/^(?:ghp_|gho_|github_pat_|glpat-|sk-[a-zA-Z0-9]{10,}|AKIA[0-9A-Z]{16}|xox[baprs]-|eyJ[A-Za-z0-9_-]{10,}\.)/i.test(
|
|
501
525
|
blob
|
|
502
526
|
)) {
|
|
@@ -1821,6 +1845,7 @@ function mapTrufflehogFindings(rows, workspaceRoot) {
|
|
|
1821
1845
|
if (isLockfileOrModulesPath(rel)) continue;
|
|
1822
1846
|
const blob = `${display} ${rawSecret} ${description}`;
|
|
1823
1847
|
if (hasEnvironmentInterpolation(blob)) continue;
|
|
1848
|
+
if (blobHasDevDatabaseSecret(blob)) continue;
|
|
1824
1849
|
if (detector.toLowerCase() === "customregex") continue;
|
|
1825
1850
|
}
|
|
1826
1851
|
const severity = severityForSecret(detector, verified);
|
|
@@ -5947,6 +5972,10 @@ function dockerInternalAlternateUploadUrl(url) {
|
|
|
5947
5972
|
}
|
|
5948
5973
|
}
|
|
5949
5974
|
|
|
5975
|
+
// src/telemetryClient.ts
|
|
5976
|
+
var import_node_http = __toESM(require("http"));
|
|
5977
|
+
var import_node_https = __toESM(require("https"));
|
|
5978
|
+
|
|
5950
5979
|
// src/complianceScores.ts
|
|
5951
5980
|
var SEVERITY_PENALTY = {
|
|
5952
5981
|
CRITICAL: 15,
|
|
@@ -6055,14 +6084,70 @@ function logHubSyncFailure(message, error, targetUrl) {
|
|
|
6055
6084
|
}
|
|
6056
6085
|
console.error(`[runsec] Hub telemetry upload error (scan saved locally): ${message}`);
|
|
6057
6086
|
}
|
|
6058
|
-
|
|
6059
|
-
return
|
|
6060
|
-
|
|
6061
|
-
|
|
6062
|
-
|
|
6063
|
-
|
|
6087
|
+
function postHubUploadNodeHttp(url, apiKey, payload) {
|
|
6088
|
+
return new Promise((resolve, reject) => {
|
|
6089
|
+
const body = JSON.stringify(payload);
|
|
6090
|
+
let parsed;
|
|
6091
|
+
try {
|
|
6092
|
+
parsed = new URL(url);
|
|
6093
|
+
} catch (err) {
|
|
6094
|
+
reject(err);
|
|
6095
|
+
return;
|
|
6096
|
+
}
|
|
6097
|
+
const headers = {
|
|
6098
|
+
...hubAuthHeaders(apiKey),
|
|
6099
|
+
"Content-Length": String(Buffer.byteLength(body))
|
|
6100
|
+
};
|
|
6101
|
+
const lib = parsed.protocol === "https:" ? import_node_https.default : import_node_http.default;
|
|
6102
|
+
const port = parsed.port !== "" ? Number(parsed.port) : parsed.protocol === "https:" ? 443 : 80;
|
|
6103
|
+
const req = lib.request(
|
|
6104
|
+
{
|
|
6105
|
+
hostname: parsed.hostname,
|
|
6106
|
+
port,
|
|
6107
|
+
path: `${parsed.pathname}${parsed.search}`,
|
|
6108
|
+
method: "POST",
|
|
6109
|
+
headers,
|
|
6110
|
+
timeout: HUB_UPLOAD_TIMEOUT_MS
|
|
6111
|
+
},
|
|
6112
|
+
(res) => {
|
|
6113
|
+
const chunks = [];
|
|
6114
|
+
res.on("data", (chunk) => chunks.push(chunk));
|
|
6115
|
+
res.on("end", () => {
|
|
6116
|
+
const text = Buffer.concat(chunks).toString("utf8");
|
|
6117
|
+
const status = res.statusCode ?? 0;
|
|
6118
|
+
resolve({
|
|
6119
|
+
ok: status >= 200 && status < 300,
|
|
6120
|
+
status,
|
|
6121
|
+
statusText: res.statusMessage ?? "",
|
|
6122
|
+
text: async () => text,
|
|
6123
|
+
json: async () => JSON.parse(text || "{}")
|
|
6124
|
+
});
|
|
6125
|
+
});
|
|
6126
|
+
}
|
|
6127
|
+
);
|
|
6128
|
+
req.on("error", reject);
|
|
6129
|
+
req.on("timeout", () => {
|
|
6130
|
+
req.destroy();
|
|
6131
|
+
reject(Object.assign(new Error("Hub upload request timeout"), { code: "ETIMEDOUT" }));
|
|
6132
|
+
});
|
|
6133
|
+
req.write(body);
|
|
6134
|
+
req.end();
|
|
6064
6135
|
});
|
|
6065
6136
|
}
|
|
6137
|
+
async function postHubUpload(url, apiKey, payload) {
|
|
6138
|
+
try {
|
|
6139
|
+
return await fetch(url, {
|
|
6140
|
+
method: "POST",
|
|
6141
|
+
headers: hubAuthHeaders(apiKey),
|
|
6142
|
+
body: JSON.stringify(payload),
|
|
6143
|
+
signal: AbortSignal.timeout(HUB_UPLOAD_TIMEOUT_MS)
|
|
6144
|
+
});
|
|
6145
|
+
} catch (fetchError) {
|
|
6146
|
+
console.error("[runsec] fetch() failed \u2014 retrying Hub upload via node:http(s)");
|
|
6147
|
+
dumpHubNetworkError(fetchError, url);
|
|
6148
|
+
return postHubUploadNodeHttp(url, apiKey, payload);
|
|
6149
|
+
}
|
|
6150
|
+
}
|
|
6066
6151
|
function countSeverityMetrics(findings) {
|
|
6067
6152
|
const metrics = { critical: 0, high: 0, medium: 0, low: 0, total: 0 };
|
|
6068
6153
|
for (const f of findings) {
|