@runsec/mcp 1.0.67 → 1.0.69

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.
@@ -26,23 +26,15 @@ function isRunnableBinary(filePath) {
26
26
  }
27
27
  }
28
28
 
29
+ const { hasOptionalEngine, resolveOptionalEngineBinary } = require("./engine-resolve.cjs");
30
+
29
31
  function optionalEngineResolvable(engine) {
30
- const pkg = `@runsec/engine-${engine}-${process.platform}-${process.arch}`;
31
- const win = process.platform === "win32";
32
- const candidates = win
33
- ? [`${pkg}/bin/${engine}.exe`, `${pkg}/bin/${engine}`]
34
- : [`${pkg}/bin/${engine}`];
35
- for (const candidate of candidates) {
36
- console.error(`[RunSec Debug] runsec-mcp require.resolve: ${candidate}`);
37
- try {
38
- const resolved = require.resolve(candidate);
39
- console.error(`[RunSec Debug] runsec-mcp optional OK ${candidate} -> ${resolved}`);
40
- return true;
41
- } catch (err) {
42
- const message = err instanceof Error ? err.message : String(err);
43
- console.error(`[RunSec Debug] runsec-mcp require.resolve failed ${candidate}: ${message}`);
44
- }
32
+ const resolved = resolveOptionalEngineBinary(engine);
33
+ if (resolved) {
34
+ console.error(`[RunSec Debug] runsec-mcp optional OK ${engine} -> ${resolved}`);
35
+ return true;
45
36
  }
37
+ console.error(`[RunSec Debug] runsec-mcp optional missing for ${engine}`);
46
38
  return false;
47
39
  }
48
40
 
@@ -70,7 +62,11 @@ function shouldFetchBundledBinaries() {
70
62
  return false;
71
63
  }
72
64
  const missing = binariesMissing();
73
- console.error(`[RunSec Debug] binariesMissing()=${missing} PKG_ROOT=${PKG_ROOT}`);
65
+ console.error(
66
+ `[RunSec Debug] ensure-binaries needed: trufflehogOptional=${thOptional} syftOptional=${syOptional} binariesMissing=${missing}`
67
+ );
68
+ // If any optional engine package is missing, always try GitHub download into bin/
69
+ if (!thOptional || !syOptional) return true;
74
70
  return missing;
75
71
  }
76
72
 
package/dist/index.js CHANGED
@@ -400,7 +400,7 @@ async function ignoreFinding(args) {
400
400
  }
401
401
 
402
402
  // src/engine/ruleEngine.ts
403
- var import_node_fs7 = require("fs");
403
+ var import_node_fs8 = require("fs");
404
404
  var import_node_path13 = __toESM(require("path"));
405
405
  var import_ignore = __toESM(require("ignore"));
406
406
 
@@ -2004,12 +2004,27 @@ var import_node_util = require("util");
2004
2004
  var import_node_path10 = __toESM(require("path"));
2005
2005
 
2006
2006
  // src/engine/toolPaths.ts
2007
- var import_node_fs6 = __toESM(require("fs"));
2007
+ var import_node_fs7 = __toESM(require("fs"));
2008
2008
  var import_node_path9 = __toESM(require("path"));
2009
2009
 
2010
2010
  // src/engine/binaryResolver.ts
2011
+ var import_node_fs6 = __toESM(require("fs"));
2011
2012
  var import_node_module = require("module");
2012
2013
  var import_node_path8 = __toESM(require("path"));
2014
+ var MIN_BINARY_BYTES = 1024;
2015
+ function getPackageRoot() {
2016
+ const dir = __dirname;
2017
+ if (import_node_path8.default.basename(dir) === "dist") {
2018
+ return import_node_path8.default.resolve(dir, "..");
2019
+ }
2020
+ if (import_node_path8.default.basename(dir) === "engine" && import_node_path8.default.basename(import_node_path8.default.dirname(dir)) === "dist") {
2021
+ return import_node_path8.default.resolve(dir, "../..");
2022
+ }
2023
+ return import_node_path8.default.resolve(dir, "..", "..");
2024
+ }
2025
+ function packageRequire() {
2026
+ return (0, import_node_module.createRequire)(import_node_path8.default.join(getPackageRoot(), "package.json"));
2027
+ }
2013
2028
  var SUPPORTED_PLATFORMS = /* @__PURE__ */ new Set(["darwin", "linux", "win32"]);
2014
2029
  var SUPPORTED_ARCHES = /* @__PURE__ */ new Set(["x64", "arm64"]);
2015
2030
  function buildOptionalEnginePackageName(engineName, platform = process.platform, arch = process.arch) {
@@ -2039,15 +2054,18 @@ function getEngineExecutable(engineName) {
2039
2054
  );
2040
2055
  return engineName;
2041
2056
  }
2042
- console.error(`[RunSec Debug] Optional package name: ${packageName}`);
2043
- const require2 = (0, import_node_module.createRequire)(__filename);
2057
+ console.error(`[RunSec Debug] Optional package name: ${packageName} packageRoot=${getPackageRoot()}`);
2058
+ const req = packageRequire();
2044
2059
  const candidates = binResolveCandidates(engineName, packageName);
2045
2060
  for (const candidate of candidates) {
2046
2061
  console.error(`[RunSec Debug] require.resolve target: ${candidate}`);
2047
2062
  try {
2048
- const resolved = require2.resolve(candidate);
2049
- console.error(`[RunSec Debug] Found optional package binary at: ${resolved}`);
2050
- return resolved;
2063
+ const resolved = req.resolve(candidate);
2064
+ if (import_node_fs6.default.existsSync(resolved) && import_node_fs6.default.statSync(resolved).size >= MIN_BINARY_BYTES) {
2065
+ console.error(`[RunSec Debug] Found optional package binary at: ${resolved}`);
2066
+ return resolved;
2067
+ }
2068
+ console.error(`[RunSec Debug] Optional binary too small or missing: ${resolved}`);
2051
2069
  } catch (err) {
2052
2070
  const message = err instanceof Error ? err.message : String(err);
2053
2071
  console.error(`[RunSec Debug] require.resolve failed for ${candidate}: ${message}`);
@@ -2063,40 +2081,40 @@ function isOptionalPackageExecutable(engineName, command) {
2063
2081
  }
2064
2082
 
2065
2083
  // src/engine/toolPaths.ts
2066
- var MIN_BINARY_BYTES = 1024;
2084
+ var MIN_BINARY_BYTES2 = 1024;
2067
2085
  function binFileName(tool) {
2068
2086
  return process.platform === "win32" ? `${tool}.exe` : tool;
2069
2087
  }
2070
2088
  function isBundledBinaryRunnable(filePath) {
2071
2089
  try {
2072
- const stat = import_node_fs6.default.statSync(filePath);
2073
- if (!stat.isFile() || stat.size < MIN_BINARY_BYTES) return false;
2074
- const fd = import_node_fs6.default.openSync(filePath, "r");
2090
+ const stat = import_node_fs7.default.statSync(filePath);
2091
+ if (!stat.isFile() || stat.size < MIN_BINARY_BYTES2) return false;
2092
+ const fd = import_node_fs7.default.openSync(filePath, "r");
2075
2093
  try {
2076
2094
  const magic = Buffer.alloc(2);
2077
- import_node_fs6.default.readSync(fd, magic, 0, 2, 0);
2095
+ import_node_fs7.default.readSync(fd, magic, 0, 2, 0);
2078
2096
  if (process.platform === "win32") {
2079
2097
  return magic[0] === 77 && magic[1] === 90;
2080
2098
  }
2081
2099
  return magic[0] === 127 && magic[1] === 69;
2082
2100
  } finally {
2083
- import_node_fs6.default.closeSync(fd);
2101
+ import_node_fs7.default.closeSync(fd);
2084
2102
  }
2085
2103
  } catch {
2086
2104
  return false;
2087
2105
  }
2088
2106
  }
2089
- function getPackageRoot() {
2107
+ function getPackageRoot2() {
2090
2108
  if (import_node_path9.default.basename(__dirname) === "dist") {
2091
2109
  return import_node_path9.default.resolve(__dirname, "..");
2092
2110
  }
2093
2111
  return import_node_path9.default.resolve(__dirname, "..", "..");
2094
2112
  }
2095
2113
  function getBundledBinDir() {
2096
- return import_node_path9.default.join(getPackageRoot(), "bin");
2114
+ return import_node_path9.default.join(getPackageRoot2(), "bin");
2097
2115
  }
2098
2116
  function resolveEngineBinary(tool) {
2099
- const packageRoot = getPackageRoot();
2117
+ const packageRoot = getPackageRoot2();
2100
2118
  const bundledBinDir = getBundledBinDir();
2101
2119
  console.error(
2102
2120
  `[RunSec Debug] resolveEngineBinary(${tool}) packageRoot=${packageRoot} bundledBinDir=${bundledBinDir} __dirname=${__dirname}`
@@ -2109,10 +2127,10 @@ function resolveEngineBinary(tool) {
2109
2127
  }
2110
2128
  const bundled = import_node_path9.default.join(bundledBinDir, binFileName(tool));
2111
2129
  console.error(`[RunSec Debug] Checking bundled path: ${bundled}`);
2112
- const bundledExists = import_node_fs6.default.existsSync(bundled);
2130
+ const bundledExists = import_node_fs7.default.existsSync(bundled);
2113
2131
  const bundledRunnable = bundledExists && isBundledBinaryRunnable(bundled);
2114
2132
  console.error(
2115
- `[RunSec Debug] Bundled path exists=${bundledExists} runnable=${bundledRunnable}` + (bundledExists ? ` size=${import_node_fs6.default.statSync(bundled).size}` : "")
2133
+ `[RunSec Debug] Bundled path exists=${bundledExists} runnable=${bundledRunnable}` + (bundledExists ? ` size=${import_node_fs7.default.statSync(bundled).size}` : "")
2116
2134
  );
2117
2135
  if (bundledRunnable) {
2118
2136
  console.error(`[RunSec Debug] Using bundled binary for ${tool}: ${bundled}`);
@@ -2924,7 +2942,7 @@ async function buildIgnoreMatcher(workspaceRoot) {
2924
2942
  ]);
2925
2943
  const runsecIgnorePath = import_node_path13.default.join(workspaceRoot, RUNSEC_IGNORE_FILE);
2926
2944
  try {
2927
- const content = await import_node_fs7.promises.readFile(runsecIgnorePath, "utf-8");
2945
+ const content = await import_node_fs8.promises.readFile(runsecIgnorePath, "utf-8");
2928
2946
  matcher.add(content);
2929
2947
  } catch {
2930
2948
  }
@@ -2936,7 +2954,7 @@ async function collectFilesWithStats(workspacePath, targetFiles) {
2936
2954
  let skippedByIgnore = 0;
2937
2955
  let stat;
2938
2956
  try {
2939
- stat = await import_node_fs7.promises.stat(root);
2957
+ stat = await import_node_fs8.promises.stat(root);
2940
2958
  } catch {
2941
2959
  stat = null;
2942
2960
  }
@@ -2953,7 +2971,7 @@ async function collectFilesWithStats(workspacePath, targetFiles) {
2953
2971
  continue;
2954
2972
  }
2955
2973
  try {
2956
- const s = await import_node_fs7.promises.stat(candidate);
2974
+ const s = await import_node_fs8.promises.stat(candidate);
2957
2975
  if (s.isFile()) out.push(candidate);
2958
2976
  } catch {
2959
2977
  }
@@ -3000,7 +3018,7 @@ async function executeAudit(toolName, args) {
3000
3018
  }
3001
3019
 
3002
3020
  // src/engine/reportFormatter.ts
3003
- var import_node_fs8 = __toESM(require("fs"));
3021
+ var import_node_fs9 = __toESM(require("fs"));
3004
3022
  var import_node_path14 = __toESM(require("path"));
3005
3023
  var REPORT_SECTION_TITLES = {
3006
3024
  code: "Code Vulnerabilities",
@@ -3468,7 +3486,7 @@ function generateMarkdownReport(standard, findings, metrics, workspacePath) {
3468
3486
  const rows = Array.isArray(findings) ? findings : [];
3469
3487
  const reportContent = finalizeReportMarkdown(buildServerSideReportMarkdown(standard, rows, m));
3470
3488
  const reportPath = resolveReportPath(workspacePath);
3471
- import_node_fs8.default.writeFileSync(reportPath, reportContent, "utf-8");
3489
+ import_node_fs9.default.writeFileSync(reportPath, reportContent, "utf-8");
3472
3490
  console.error(`[runsec] wrote unified report to: ${reportPath}`);
3473
3491
  console.error(`[runsec] X-RunSec-Verdict: ${m.verdict?.http_headers?.["X-RunSec-Verdict"] ?? m.verdict?.status ?? "PASS"}`);
3474
3492
  return `
@@ -3483,16 +3501,16 @@ Simply confirm that the scan is complete and the report is saved at the path abo
3483
3501
  }
3484
3502
 
3485
3503
  // src/engine/reviewFormatter.ts
3486
- var import_node_fs13 = __toESM(require("fs"));
3504
+ var import_node_fs14 = __toESM(require("fs"));
3487
3505
  var import_node_path20 = __toESM(require("path"));
3488
3506
 
3489
3507
  // src/engine/threatModelEngine.ts
3490
3508
  var import_node_crypto4 = require("crypto");
3491
- var import_node_fs12 = __toESM(require("fs"));
3509
+ var import_node_fs13 = __toESM(require("fs"));
3492
3510
  var import_node_path19 = __toESM(require("path"));
3493
3511
 
3494
3512
  // src/skills/skillsApi.ts
3495
- var import_node_fs11 = __toESM(require("fs"));
3513
+ var import_node_fs12 = __toESM(require("fs"));
3496
3514
  var import_node_path18 = __toESM(require("path"));
3497
3515
 
3498
3516
  // src/skills/patternParser.ts
@@ -3579,23 +3597,23 @@ function getRagCachePath() {
3579
3597
 
3580
3598
  // src/skills/ragIndex.ts
3581
3599
  var import_node_crypto3 = require("crypto");
3582
- var import_node_fs10 = __toESM(require("fs"));
3600
+ var import_node_fs11 = __toESM(require("fs"));
3583
3601
  var import_node_path17 = __toESM(require("path"));
3584
3602
 
3585
3603
  // src/skills/skillLoader.ts
3586
- var import_node_fs9 = __toESM(require("fs"));
3604
+ var import_node_fs10 = __toESM(require("fs"));
3587
3605
  var import_node_path16 = __toESM(require("path"));
3588
3606
  function loadSkillManifests() {
3589
3607
  const skillsDir = getSkillsDirectory();
3590
3608
  const manifests = {};
3591
- if (!import_node_fs9.default.existsSync(skillsDir)) {
3609
+ if (!import_node_fs10.default.existsSync(skillsDir)) {
3592
3610
  return manifests;
3593
3611
  }
3594
- for (const entry of import_node_fs9.default.readdirSync(skillsDir, { withFileTypes: true })) {
3612
+ for (const entry of import_node_fs10.default.readdirSync(skillsDir, { withFileTypes: true })) {
3595
3613
  if (!entry.isDirectory()) continue;
3596
3614
  const skillJson = import_node_path16.default.join(skillsDir, entry.name, "skill.json");
3597
- if (!import_node_fs9.default.existsSync(skillJson)) continue;
3598
- const data = JSON.parse(import_node_fs9.default.readFileSync(skillJson, "utf-8"));
3615
+ if (!import_node_fs10.default.existsSync(skillJson)) continue;
3616
+ const data = JSON.parse(import_node_fs10.default.readFileSync(skillJson, "utf-8"));
3599
3617
  const sid = String(data.skill_id ?? entry.name);
3600
3618
  manifests[sid] = { ...data, skill_id: sid, __dir_name: entry.name };
3601
3619
  }
@@ -3648,27 +3666,27 @@ function cosine(a, b) {
3648
3666
  }
3649
3667
  function hashFile(filePath) {
3650
3668
  const h = (0, import_node_crypto3.createHash)("sha256");
3651
- h.update(import_node_fs10.default.readFileSync(filePath));
3669
+ h.update(import_node_fs11.default.readFileSync(filePath));
3652
3670
  return h.digest("hex");
3653
3671
  }
3654
3672
  function listSkillSourceFiles() {
3655
3673
  const skillsDir = getSkillsDirectory();
3656
3674
  const files = [];
3657
3675
  const walk = (dir) => {
3658
- for (const entry of import_node_fs10.default.readdirSync(dir, { withFileTypes: true })) {
3676
+ for (const entry of import_node_fs11.default.readdirSync(dir, { withFileTypes: true })) {
3659
3677
  const full = import_node_path17.default.join(dir, entry.name);
3660
3678
  if (entry.isDirectory()) walk(full);
3661
3679
  else if (entry.isFile()) files.push(full);
3662
3680
  }
3663
3681
  };
3664
- if (import_node_fs10.default.existsSync(skillsDir)) walk(skillsDir);
3682
+ if (import_node_fs11.default.existsSync(skillsDir)) walk(skillsDir);
3665
3683
  return files.sort();
3666
3684
  }
3667
3685
  function computeFilesChecksum() {
3668
3686
  const files = listSkillSourceFiles();
3669
3687
  const entries = [];
3670
3688
  for (const full of files) {
3671
- const st = import_node_fs10.default.statSync(full);
3689
+ const st = import_node_fs11.default.statSync(full);
3672
3690
  const rel = import_node_path17.default.relative(getSkillsDirectory(), full).replace(/\\/g, "/");
3673
3691
  const mtimeNs = typeof st.mtimeNs === "bigint" ? Number(st.mtimeNs) : Math.round(st.mtimeMs * 1e6);
3674
3692
  entries.push({
@@ -3702,9 +3720,9 @@ function buildRagIndex() {
3702
3720
  const dir = skillDirectory(manifest);
3703
3721
  const indexPath = import_node_path17.default.join(dir, "index.md");
3704
3722
  const patternsPath = import_node_path17.default.join(dir, "patterns.md");
3705
- if (!import_node_fs10.default.existsSync(indexPath) || !import_node_fs10.default.existsSync(patternsPath)) continue;
3706
- const indexText = import_node_fs10.default.readFileSync(indexPath, "utf-8");
3707
- const patternsText = import_node_fs10.default.readFileSync(patternsPath, "utf-8");
3723
+ if (!import_node_fs11.default.existsSync(indexPath) || !import_node_fs11.default.existsSync(patternsPath)) continue;
3724
+ const indexText = import_node_fs11.default.readFileSync(indexPath, "utf-8");
3725
+ const patternsText = import_node_fs11.default.readFileSync(patternsPath, "utf-8");
3708
3726
  const example_path = String(manifest.few_shot_examples ?? "");
3709
3727
  for (const paragraph of indexText.split(/\n\n+/).map((p) => p.trim()).filter(Boolean)) {
3710
3728
  chunks.push({
@@ -3745,9 +3763,9 @@ function buildRagIndex() {
3745
3763
  }
3746
3764
  function loadRagCache(current) {
3747
3765
  const cachePath = getRagCachePath();
3748
- if (!import_node_fs10.default.existsSync(cachePath)) return null;
3766
+ if (!import_node_fs11.default.existsSync(cachePath)) return null;
3749
3767
  try {
3750
- const payload = JSON.parse(import_node_fs10.default.readFileSync(cachePath, "utf-8"));
3768
+ const payload = JSON.parse(import_node_fs11.default.readFileSync(cachePath, "utf-8"));
3751
3769
  if (payload.schema_version !== RAG_CACHE_SCHEMA_VERSION) return null;
3752
3770
  if (JSON.stringify(payload.files_checksum) !== JSON.stringify(current)) return null;
3753
3771
  return deserializeChunks(payload.chunks ?? []);
@@ -3762,7 +3780,7 @@ function saveRagCache(chunks, filesChecksum) {
3762
3780
  files_checksum: filesChecksum,
3763
3781
  chunks: serializeChunks(chunks)
3764
3782
  };
3765
- import_node_fs10.default.writeFileSync(getRagCachePath(), JSON.stringify(payload), "utf-8");
3783
+ import_node_fs11.default.writeFileSync(getRagCachePath(), JSON.stringify(payload), "utf-8");
3766
3784
  } catch {
3767
3785
  console.error("[runsec] RAG cache write failed; continuing in-memory only");
3768
3786
  }
@@ -3964,9 +3982,9 @@ function findPatternChunkByMetric(metricId) {
3964
3982
  // src/skills/skillsApi.ts
3965
3983
  function loadComplianceSnapshot() {
3966
3984
  const p = import_node_path18.default.join(getDataDirectory(), "rule-compliance-map.json");
3967
- if (!import_node_fs11.default.existsSync(p)) return {};
3985
+ if (!import_node_fs12.default.existsSync(p)) return {};
3968
3986
  try {
3969
- return JSON.parse(import_node_fs11.default.readFileSync(p, "utf-8"));
3987
+ return JSON.parse(import_node_fs12.default.readFileSync(p, "utf-8"));
3970
3988
  } catch {
3971
3989
  return {};
3972
3990
  }
@@ -3974,9 +3992,9 @@ function loadComplianceSnapshot() {
3974
3992
  function extractTestbedExample(metricId, examplePath, skillsRoot) {
3975
3993
  if (!examplePath) return "";
3976
3994
  const p = import_node_path18.default.isAbsolute(examplePath) ? examplePath : import_node_path18.default.join(import_node_path18.default.dirname(skillsRoot), "..", "..", examplePath);
3977
- const resolved = import_node_fs11.default.existsSync(p) ? p : import_node_path18.default.join(getDataDirectory(), "..", "..", examplePath);
3978
- if (!import_node_fs11.default.existsSync(resolved)) return "";
3979
- const lines = import_node_fs11.default.readFileSync(resolved, "utf-8").split(/\r?\n/);
3995
+ const resolved = import_node_fs12.default.existsSync(p) ? p : import_node_path18.default.join(getDataDirectory(), "..", "..", examplePath);
3996
+ if (!import_node_fs12.default.existsSync(resolved)) return "";
3997
+ const lines = import_node_fs12.default.readFileSync(resolved, "utf-8").split(/\r?\n/);
3980
3998
  const needle = `Vulnerable: ${metricId}`;
3981
3999
  for (let idx = 0; idx < lines.length; idx += 1) {
3982
4000
  if (lines[idx].includes(needle)) {
@@ -4030,14 +4048,14 @@ function getSkillContext(args) {
4030
4048
  const dir = skillDirectory(manifest);
4031
4049
  const indexPath = import_node_path18.default.join(dir, "index.md");
4032
4050
  const patternsPath = import_node_path18.default.join(dir, "patterns.md");
4033
- if (!import_node_fs11.default.existsSync(indexPath) || !import_node_fs11.default.existsSync(patternsPath)) {
4051
+ if (!import_node_fs12.default.existsSync(indexPath) || !import_node_fs12.default.existsSync(patternsPath)) {
4034
4052
  return {
4035
4053
  error: `incomplete skill data for ${skill_id}`,
4036
- index_exists: import_node_fs11.default.existsSync(indexPath),
4037
- patterns_exists: import_node_fs11.default.existsSync(patternsPath)
4054
+ index_exists: import_node_fs12.default.existsSync(indexPath),
4055
+ patterns_exists: import_node_fs12.default.existsSync(patternsPath)
4038
4056
  };
4039
4057
  }
4040
- const parsedRows = parsePatternRows(import_node_fs11.default.readFileSync(patternsPath, "utf-8"));
4058
+ const parsedRows = parsePatternRows(import_node_fs12.default.readFileSync(patternsPath, "utf-8"));
4041
4059
  const grouped = {};
4042
4060
  for (const row of parsedRows) {
4043
4061
  const stack = row.stack.trim() || "Generic";
@@ -4055,8 +4073,8 @@ function getSkillContext(args) {
4055
4073
  skill_id,
4056
4074
  index_path: import_node_path18.default.relative(getDataDirectory(), indexPath).replace(/\\/g, "/"),
4057
4075
  patterns_path: import_node_path18.default.relative(getDataDirectory(), patternsPath).replace(/\\/g, "/"),
4058
- index_md: import_node_fs11.default.readFileSync(indexPath, "utf-8"),
4059
- patterns_md: import_node_fs11.default.readFileSync(patternsPath, "utf-8"),
4076
+ index_md: import_node_fs12.default.readFileSync(indexPath, "utf-8"),
4077
+ patterns_md: import_node_fs12.default.readFileSync(patternsPath, "utf-8"),
4060
4078
  patterns_by_stack: Object.fromEntries(Object.keys(grouped).sort().map((k) => [k, grouped[k]])),
4061
4079
  agent_system_insert: ANTI_HALLUCINATION_PROMPT,
4062
4080
  compliance_snapshot: loadComplianceSnapshot()
@@ -4159,7 +4177,7 @@ var STRIDE_LABELS = {
4159
4177
  };
4160
4178
  function readTextSafe(filePath, limit = 4e5) {
4161
4179
  try {
4162
- const data = import_node_fs12.default.readFileSync(filePath, "utf-8");
4180
+ const data = import_node_fs13.default.readFileSync(filePath, "utf-8");
4163
4181
  return data.length > limit ? data.slice(0, limit) : data;
4164
4182
  } catch {
4165
4183
  return "";
@@ -4199,7 +4217,7 @@ function walkFiles2(root, opts) {
4199
4217
  let dir;
4200
4218
  try {
4201
4219
  dir = stack.pop();
4202
- const entries = import_node_fs12.default.readdirSync(dir, { withFileTypes: true });
4220
+ const entries = import_node_fs13.default.readdirSync(dir, { withFileTypes: true });
4203
4221
  for (const ent of entries) {
4204
4222
  if (out.length >= max) break;
4205
4223
  const full = import_node_path19.default.join(dir, ent.name);
@@ -4268,7 +4286,7 @@ function collectRepoThreatSignals(scanRoot, syftPayload) {
4268
4286
  }
4269
4287
  };
4270
4288
  try {
4271
- for (const ent of import_node_fs12.default.readdirSync(root, { withFileTypes: true })) {
4289
+ for (const ent of import_node_fs13.default.readdirSync(root, { withFileTypes: true })) {
4272
4290
  if (ent.isDirectory() && !ent.name.startsWith(".")) {
4273
4291
  signals.top_level_dirs.push(ent.name);
4274
4292
  if (signals.top_level_dirs.length >= 40) break;
@@ -4287,7 +4305,7 @@ function collectRepoThreatSignals(scanRoot, syftPayload) {
4287
4305
  const dir = stack.pop();
4288
4306
  let entries;
4289
4307
  try {
4290
- entries = import_node_fs12.default.readdirSync(dir, { withFileTypes: true });
4308
+ entries = import_node_fs13.default.readdirSync(dir, { withFileTypes: true });
4291
4309
  } catch {
4292
4310
  continue;
4293
4311
  }
@@ -4317,7 +4335,7 @@ function collectRepoThreatSignals(scanRoot, syftPayload) {
4317
4335
  });
4318
4336
  for (const name of ["Dockerfile", "docker-compose.yml", "docker-compose.yaml"]) {
4319
4337
  const fp = import_node_path19.default.join(root, name);
4320
- if (import_node_fs12.default.existsSync(fp)) {
4338
+ if (import_node_fs13.default.existsSync(fp)) {
4321
4339
  addKey(name);
4322
4340
  if (name === "Dockerfile") signals.flags.has_dockerfile = true;
4323
4341
  else signals.flags.has_compose = true;
@@ -4331,7 +4349,7 @@ function collectRepoThreatSignals(scanRoot, syftPayload) {
4331
4349
  break;
4332
4350
  }
4333
4351
  }
4334
- if (import_node_fs12.default.existsSync(import_node_path19.default.join(root, ".github", "workflows"))) {
4352
+ if (import_node_fs13.default.existsSync(import_node_path19.default.join(root, ".github", "workflows"))) {
4335
4353
  signals.flags.has_github_workflows = true;
4336
4354
  }
4337
4355
  for (const pkg of signals.syft_packages) {
@@ -4364,11 +4382,11 @@ ${repoFp}`).digest("hex");
4364
4382
  }
4365
4383
  function loadThreatModelCache(workspaceRoot) {
4366
4384
  const p = import_node_path19.default.join(workspaceRoot, THREAT_MODEL_CACHE_FILE);
4367
- if (!import_node_fs12.default.existsSync(p)) {
4385
+ if (!import_node_fs13.default.existsSync(p)) {
4368
4386
  return { schema_version: THREAT_MODEL_CACHE_SCHEMA_VERSION, entries: {} };
4369
4387
  }
4370
4388
  try {
4371
- const data = JSON.parse(import_node_fs12.default.readFileSync(p, "utf-8"));
4389
+ const data = JSON.parse(import_node_fs13.default.readFileSync(p, "utf-8"));
4372
4390
  if (data.schema_version !== THREAT_MODEL_CACHE_SCHEMA_VERSION) {
4373
4391
  return { schema_version: THREAT_MODEL_CACHE_SCHEMA_VERSION, entries: {} };
4374
4392
  }
@@ -4388,7 +4406,7 @@ function saveThreatModelCache(workspaceRoot, cacheKey, markdown, baseline) {
4388
4406
  repo_fingerprint: String(baseline.repo_fingerprint ?? "")
4389
4407
  };
4390
4408
  payload.baseline = baseline;
4391
- import_node_fs12.default.writeFileSync(p, JSON.stringify(payload, null, 2), "utf-8");
4409
+ import_node_fs13.default.writeFileSync(p, JSON.stringify(payload, null, 2), "utf-8");
4392
4410
  return p;
4393
4411
  }
4394
4412
  function strideClassifyClause(clause) {
@@ -4819,7 +4837,7 @@ Project: ${projectName}`;
4819
4837
  const cached = cache.entries[cacheKey];
4820
4838
  if (cached?.markdown) {
4821
4839
  const reportPath2 = import_node_path19.default.join(workspaceRoot, THREAT_MODEL_REPORT_FILE);
4822
- import_node_fs12.default.writeFileSync(reportPath2, cached.markdown, "utf-8");
4840
+ import_node_fs13.default.writeFileSync(reportPath2, cached.markdown, "utf-8");
4823
4841
  return {
4824
4842
  profile,
4825
4843
  markdown: cached.markdown,
@@ -4857,7 +4875,7 @@ Project: ${projectName}`;
4857
4875
  };
4858
4876
  const cachePath = saveThreatModelCache(workspaceRoot, cacheKey, markdown, baselinePayload);
4859
4877
  const reportPath = import_node_path19.default.join(workspaceRoot, THREAT_MODEL_REPORT_FILE);
4860
- import_node_fs12.default.writeFileSync(reportPath, markdown, "utf-8");
4878
+ import_node_fs13.default.writeFileSync(reportPath, markdown, "utf-8");
4861
4879
  return {
4862
4880
  profile,
4863
4881
  markdown,
@@ -5077,7 +5095,7 @@ function resolveSecurityReviewPath(workspacePath) {
5077
5095
  }
5078
5096
  function writeSecurityReviewReport(markdown, workspacePath) {
5079
5097
  const reportPath = resolveSecurityReviewPath(workspacePath);
5080
- import_node_fs13.default.writeFileSync(reportPath, markdown, "utf-8");
5098
+ import_node_fs14.default.writeFileSync(reportPath, markdown, "utf-8");
5081
5099
  console.error(`[runsec] wrote security review to: ${reportPath}`);
5082
5100
  return reportPath;
5083
5101
  }
@@ -5116,11 +5134,11 @@ async function executeSecurityReview(opts) {
5116
5134
  }
5117
5135
 
5118
5136
  // src/engine/remediation.ts
5119
- var import_node_fs15 = __toESM(require("fs"));
5137
+ var import_node_fs16 = __toESM(require("fs"));
5120
5138
  var import_node_path22 = __toESM(require("path"));
5121
5139
 
5122
5140
  // src/skills/metricLookup.ts
5123
- var import_node_fs14 = __toESM(require("fs"));
5141
+ var import_node_fs15 = __toESM(require("fs"));
5124
5142
  var import_node_path21 = __toESM(require("path"));
5125
5143
  function findMetricRow(metricId) {
5126
5144
  const mid = metricId.trim().toUpperCase();
@@ -5128,8 +5146,8 @@ function findMetricRow(metricId) {
5128
5146
  const manifests = loadSkillManifests();
5129
5147
  for (const [, manifest] of Object.entries(manifests)) {
5130
5148
  const patternsPath = import_node_path21.default.join(skillDirectory(manifest), "patterns.md");
5131
- if (!import_node_fs14.default.existsSync(patternsPath)) continue;
5132
- const rows = parsePatternRows(import_node_fs14.default.readFileSync(patternsPath, "utf-8"));
5149
+ if (!import_node_fs15.default.existsSync(patternsPath)) continue;
5150
+ const rows = parsePatternRows(import_node_fs15.default.readFileSync(patternsPath, "utf-8"));
5133
5151
  const row = rows.find((r) => r.metric_id.toUpperCase() === mid);
5134
5152
  if (row) return row;
5135
5153
  }
@@ -5155,8 +5173,8 @@ function resolveTargetFile(workspaceRoot, filePath) {
5155
5173
  return { error: `refusing to modify path under blocked segment: ${seg}` };
5156
5174
  }
5157
5175
  }
5158
- if (!import_node_fs15.default.existsSync(abs)) return { error: `path does not exist: ${relFromRoot}` };
5159
- if (!import_node_fs15.default.statSync(abs).isFile()) return { error: `path is not a file: ${relFromRoot}` };
5176
+ if (!import_node_fs16.default.existsSync(abs)) return { error: `path does not exist: ${relFromRoot}` };
5177
+ if (!import_node_fs16.default.statSync(abs).isFile()) return { error: `path is not a file: ${relFromRoot}` };
5160
5178
  return { abs, rel: relFromRoot };
5161
5179
  }
5162
5180
  function normalizeNewlines(text) {
@@ -5223,12 +5241,12 @@ function locateTargetInFile(content, target) {
5223
5241
  }
5224
5242
  function writeBackup(absPath) {
5225
5243
  const backupPath = `${absPath}.bak`;
5226
- import_node_fs15.default.copyFileSync(absPath, backupPath);
5244
+ import_node_fs16.default.copyFileSync(absPath, backupPath);
5227
5245
  return backupPath;
5228
5246
  }
5229
5247
  function applyDvs001Fix(absPath) {
5230
5248
  const rel = import_node_path22.default.basename(absPath);
5231
- const lines = import_node_fs15.default.readFileSync(absPath, "utf-8").split(/\r?\n/);
5249
+ const lines = import_node_fs16.default.readFileSync(absPath, "utf-8").split(/\r?\n/);
5232
5250
  const newLines = lines.filter((ln) => !/^\s*user\s+root\s*$/i.test(ln.trim()));
5233
5251
  const hasNonRootUser = newLines.some(
5234
5252
  (ln) => /^\s*user\s+\S+\s*$/i.test(ln.trim()) && !/^\s*user\s+root\s*$/i.test(ln.trim())
@@ -5254,7 +5272,7 @@ function applyDvs001Fix(absPath) {
5254
5272
  };
5255
5273
  }
5256
5274
  const backupPath = writeBackup(absPath);
5257
- import_node_fs15.default.writeFileSync(absPath, `${newLines.join("\n")}
5275
+ import_node_fs16.default.writeFileSync(absPath, `${newLines.join("\n")}
5258
5276
  `, "utf-8");
5259
5277
  return {
5260
5278
  status: "fixed",
@@ -5331,7 +5349,7 @@ function applyRemediation(args) {
5331
5349
  fix_template: row.fix_template
5332
5350
  };
5333
5351
  }
5334
- const original = import_node_fs15.default.readFileSync(resolved.abs, "utf-8");
5352
+ const original = import_node_fs16.default.readFileSync(resolved.abs, "utf-8");
5335
5353
  const { index, count } = locateTargetInFile(original, target);
5336
5354
  if (count === 0) {
5337
5355
  return {
@@ -5358,7 +5376,7 @@ function applyRemediation(args) {
5358
5376
  const backupPath = writeBackup(resolved.abs);
5359
5377
  const ending = original.includes("\r\n") ? "\r\n" : "\n";
5360
5378
  const outText = updated.split("\n").join(ending);
5361
- import_node_fs15.default.writeFileSync(resolved.abs, outText.endsWith(ending) || !original.endsWith(ending) ? outText : outText + ending, "utf-8");
5379
+ import_node_fs16.default.writeFileSync(resolved.abs, outText.endsWith(ending) || !original.endsWith(ending) ? outText : outText + ending, "utf-8");
5362
5380
  return {
5363
5381
  status: "fixed",
5364
5382
  message: "Remediation applied after exact target verification; backup created",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runsec/mcp",
3
- "version": "1.0.67",
3
+ "version": "1.0.69",
4
4
  "main": "dist/index.js",
5
5
  "files": [
6
6
  "dist",