@runsec/mcp 1.0.74 → 1.0.76
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 +95 -13
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -413,7 +413,7 @@ var import_node_fs3 = __toESM(require("fs"));
|
|
|
413
413
|
var import_node_path3 = __toESM(require("path"));
|
|
414
414
|
|
|
415
415
|
// src/engine/secretHeuristics.ts
|
|
416
|
-
var ENV_INTERP_RE =
|
|
416
|
+
var ENV_INTERP_RE = /(?:\$\{[A-Z0-9_]+\}|\$[A-Z][A-Z0-9_]{2,}|%\([A-Za-z0-9_.]+\)s|process\.env\.|os\.getenv\(|getenv\()/i;
|
|
417
417
|
var LOCKFILE_BASENAMES = /^(?:poetry\.lock|package-lock\.json|pnpm-lock\.yaml|yarn\.lock|cargo\.lock|composer\.lock|gemfile\.lock)$/i;
|
|
418
418
|
function hasEnvironmentInterpolation(text) {
|
|
419
419
|
return ENV_INTERP_RE.test(text);
|
|
@@ -431,8 +431,54 @@ function isLockfileOrModulesPath(relPath) {
|
|
|
431
431
|
function isTrufflehogVerified(verifiedFlag, description) {
|
|
432
432
|
return verifiedFlag || /\(verified\)/i.test(description);
|
|
433
433
|
}
|
|
434
|
+
var LOCKFILE_LAYOUT_RE = /(?:poetry\.lock|package-lock\.json|pnpm-lock\.yaml|yarn\.lock|\.xib|\.storyboard|\.docx)/i;
|
|
435
|
+
function isLockfileLayoutArtifactPath(relPath) {
|
|
436
|
+
return LOCKFILE_LAYOUT_RE.test(relPath.replace(/\\/g, "/"));
|
|
437
|
+
}
|
|
434
438
|
|
|
435
439
|
// src/engine/cognitiveEngine.ts
|
|
440
|
+
var NUCLEAR_HARD_DROP_CONFIDENCE = 0.01;
|
|
441
|
+
var CUSTOMREGEX_UNVERIFIED_CAP = 0.1;
|
|
442
|
+
function findingIsVerified(finding) {
|
|
443
|
+
if (finding.verified === true) return true;
|
|
444
|
+
return isTrufflehogVerified(false, finding.description) || /\(verified\)/i.test(finding.match_text ?? "");
|
|
445
|
+
}
|
|
446
|
+
function applyNuclearHardDrop(finding, relPath) {
|
|
447
|
+
const matchText = finding.match_text ?? "";
|
|
448
|
+
const snippet = finding.snippet ?? "";
|
|
449
|
+
if (ENV_INTERP_RE.test(matchText) || ENV_INTERP_RE.test(snippet)) {
|
|
450
|
+
return { cap: NUCLEAR_HARD_DROP_CONFIDENCE, reason: "env_variable_interpolation" };
|
|
451
|
+
}
|
|
452
|
+
if (!findingIsVerified(finding) && LOCKFILE_LAYOUT_RE.test(relPath)) {
|
|
453
|
+
return { cap: NUCLEAR_HARD_DROP_CONFIDENCE, reason: "lockfile_or_layout_artifact" };
|
|
454
|
+
}
|
|
455
|
+
return null;
|
|
456
|
+
}
|
|
457
|
+
function isCustomRegexFinding(finding) {
|
|
458
|
+
const detector = String(finding.detector_name ?? "").trim().toLowerCase();
|
|
459
|
+
if (detector === "customregex") return true;
|
|
460
|
+
const title = String(finding.title ?? finding.description ?? "");
|
|
461
|
+
if (/customregex/i.test(title)) return true;
|
|
462
|
+
return /(?:^|\.)customregex$/i.test(finding.rule_id) || finding.rule_id.includes("trufflehog.customregex");
|
|
463
|
+
}
|
|
464
|
+
function looksLikeHighEntropyRawToken(finding) {
|
|
465
|
+
const blob = `${finding.match_text ?? ""} ${finding.snippet ?? ""}`.trim();
|
|
466
|
+
if (blob.length < 24 || ENV_INTERP_RE.test(blob)) return false;
|
|
467
|
+
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(
|
|
468
|
+
blob
|
|
469
|
+
)) {
|
|
470
|
+
return true;
|
|
471
|
+
}
|
|
472
|
+
const compact = blob.replace(/\s+/g, "");
|
|
473
|
+
if (compact.length >= 40 && /^[A-Za-z0-9+/=_-]+$/.test(compact)) return true;
|
|
474
|
+
return false;
|
|
475
|
+
}
|
|
476
|
+
function customRegexNeedsBaseClamp(finding) {
|
|
477
|
+
if (!isCustomRegexFinding(finding)) return false;
|
|
478
|
+
if (findingIsVerified(finding)) return false;
|
|
479
|
+
if (looksLikeHighEntropyRawToken(finding)) return false;
|
|
480
|
+
return true;
|
|
481
|
+
}
|
|
436
482
|
var PRIMARY_LOG_THRESHOLD = 0.8;
|
|
437
483
|
var CONFIDENCE_THRESHOLD = PRIMARY_LOG_THRESHOLD;
|
|
438
484
|
var ELITE_LOW_CONFIDENCE_CAP = 0.28;
|
|
@@ -823,9 +869,13 @@ function attackPathConcrete(finding, confidence, eliteHard, allReasons, snippet)
|
|
|
823
869
|
return confidence >= PRIMARY_LOG_THRESHOLD;
|
|
824
870
|
}
|
|
825
871
|
function baseConfidenceForFinding(finding, phase1, relPath, category, repoRoot) {
|
|
872
|
+
const nuclear = applyNuclearHardDrop(finding, relPath);
|
|
873
|
+
if (nuclear) {
|
|
874
|
+
return [nuclear.cap, [nuclear.reason]];
|
|
875
|
+
}
|
|
826
876
|
const reasons = [];
|
|
827
877
|
let score = category === "secrets" ? 0.9 : category === "dependencies" ? 0.78 : 0.82;
|
|
828
|
-
const title = finding.description;
|
|
878
|
+
const title = finding.title ?? finding.description;
|
|
829
879
|
const sev = (finding.severity || "").toUpperCase();
|
|
830
880
|
if (category === "code") {
|
|
831
881
|
if (sev === "CRITICAL" || sev === "ERROR") score = 0.92;
|
|
@@ -833,23 +883,27 @@ function baseConfidenceForFinding(finding, phase1, relPath, category, repoRoot)
|
|
|
833
883
|
else score = 0.55;
|
|
834
884
|
} else if (category === "secrets") {
|
|
835
885
|
if (sev === "CRITICAL") score = 0.95;
|
|
836
|
-
else if (finding.match_text.includes("(verified)")) score = 0.93;
|
|
886
|
+
else if (findingIsVerified(finding) || finding.match_text.includes("(verified)")) score = 0.93;
|
|
837
887
|
if (/pii\s+email/i.test(title) || finding.rule_id.includes("pii-email")) {
|
|
838
|
-
const piiVerified =
|
|
888
|
+
const piiVerified = findingIsVerified(finding);
|
|
839
889
|
score = Math.min(score, piiVerified ? 0.45 : 0.32);
|
|
840
890
|
reasons.push("pii_email_deprioritized");
|
|
841
891
|
}
|
|
842
892
|
if (isUnverifiedTrufflehogSecret(finding)) {
|
|
843
893
|
const secretBlob = `${finding.match_text} ${finding.snippet ?? ""}`;
|
|
844
894
|
if (hasEnvironmentInterpolation(secretBlob)) {
|
|
845
|
-
score = Math.min(score,
|
|
846
|
-
reasons.push("
|
|
895
|
+
score = Math.min(score, NUCLEAR_HARD_DROP_CONFIDENCE);
|
|
896
|
+
reasons.push("env_variable_interpolation");
|
|
847
897
|
}
|
|
848
|
-
if (isLockfileOrModulesPath(relPath)) {
|
|
849
|
-
score = Math.min(score,
|
|
850
|
-
reasons.push("
|
|
898
|
+
if (isLockfileOrModulesPath(relPath) || isLockfileLayoutArtifactPath(relPath)) {
|
|
899
|
+
score = Math.min(score, NUCLEAR_HARD_DROP_CONFIDENCE);
|
|
900
|
+
reasons.push("lockfile_or_layout_artifact");
|
|
851
901
|
}
|
|
852
902
|
}
|
|
903
|
+
if (customRegexNeedsBaseClamp(finding)) {
|
|
904
|
+
score = Math.min(score, CUSTOMREGEX_UNVERIFIED_CAP);
|
|
905
|
+
reasons.push("customregex_unverified_clamped");
|
|
906
|
+
}
|
|
853
907
|
}
|
|
854
908
|
const libs = phase1.protection_libs_detected ?? [];
|
|
855
909
|
if (libs.length && protectionMatchesMetric(title, libs)) {
|
|
@@ -923,6 +977,19 @@ function downgradeSeverity(severity, cap) {
|
|
|
923
977
|
function enrichAuditFinding(repoRoot, finding, opts) {
|
|
924
978
|
const applyFp = opts?.applyFalsePositiveFilter ?? !isCalibrationTestbedPath(finding.file_path);
|
|
925
979
|
const relPath = finding.file_path.replace(/\\/g, "/");
|
|
980
|
+
const nuclearDrop = applyNuclearHardDrop(finding, relPath);
|
|
981
|
+
if (nuclearDrop) {
|
|
982
|
+
return {
|
|
983
|
+
...finding,
|
|
984
|
+
severity: downgradeSeverity(finding.severity, "LOW"),
|
|
985
|
+
confidence_score: nuclearDrop.cap,
|
|
986
|
+
confidence_reasons: [nuclearDrop.reason],
|
|
987
|
+
primary_log_eligible: false,
|
|
988
|
+
suppressed: true,
|
|
989
|
+
suppression_reason: nuclearDrop.reason,
|
|
990
|
+
attack_path_concrete: false
|
|
991
|
+
};
|
|
992
|
+
}
|
|
926
993
|
const phase1 = phase1ContextResearch(repoRoot, relPath);
|
|
927
994
|
let [conf, reasons] = baseConfidenceForFinding(finding, phase1, relPath, finding.category, repoRoot);
|
|
928
995
|
const [boost, boostReason] = comparativeAnalysisBoost(repoRoot, relPath, applyFp);
|
|
@@ -947,11 +1014,18 @@ function enrichAuditFinding(repoRoot, finding, opts) {
|
|
|
947
1014
|
}
|
|
948
1015
|
}
|
|
949
1016
|
}
|
|
950
|
-
|
|
1017
|
+
const postNuclear = applyNuclearHardDrop(finding, relPath);
|
|
1018
|
+
if (postNuclear) {
|
|
1019
|
+
conf = Math.min(conf, postNuclear.cap);
|
|
1020
|
+
if (!reasons.includes(postNuclear.reason)) reasons.push(postNuclear.reason);
|
|
1021
|
+
} else {
|
|
1022
|
+
conf = Math.max(0.05, Math.min(1, conf));
|
|
1023
|
+
}
|
|
951
1024
|
const snippetL = snippetLower(finding);
|
|
952
1025
|
const attackConcrete = attackPathConcrete(finding, conf, eliteHard, reasons, snippetL);
|
|
953
1026
|
const critique = selfCritique(finding, conf, phase1);
|
|
954
|
-
const
|
|
1027
|
+
const hardDropped = Boolean(postNuclear);
|
|
1028
|
+
const primaryOk = !hardDropped && conf >= PRIMARY_LOG_THRESHOLD && !hardExcludeFromPrimaryLog(relPath, finding.description, conf, eliteHard);
|
|
955
1029
|
let severity = finding.severity;
|
|
956
1030
|
const originalSeverity = finding.original_severity ?? finding.severity;
|
|
957
1031
|
if (reasons.includes("phase1_protection_lib_present") && conf <= 0.25) {
|
|
@@ -959,6 +1033,7 @@ function enrichAuditFinding(repoRoot, finding, opts) {
|
|
|
959
1033
|
} else if (!primaryOk && conf < PRIMARY_LOG_THRESHOLD) {
|
|
960
1034
|
severity = downgradeSeverity(severity, critique.suggested_severity_cap);
|
|
961
1035
|
}
|
|
1036
|
+
const forceSuppressed = hardDropped;
|
|
962
1037
|
return {
|
|
963
1038
|
...finding,
|
|
964
1039
|
severity,
|
|
@@ -966,6 +1041,10 @@ function enrichAuditFinding(repoRoot, finding, opts) {
|
|
|
966
1041
|
confidence_score: Math.round(conf * 1e3) / 1e3,
|
|
967
1042
|
confidence_reasons: reasons,
|
|
968
1043
|
primary_log_eligible: primaryOk,
|
|
1044
|
+
...forceSuppressed ? {
|
|
1045
|
+
suppressed: true,
|
|
1046
|
+
suppression_reason: postNuclear.reason
|
|
1047
|
+
} : {},
|
|
969
1048
|
attack_path_concrete: attackConcrete,
|
|
970
1049
|
cognitive: {
|
|
971
1050
|
phase1_context_research: phase1,
|
|
@@ -1063,8 +1142,8 @@ function buildVerdict(primaryFindings) {
|
|
|
1063
1142
|
function applyCognitivePipeline(workspaceRoot, findings) {
|
|
1064
1143
|
const enriched = findings.map((f) => enrichAuditFinding(workspaceRoot, f));
|
|
1065
1144
|
const { kept, duplicates } = deduplicateSameLineFindings(enriched);
|
|
1066
|
-
const primary = kept.filter((f) => f.primary_log_eligible);
|
|
1067
|
-
const suppressed = kept.filter((f) => !f.primary_log_eligible).concat(duplicates);
|
|
1145
|
+
const primary = kept.filter((f) => f.primary_log_eligible && !f.suppressed);
|
|
1146
|
+
const suppressed = kept.filter((f) => !f.primary_log_eligible || f.suppressed).concat(duplicates);
|
|
1068
1147
|
return {
|
|
1069
1148
|
primary,
|
|
1070
1149
|
suppressed,
|
|
@@ -1708,6 +1787,9 @@ function mapTrufflehogFindings(rows, workspaceRoot) {
|
|
|
1708
1787
|
asvsTrace: "V6.4.1",
|
|
1709
1788
|
severity,
|
|
1710
1789
|
description,
|
|
1790
|
+
title: description,
|
|
1791
|
+
detector_name: detector,
|
|
1792
|
+
verified,
|
|
1711
1793
|
file_path: rel,
|
|
1712
1794
|
line,
|
|
1713
1795
|
match_text: display.slice(0, 200),
|