@runsec/mcp 1.0.75 → 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 +82 -47
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -413,8 +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
|
|
417
|
-
var ENV_INTERP_RE = STRICT_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;
|
|
418
417
|
var LOCKFILE_BASENAMES = /^(?:poetry\.lock|package-lock\.json|pnpm-lock\.yaml|yarn\.lock|cargo\.lock|composer\.lock|gemfile\.lock)$/i;
|
|
419
418
|
function hasEnvironmentInterpolation(text) {
|
|
420
419
|
return ENV_INTERP_RE.test(text);
|
|
@@ -432,37 +431,54 @@ function isLockfileOrModulesPath(relPath) {
|
|
|
432
431
|
function isTrufflehogVerified(verifiedFlag, description) {
|
|
433
432
|
return verifiedFlag || /\(verified\)/i.test(description);
|
|
434
433
|
}
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
if (base.endsWith(".storyboard") || base.endsWith(".xib") || base.endsWith(".docx")) return true;
|
|
439
|
-
if (base.endsWith(".lock") || /\.lock$/i.test(base)) return true;
|
|
440
|
-
return false;
|
|
441
|
-
}
|
|
442
|
-
function matchTextHasStrictEnvInterpolation(matchText, snippet) {
|
|
443
|
-
const m = (matchText ?? "").trim();
|
|
444
|
-
const s = (snippet ?? "").trim();
|
|
445
|
-
if (m && STRICT_ENV_INTERP_RE.test(m)) return true;
|
|
446
|
-
if (s && STRICT_ENV_INTERP_RE.test(s)) return true;
|
|
447
|
-
return false;
|
|
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, "/"));
|
|
448
437
|
}
|
|
449
438
|
|
|
450
439
|
// src/engine/cognitiveEngine.ts
|
|
451
|
-
var
|
|
452
|
-
var
|
|
453
|
-
function
|
|
454
|
-
if (
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
)
|
|
461
|
-
|
|
462
|
-
|
|
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" };
|
|
463
454
|
}
|
|
464
455
|
return null;
|
|
465
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
|
+
}
|
|
466
482
|
var PRIMARY_LOG_THRESHOLD = 0.8;
|
|
467
483
|
var CONFIDENCE_THRESHOLD = PRIMARY_LOG_THRESHOLD;
|
|
468
484
|
var ELITE_LOW_CONFIDENCE_CAP = 0.28;
|
|
@@ -853,13 +869,13 @@ function attackPathConcrete(finding, confidence, eliteHard, allReasons, snippet)
|
|
|
853
869
|
return confidence >= PRIMARY_LOG_THRESHOLD;
|
|
854
870
|
}
|
|
855
871
|
function baseConfidenceForFinding(finding, phase1, relPath, category, repoRoot) {
|
|
856
|
-
const
|
|
857
|
-
if (
|
|
858
|
-
return [
|
|
872
|
+
const nuclear = applyNuclearHardDrop(finding, relPath);
|
|
873
|
+
if (nuclear) {
|
|
874
|
+
return [nuclear.cap, [nuclear.reason]];
|
|
859
875
|
}
|
|
860
876
|
const reasons = [];
|
|
861
877
|
let score = category === "secrets" ? 0.9 : category === "dependencies" ? 0.78 : 0.82;
|
|
862
|
-
const title = finding.description;
|
|
878
|
+
const title = finding.title ?? finding.description;
|
|
863
879
|
const sev = (finding.severity || "").toUpperCase();
|
|
864
880
|
if (category === "code") {
|
|
865
881
|
if (sev === "CRITICAL" || sev === "ERROR") score = 0.92;
|
|
@@ -867,23 +883,27 @@ function baseConfidenceForFinding(finding, phase1, relPath, category, repoRoot)
|
|
|
867
883
|
else score = 0.55;
|
|
868
884
|
} else if (category === "secrets") {
|
|
869
885
|
if (sev === "CRITICAL") score = 0.95;
|
|
870
|
-
else if (finding.match_text.includes("(verified)")) score = 0.93;
|
|
886
|
+
else if (findingIsVerified(finding) || finding.match_text.includes("(verified)")) score = 0.93;
|
|
871
887
|
if (/pii\s+email/i.test(title) || finding.rule_id.includes("pii-email")) {
|
|
872
|
-
const piiVerified =
|
|
888
|
+
const piiVerified = findingIsVerified(finding);
|
|
873
889
|
score = Math.min(score, piiVerified ? 0.45 : 0.32);
|
|
874
890
|
reasons.push("pii_email_deprioritized");
|
|
875
891
|
}
|
|
876
892
|
if (isUnverifiedTrufflehogSecret(finding)) {
|
|
877
893
|
const secretBlob = `${finding.match_text} ${finding.snippet ?? ""}`;
|
|
878
894
|
if (hasEnvironmentInterpolation(secretBlob)) {
|
|
879
|
-
score = Math.min(score,
|
|
880
|
-
reasons.push("
|
|
895
|
+
score = Math.min(score, NUCLEAR_HARD_DROP_CONFIDENCE);
|
|
896
|
+
reasons.push("env_variable_interpolation");
|
|
881
897
|
}
|
|
882
|
-
if (isLockfileOrModulesPath(relPath)) {
|
|
883
|
-
score = Math.min(score,
|
|
884
|
-
reasons.push("
|
|
898
|
+
if (isLockfileOrModulesPath(relPath) || isLockfileLayoutArtifactPath(relPath)) {
|
|
899
|
+
score = Math.min(score, NUCLEAR_HARD_DROP_CONFIDENCE);
|
|
900
|
+
reasons.push("lockfile_or_layout_artifact");
|
|
885
901
|
}
|
|
886
902
|
}
|
|
903
|
+
if (customRegexNeedsBaseClamp(finding)) {
|
|
904
|
+
score = Math.min(score, CUSTOMREGEX_UNVERIFIED_CAP);
|
|
905
|
+
reasons.push("customregex_unverified_clamped");
|
|
906
|
+
}
|
|
887
907
|
}
|
|
888
908
|
const libs = phase1.protection_libs_detected ?? [];
|
|
889
909
|
if (libs.length && protectionMatchesMetric(title, libs)) {
|
|
@@ -957,7 +977,19 @@ function downgradeSeverity(severity, cap) {
|
|
|
957
977
|
function enrichAuditFinding(repoRoot, finding, opts) {
|
|
958
978
|
const applyFp = opts?.applyFalsePositiveFilter ?? !isCalibrationTestbedPath(finding.file_path);
|
|
959
979
|
const relPath = finding.file_path.replace(/\\/g, "/");
|
|
960
|
-
const
|
|
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
|
+
}
|
|
961
993
|
const phase1 = phase1ContextResearch(repoRoot, relPath);
|
|
962
994
|
let [conf, reasons] = baseConfidenceForFinding(finding, phase1, relPath, finding.category, repoRoot);
|
|
963
995
|
const [boost, boostReason] = comparativeAnalysisBoost(repoRoot, relPath, applyFp);
|
|
@@ -982,18 +1014,18 @@ function enrichAuditFinding(repoRoot, finding, opts) {
|
|
|
982
1014
|
}
|
|
983
1015
|
}
|
|
984
1016
|
}
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
}
|
|
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);
|
|
990
1021
|
} else {
|
|
991
1022
|
conf = Math.max(0.05, Math.min(1, conf));
|
|
992
1023
|
}
|
|
993
1024
|
const snippetL = snippetLower(finding);
|
|
994
1025
|
const attackConcrete = attackPathConcrete(finding, conf, eliteHard, reasons, snippetL);
|
|
995
1026
|
const critique = selfCritique(finding, conf, phase1);
|
|
996
|
-
const
|
|
1027
|
+
const hardDropped = Boolean(postNuclear);
|
|
1028
|
+
const primaryOk = !hardDropped && conf >= PRIMARY_LOG_THRESHOLD && !hardExcludeFromPrimaryLog(relPath, finding.description, conf, eliteHard);
|
|
997
1029
|
let severity = finding.severity;
|
|
998
1030
|
const originalSeverity = finding.original_severity ?? finding.severity;
|
|
999
1031
|
if (reasons.includes("phase1_protection_lib_present") && conf <= 0.25) {
|
|
@@ -1001,7 +1033,7 @@ function enrichAuditFinding(repoRoot, finding, opts) {
|
|
|
1001
1033
|
} else if (!primaryOk && conf < PRIMARY_LOG_THRESHOLD) {
|
|
1002
1034
|
severity = downgradeSeverity(severity, critique.suggested_severity_cap);
|
|
1003
1035
|
}
|
|
1004
|
-
const forceSuppressed =
|
|
1036
|
+
const forceSuppressed = hardDropped;
|
|
1005
1037
|
return {
|
|
1006
1038
|
...finding,
|
|
1007
1039
|
severity,
|
|
@@ -1011,7 +1043,7 @@ function enrichAuditFinding(repoRoot, finding, opts) {
|
|
|
1011
1043
|
primary_log_eligible: primaryOk,
|
|
1012
1044
|
...forceSuppressed ? {
|
|
1013
1045
|
suppressed: true,
|
|
1014
|
-
suppression_reason:
|
|
1046
|
+
suppression_reason: postNuclear.reason
|
|
1015
1047
|
} : {},
|
|
1016
1048
|
attack_path_concrete: attackConcrete,
|
|
1017
1049
|
cognitive: {
|
|
@@ -1755,6 +1787,9 @@ function mapTrufflehogFindings(rows, workspaceRoot) {
|
|
|
1755
1787
|
asvsTrace: "V6.4.1",
|
|
1756
1788
|
severity,
|
|
1757
1789
|
description,
|
|
1790
|
+
title: description,
|
|
1791
|
+
detector_name: detector,
|
|
1792
|
+
verified,
|
|
1758
1793
|
file_path: rel,
|
|
1759
1794
|
line,
|
|
1760
1795
|
match_text: display.slice(0, 200),
|