openkrak-mcp 1.0.18 → 1.1.0
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 +229 -31
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -241629,13 +241629,63 @@ function createLogger(component) {
|
|
|
241629
241629
|
process.stderr
|
|
241630
241630
|
);
|
|
241631
241631
|
}
|
|
241632
|
-
var SUPPORTED_EXTENSIONS = [
|
|
241632
|
+
var SUPPORTED_EXTENSIONS = [
|
|
241633
|
+
// JavaScript / TypeScript
|
|
241634
|
+
".ts",
|
|
241635
|
+
".tsx",
|
|
241636
|
+
".js",
|
|
241637
|
+
".jsx",
|
|
241638
|
+
// Python
|
|
241639
|
+
".py",
|
|
241640
|
+
// Go
|
|
241641
|
+
".go",
|
|
241642
|
+
// Rust
|
|
241643
|
+
".rs",
|
|
241644
|
+
// Java
|
|
241645
|
+
".java",
|
|
241646
|
+
// C#
|
|
241647
|
+
".cs"
|
|
241648
|
+
];
|
|
241649
|
+
var LANGUAGE_BY_EXT = {
|
|
241650
|
+
".ts": "typescript",
|
|
241651
|
+
".tsx": "typescript",
|
|
241652
|
+
".js": "javascript",
|
|
241653
|
+
".jsx": "javascript",
|
|
241654
|
+
".py": "python",
|
|
241655
|
+
".go": "go",
|
|
241656
|
+
".rs": "rust",
|
|
241657
|
+
".java": "java",
|
|
241658
|
+
".cs": "csharp"
|
|
241659
|
+
};
|
|
241660
|
+
function isTypeScriptOrJS(filePath) {
|
|
241661
|
+
const ext2 = import_path.default.extname(filePath).toLowerCase();
|
|
241662
|
+
return [".ts", ".tsx", ".js", ".jsx"].includes(ext2);
|
|
241663
|
+
}
|
|
241633
241664
|
async function discoverFiles(repoRoot) {
|
|
241634
241665
|
const patterns = SUPPORTED_EXTENSIONS.map((ext2) => `**/*${ext2}`);
|
|
241635
241666
|
const files = await glob(patterns, {
|
|
241636
241667
|
cwd: repoRoot,
|
|
241637
241668
|
absolute: false,
|
|
241638
|
-
ignore: [
|
|
241669
|
+
ignore: [
|
|
241670
|
+
"**/node_modules/**",
|
|
241671
|
+
"**/dist/**",
|
|
241672
|
+
"**/build/**",
|
|
241673
|
+
"**/.git/**",
|
|
241674
|
+
"**/*.d.ts",
|
|
241675
|
+
// Python
|
|
241676
|
+
"**/__pycache__/**",
|
|
241677
|
+
"**/.venv/**",
|
|
241678
|
+
"**/venv/**",
|
|
241679
|
+
"**/*.pyc",
|
|
241680
|
+
// Go
|
|
241681
|
+
"**/vendor/**",
|
|
241682
|
+
// Rust
|
|
241683
|
+
"**/target/**",
|
|
241684
|
+
// Java / C#
|
|
241685
|
+
"**/bin/**",
|
|
241686
|
+
"**/obj/**",
|
|
241687
|
+
"**/.gradle/**"
|
|
241688
|
+
]
|
|
241639
241689
|
});
|
|
241640
241690
|
return files.map((f) => f.replace(/\\/g, "/")).sort();
|
|
241641
241691
|
}
|
|
@@ -242516,7 +242566,7 @@ function detectSecurityPatterns(filePath, content) {
|
|
|
242516
242566
|
}
|
|
242517
242567
|
async function classifyTopology(repoRoot, allFiles) {
|
|
242518
242568
|
const type = await detectProjectType(repoRoot);
|
|
242519
|
-
const framework = await detectFramework(repoRoot);
|
|
242569
|
+
const framework = await detectFramework(repoRoot, allFiles);
|
|
242520
242570
|
const entryPoints = detectEntryPoints(allFiles);
|
|
242521
242571
|
const layers = detectLayers(allFiles);
|
|
242522
242572
|
const modules = detectModules(allFiles);
|
|
@@ -242539,9 +242589,8 @@ async function detectProjectType(repoRoot) {
|
|
|
242539
242589
|
}
|
|
242540
242590
|
return "monolith";
|
|
242541
242591
|
}
|
|
242542
|
-
async function
|
|
242592
|
+
async function readFrameworkFromPkg(pkgPath) {
|
|
242543
242593
|
try {
|
|
242544
|
-
const pkgPath = import_path4.default.join(repoRoot, "package.json");
|
|
242545
242594
|
const pkg = JSON.parse(await import_promises3.default.readFile(pkgPath, "utf-8"));
|
|
242546
242595
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
242547
242596
|
if (deps["next"]) return `next@${deps["next"].replace(/[\^~]/g, "")}`;
|
|
@@ -242558,30 +242607,68 @@ async function detectFramework(repoRoot) {
|
|
|
242558
242607
|
if (deps["@angular/core"]) return "angular";
|
|
242559
242608
|
} catch {
|
|
242560
242609
|
}
|
|
242561
|
-
|
|
242562
|
-
|
|
242563
|
-
|
|
242564
|
-
|
|
242565
|
-
|
|
242566
|
-
|
|
242567
|
-
|
|
242568
|
-
|
|
242569
|
-
} catch {
|
|
242610
|
+
return null;
|
|
242611
|
+
}
|
|
242612
|
+
async function findSubPackageRoots(repoRoot, allFiles) {
|
|
242613
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
242614
|
+
for (const f of allFiles) {
|
|
242615
|
+
const parts = f.split("/");
|
|
242616
|
+
if (parts.length >= 1) candidates.add(parts[0]);
|
|
242617
|
+
if (parts.length >= 2) candidates.add(`${parts[0]}/${parts[1]}`);
|
|
242570
242618
|
}
|
|
242571
|
-
|
|
242572
|
-
|
|
242573
|
-
|
|
242574
|
-
|
|
242619
|
+
const roots = [];
|
|
242620
|
+
for (const candidate of candidates) {
|
|
242621
|
+
const pkgPath = import_path4.default.join(repoRoot, candidate, "package.json");
|
|
242622
|
+
try {
|
|
242623
|
+
await import_promises3.default.access(pkgPath);
|
|
242624
|
+
roots.push(candidate);
|
|
242625
|
+
} catch {
|
|
242626
|
+
}
|
|
242575
242627
|
}
|
|
242576
|
-
|
|
242577
|
-
|
|
242578
|
-
|
|
242579
|
-
|
|
242628
|
+
return roots;
|
|
242629
|
+
}
|
|
242630
|
+
async function detectFramework(repoRoot, allFiles) {
|
|
242631
|
+
const rootFramework = await readFrameworkFromPkg(import_path4.default.join(repoRoot, "package.json"));
|
|
242632
|
+
if (rootFramework) return rootFramework;
|
|
242633
|
+
const configChecks = [
|
|
242634
|
+
["next.config.js", "next"],
|
|
242635
|
+
["next.config.mjs", "next"],
|
|
242636
|
+
["next.config.ts", "next"],
|
|
242637
|
+
["vite.config.ts", "vite"],
|
|
242638
|
+
["astro.config.mjs", "astro"]
|
|
242639
|
+
];
|
|
242640
|
+
for (const [cfg, name] of configChecks) {
|
|
242641
|
+
try {
|
|
242642
|
+
await import_promises3.default.access(import_path4.default.join(repoRoot, cfg));
|
|
242643
|
+
return name;
|
|
242644
|
+
} catch {
|
|
242645
|
+
}
|
|
242580
242646
|
}
|
|
242581
|
-
|
|
242582
|
-
await
|
|
242583
|
-
|
|
242584
|
-
|
|
242647
|
+
if (allFiles && allFiles.length > 0) {
|
|
242648
|
+
const subRoots = await findSubPackageRoots(repoRoot, allFiles);
|
|
242649
|
+
const detected = [];
|
|
242650
|
+
for (const subRoot of subRoots) {
|
|
242651
|
+
const fw = await readFrameworkFromPkg(import_path4.default.join(repoRoot, subRoot, "package.json"));
|
|
242652
|
+
if (fw && !detected.includes(fw)) detected.push(fw);
|
|
242653
|
+
}
|
|
242654
|
+
for (const subRoot of subRoots) {
|
|
242655
|
+
for (const [cfg, name] of configChecks) {
|
|
242656
|
+
try {
|
|
242657
|
+
await import_promises3.default.access(import_path4.default.join(repoRoot, subRoot, cfg));
|
|
242658
|
+
if (!detected.includes(name)) detected.push(name);
|
|
242659
|
+
} catch {
|
|
242660
|
+
}
|
|
242661
|
+
}
|
|
242662
|
+
}
|
|
242663
|
+
if (detected.length === 1) return detected[0];
|
|
242664
|
+
if (detected.length > 1) {
|
|
242665
|
+
const prefixCount = /* @__PURE__ */ new Map();
|
|
242666
|
+
for (const d of detected) {
|
|
242667
|
+
const prefix = d.split("@")[0];
|
|
242668
|
+
prefixCount.set(prefix, (prefixCount.get(prefix) ?? 0) + 1);
|
|
242669
|
+
}
|
|
242670
|
+
return [...prefixCount.entries()].map(([fw, count]) => count > 1 ? `${fw} (\xD7${count})` : fw).join(", ");
|
|
242671
|
+
}
|
|
242585
242672
|
}
|
|
242586
242673
|
return null;
|
|
242587
242674
|
}
|
|
@@ -242593,11 +242680,15 @@ function detectEntryPoints(files) {
|
|
|
242593
242680
|
entries.push({ path: f, type });
|
|
242594
242681
|
continue;
|
|
242595
242682
|
}
|
|
242596
|
-
if (
|
|
242683
|
+
if (/(?:^|\/)app\/.*page\.(ts|js)x?$/.test(f) || /(?:^|\/)app\/layout\.(ts|js)x?$/.test(f)) {
|
|
242684
|
+
entries.push({ path: f, type: "api" });
|
|
242685
|
+
continue;
|
|
242686
|
+
}
|
|
242687
|
+
if (/(?:^|\/)app\/.*(?:loading|error|not-found)\.(ts|js)x?$/.test(f)) {
|
|
242597
242688
|
entries.push({ path: f, type: "api" });
|
|
242598
242689
|
continue;
|
|
242599
242690
|
}
|
|
242600
|
-
if (
|
|
242691
|
+
if (/(?:^|\/)app\/.*route\.(ts|js)x?$/.test(f) || /(?:^|\/)pages\/api\//.test(f)) {
|
|
242601
242692
|
entries.push({ path: f, type: "api" });
|
|
242602
242693
|
continue;
|
|
242603
242694
|
}
|
|
@@ -242763,6 +242854,35 @@ function extractCyclomaticComplexity(ast, filePath) {
|
|
|
242763
242854
|
return fileMetrics;
|
|
242764
242855
|
}
|
|
242765
242856
|
var logger = createLogger("deepstrike");
|
|
242857
|
+
function detectPrimaryLanguage(filePaths) {
|
|
242858
|
+
const counts = computeLanguageCounts(filePaths);
|
|
242859
|
+
let maxLang = "typescript";
|
|
242860
|
+
let maxCount = 0;
|
|
242861
|
+
for (const [lang, count] of Object.entries(counts)) {
|
|
242862
|
+
if (count > maxCount) {
|
|
242863
|
+
maxCount = count;
|
|
242864
|
+
maxLang = lang;
|
|
242865
|
+
}
|
|
242866
|
+
}
|
|
242867
|
+
return maxLang;
|
|
242868
|
+
}
|
|
242869
|
+
function computeLanguageBreakdown(filePaths) {
|
|
242870
|
+
const counts = computeLanguageCounts(filePaths);
|
|
242871
|
+
const total = filePaths.length || 1;
|
|
242872
|
+
return Object.entries(counts).map(([name, count]) => ({
|
|
242873
|
+
name,
|
|
242874
|
+
percentage: Math.round(count / total * 100)
|
|
242875
|
+
}));
|
|
242876
|
+
}
|
|
242877
|
+
function computeLanguageCounts(filePaths) {
|
|
242878
|
+
const counts = {};
|
|
242879
|
+
for (const fp of filePaths) {
|
|
242880
|
+
const ext2 = fp.slice(fp.lastIndexOf(".")).toLowerCase();
|
|
242881
|
+
const lang = LANGUAGE_BY_EXT[ext2] ?? "unknown";
|
|
242882
|
+
counts[lang] = (counts[lang] ?? 0) + 1;
|
|
242883
|
+
}
|
|
242884
|
+
return counts;
|
|
242885
|
+
}
|
|
242766
242886
|
async function runDeepStrike(store, opts) {
|
|
242767
242887
|
const start = Date.now();
|
|
242768
242888
|
const scanId = store.getMeta().scan_id;
|
|
@@ -242808,7 +242928,7 @@ async function runDeepStrike(store, opts) {
|
|
|
242808
242928
|
allFindings.push(...secFindings);
|
|
242809
242929
|
try {
|
|
242810
242930
|
const partialEntry = await buildFileIndexEntry(opts.repoRoot, relPath, content, symbols.length);
|
|
242811
|
-
const cyclomaticByFunc = extractCyclomaticComplexity(ast, relPath);
|
|
242931
|
+
const cyclomaticByFunc = isTypeScriptOrJS(relPath) ? extractCyclomaticComplexity(parseFile(absPath, content, opts.repoRoot).ast, relPath) : /* @__PURE__ */ new Map();
|
|
242812
242932
|
const entry = {
|
|
242813
242933
|
...partialEntry,
|
|
242814
242934
|
complexity: {
|
|
@@ -242840,8 +242960,8 @@ async function runDeepStrike(store, opts) {
|
|
|
242840
242960
|
name: repoName,
|
|
242841
242961
|
path: opts.repoRoot,
|
|
242842
242962
|
remote_url: null,
|
|
242843
|
-
primary_language:
|
|
242844
|
-
languages:
|
|
242963
|
+
primary_language: detectPrimaryLanguage(fileIndexEntries.map((e) => e.path)),
|
|
242964
|
+
languages: computeLanguageBreakdown(fileIndexEntries.map((e) => e.path)),
|
|
242845
242965
|
framework: null,
|
|
242846
242966
|
total_files: relFiles.length,
|
|
242847
242967
|
total_loc: fileIndexEntries.reduce((s, e) => s + e.loc, 0),
|
|
@@ -243034,6 +243154,73 @@ function buildImpactChains(candidates) {
|
|
|
243034
243154
|
function describeMechanism(root, related, depth) {
|
|
243035
243155
|
return `${root.type} in ${root.file} propagates via ${depth} dependency hop(s) to ${related.type} in ${related.file}`;
|
|
243036
243156
|
}
|
|
243157
|
+
var CROSS_BUNDLE_SAFE_EXPORTS = /* @__PURE__ */ new Set([
|
|
243158
|
+
// action_contracts
|
|
243159
|
+
"ActionType",
|
|
243160
|
+
"PlannedAction",
|
|
243161
|
+
"ShellCommandSpec",
|
|
243162
|
+
"TEST_COMMAND_WHITELIST",
|
|
243163
|
+
"SENSITIVE_KEY_RE",
|
|
243164
|
+
"DESTRUCTIVE_RE",
|
|
243165
|
+
// orchestrator / plan
|
|
243166
|
+
"OrchestratorPlan",
|
|
243167
|
+
"OrchestratorStep",
|
|
243168
|
+
// contracts / store
|
|
243169
|
+
"MahadataStore",
|
|
243170
|
+
"InMemoryMahadataStore",
|
|
243171
|
+
"Mahadata",
|
|
243172
|
+
"TokenCounter",
|
|
243173
|
+
"LLMClientConfig",
|
|
243174
|
+
// handlers exposed via MCP tool layer
|
|
243175
|
+
"handleAnalyzeRepo",
|
|
243176
|
+
"handleBlastRadius",
|
|
243177
|
+
"handleGetHotspots",
|
|
243178
|
+
"handleGetMahadata",
|
|
243179
|
+
// shared utilities
|
|
243180
|
+
"calculateRiskScore",
|
|
243181
|
+
"assembleGraph",
|
|
243182
|
+
"classifyTopology",
|
|
243183
|
+
"resolveTaskDependencies",
|
|
243184
|
+
// constants / prompts
|
|
243185
|
+
"SYSTEM_PROMPT",
|
|
243186
|
+
"JSON_TAG_RE",
|
|
243187
|
+
// Next.js / framework reserved exports — always safe
|
|
243188
|
+
"metadata",
|
|
243189
|
+
"nextConfig",
|
|
243190
|
+
"geistSans",
|
|
243191
|
+
"geistMono",
|
|
243192
|
+
"default"
|
|
243193
|
+
// default exports are always externally consumable
|
|
243194
|
+
]);
|
|
243195
|
+
var SAFE_EXPORT_PREFIXES = [
|
|
243196
|
+
"handle",
|
|
243197
|
+
// MCP handlers
|
|
243198
|
+
"create",
|
|
243199
|
+
// factory functions
|
|
243200
|
+
"build",
|
|
243201
|
+
// builder functions
|
|
243202
|
+
"run"
|
|
243203
|
+
// runner functions exposed to orchestrator
|
|
243204
|
+
];
|
|
243205
|
+
var CROSS_BUNDLE_BARREL_PATTERNS = [
|
|
243206
|
+
/\/contracts\/index\.ts$/,
|
|
243207
|
+
/\/action_contracts\/index\.ts$/,
|
|
243208
|
+
/\/action_layer\/index\.ts$/,
|
|
243209
|
+
/\/shared\/index\.ts$/,
|
|
243210
|
+
/\/shared\/logger\/index\.ts$/,
|
|
243211
|
+
/\/orchestrator\/index\.ts$/
|
|
243212
|
+
];
|
|
243213
|
+
function isCrossBundleSafeSymbol(symbolName, filePath) {
|
|
243214
|
+
if (CROSS_BUNDLE_SAFE_EXPORTS.has(symbolName)) return true;
|
|
243215
|
+
for (const prefix of SAFE_EXPORT_PREFIXES) {
|
|
243216
|
+
if (symbolName.startsWith(prefix)) return true;
|
|
243217
|
+
}
|
|
243218
|
+
if (symbolName === "logger" || symbolName.startsWith("logger")) return true;
|
|
243219
|
+
for (const pattern of CROSS_BUNDLE_BARREL_PATTERNS) {
|
|
243220
|
+
if (pattern.test(filePath)) return true;
|
|
243221
|
+
}
|
|
243222
|
+
return false;
|
|
243223
|
+
}
|
|
243037
243224
|
function tagNoise(findings, chains) {
|
|
243038
243225
|
const suppressionReasons = /* @__PURE__ */ new Map();
|
|
243039
243226
|
const severityOrder = {
|
|
@@ -243043,6 +243230,17 @@ function tagNoise(findings, chains) {
|
|
|
243043
243230
|
low: 1,
|
|
243044
243231
|
info: 0
|
|
243045
243232
|
};
|
|
243233
|
+
for (const f of findings) {
|
|
243234
|
+
if (f.type !== "dead_code") continue;
|
|
243235
|
+
if (suppressionReasons.has(f.id)) continue;
|
|
243236
|
+
const symbolName = f.symbol ? f.symbol.split("::")[1] ?? f.symbol : "";
|
|
243237
|
+
if (isCrossBundleSafeSymbol(symbolName, f.file)) {
|
|
243238
|
+
suppressionReasons.set(
|
|
243239
|
+
f.id,
|
|
243240
|
+
`Cross-bundle false positive: '${symbolName}' is a known exported interface consumed outside this scan boundary.`
|
|
243241
|
+
);
|
|
243242
|
+
}
|
|
243243
|
+
}
|
|
243046
243244
|
const groupedByFileType = /* @__PURE__ */ new Map();
|
|
243047
243245
|
for (const f of findings) {
|
|
243048
243246
|
const key = `${f.file}::${f.type}`;
|