@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.
- package/bin/runsec-mcp.cjs +12 -16
- package/dist/index.js +94 -76
- package/package.json +1 -1
package/bin/runsec-mcp.cjs
CHANGED
|
@@ -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
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
2049
|
-
|
|
2050
|
-
|
|
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
|
|
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 =
|
|
2073
|
-
if (!stat.isFile() || stat.size <
|
|
2074
|
-
const fd =
|
|
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
|
-
|
|
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
|
-
|
|
2101
|
+
import_node_fs7.default.closeSync(fd);
|
|
2084
2102
|
}
|
|
2085
2103
|
} catch {
|
|
2086
2104
|
return false;
|
|
2087
2105
|
}
|
|
2088
2106
|
}
|
|
2089
|
-
function
|
|
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(
|
|
2114
|
+
return import_node_path9.default.join(getPackageRoot2(), "bin");
|
|
2097
2115
|
}
|
|
2098
2116
|
function resolveEngineBinary(tool) {
|
|
2099
|
-
const packageRoot =
|
|
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 =
|
|
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=${
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 (!
|
|
3609
|
+
if (!import_node_fs10.default.existsSync(skillsDir)) {
|
|
3592
3610
|
return manifests;
|
|
3593
3611
|
}
|
|
3594
|
-
for (const entry of
|
|
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 (!
|
|
3598
|
-
const data = JSON.parse(
|
|
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(
|
|
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
|
|
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 (
|
|
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 =
|
|
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 (!
|
|
3706
|
-
const indexText =
|
|
3707
|
-
const patternsText =
|
|
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 (!
|
|
3766
|
+
if (!import_node_fs11.default.existsSync(cachePath)) return null;
|
|
3749
3767
|
try {
|
|
3750
|
-
const payload = JSON.parse(
|
|
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
|
-
|
|
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 (!
|
|
3985
|
+
if (!import_node_fs12.default.existsSync(p)) return {};
|
|
3968
3986
|
try {
|
|
3969
|
-
return JSON.parse(
|
|
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 =
|
|
3978
|
-
if (!
|
|
3979
|
-
const lines =
|
|
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 (!
|
|
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:
|
|
4037
|
-
patterns_exists:
|
|
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(
|
|
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:
|
|
4059
|
-
patterns_md:
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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 (
|
|
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 (
|
|
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 (!
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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 (!
|
|
5132
|
-
const rows = parsePatternRows(
|
|
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 (!
|
|
5159
|
-
if (!
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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",
|