@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.
Files changed (2) hide show
  1. package/dist/index.js +95 -13
  2. 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 = /\$\{[A-Z0-9_]+\}|\$[A-Z][A-Z0-9_]{2,}|%\([A-Za-z0-9_.]+\)s|process\.env\.|os\.getenv\(|getenv\(|environ\[/i;
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 = /\(verified\)/i.test(finding.description);
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, 0.15);
846
- reasons.push("environment_interpolation_placeholder");
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, 0.1);
850
- reasons.push("lockfile_or_modules_path");
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
- conf = Math.max(0.05, Math.min(1, conf));
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 primaryOk = conf >= PRIMARY_LOG_THRESHOLD && !hardExcludeFromPrimaryLog(relPath, finding.description, conf, eliteHard);
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),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runsec/mcp",
3
- "version": "1.0.74",
3
+ "version": "1.0.76",
4
4
  "main": "dist/index.js",
5
5
  "files": [
6
6
  "dist",