blitzstrike 1.0.38 → 1.0.40
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/CHANGELOG.md +28 -0
- package/bench/corpus/java-cmd-processbuilder.md +8 -0
- package/bench/corpus/java-deser-readunshared.md +8 -0
- package/bench/corpus/java-sqli-execute.md +8 -0
- package/bench/corpus/java-ssrf-httpurl.md +8 -0
- package/bench/corpus/java-xss-escape.md +8 -0
- package/bench/corpus/java-xss-println.md +8 -0
- package/bench/corpus/js-cmd-execsync.md +8 -0
- package/bench/corpus/js-filewrite.md +8 -0
- package/bench/corpus/js-pathtraversal-readsync.md +8 -0
- package/bench/corpus/js-redirect-encodeuri.md +8 -0
- package/bench/corpus/js-ssrf-axios.md +8 -0
- package/bench/corpus/js-ssrf-fetch.md +8 -0
- package/bench/corpus/js-xss-documentwrite.md +8 -0
- package/bench/corpus/js-xss-innerhtml.md +8 -0
- package/bench/corpus/php-cmd-absint.md +9 -0
- package/bench/corpus/php-cmd-popen.md +9 -0
- package/bench/corpus/php-deser-json.md +9 -0
- package/bench/corpus/php-include-once.md +9 -0
- package/bench/corpus/php-include-sanitized.md +9 -0
- package/bench/corpus/php-sqli-filterinput.md +9 -0
- package/bench/corpus/php-sqli-getresults.md +9 -0
- package/bench/corpus/php-ssrf-wppost.md +9 -0
- package/bench/corpus/php-xss-escurl.md +9 -0
- package/bench/corpus/php-xss-print.md +9 -0
- package/bench/corpus/php-xss-sanitizetext.md +9 -0
- package/bench/corpus/py-cmd-popen.md +9 -0
- package/bench/corpus/py-cmd-shlex.md +9 -0
- package/bench/corpus/py-deser-yaml.md +9 -0
- package/bench/corpus/py-sqli-executescript.md +9 -0
- package/bench/corpus/py-ssrf-urllib.md +9 -0
- package/bench/corpus/py-xss-escape.md +8 -0
- package/dist/index.js +258 -84
- package/intelligence/frameworks.json +1781 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -14820,10 +14820,10 @@ var require_src = __commonJS(function(exports, module) {
|
|
|
14820
14820
|
});
|
|
14821
14821
|
|
|
14822
14822
|
// src/sync.ts
|
|
14823
|
-
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync4, readFileSync as
|
|
14824
|
-
import { join as
|
|
14823
|
+
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync4, readFileSync as readFileSync16, readdirSync as readdirSync5, statSync as statSync3, rmSync as rmSync3 } from "node:fs";
|
|
14824
|
+
import { join as join12 } from "node:path";
|
|
14825
14825
|
import { homedir as homedir4 } from "node:os";
|
|
14826
|
-
import { fileURLToPath as
|
|
14826
|
+
import { fileURLToPath as fileURLToPath8 } from "node:url";
|
|
14827
14827
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
14828
14828
|
function dirExists(p) {
|
|
14829
14829
|
try {
|
|
@@ -14838,7 +14838,7 @@ function countFiles(dir) {
|
|
|
14838
14838
|
const walk = (d) => {
|
|
14839
14839
|
for (const e of readdirSync5(d, { withFileTypes: true })) {
|
|
14840
14840
|
if (e.isDirectory())
|
|
14841
|
-
walk(
|
|
14841
|
+
walk(join12(d, e.name));
|
|
14842
14842
|
else
|
|
14843
14843
|
n++;
|
|
14844
14844
|
}
|
|
@@ -14852,8 +14852,8 @@ function countFiles(dir) {
|
|
|
14852
14852
|
function syncData() {
|
|
14853
14853
|
if (!dirExists(DATA_ROOT2))
|
|
14854
14854
|
mkdirSync3(DATA_ROOT2, { recursive: true });
|
|
14855
|
-
const payloadsPath =
|
|
14856
|
-
const templatesPath =
|
|
14855
|
+
const payloadsPath = join12(DATA_ROOT2, "payloads");
|
|
14856
|
+
const templatesPath = join12(DATA_ROOT2, "templates");
|
|
14857
14857
|
const hasPayloads = dirExists(payloadsPath) && countFiles(payloadsPath) > 0;
|
|
14858
14858
|
const hasTemplates = dirExists(templatesPath) && countFiles(templatesPath) > 0;
|
|
14859
14859
|
if (hasPayloads && hasTemplates) {
|
|
@@ -14863,8 +14863,8 @@ function syncData() {
|
|
|
14863
14863
|
data_root: DATA_ROOT2
|
|
14864
14864
|
};
|
|
14865
14865
|
}
|
|
14866
|
-
const localPayloads =
|
|
14867
|
-
const localTemplates =
|
|
14866
|
+
const localPayloads = join12(ROOT6, "payloads");
|
|
14867
|
+
const localTemplates = join12(ROOT6, "templates");
|
|
14868
14868
|
let payloadsSynced = false;
|
|
14869
14869
|
let templatesSynced = false;
|
|
14870
14870
|
if (!hasPayloads && dirExists(localPayloads)) {
|
|
@@ -14877,7 +14877,7 @@ function syncData() {
|
|
|
14877
14877
|
}
|
|
14878
14878
|
const gitOk = spawnSync2("git", ["--version"]).status === 0;
|
|
14879
14879
|
if (gitOk && (!dirExists(payloadsPath) || countFiles(payloadsPath) === 0 || !dirExists(templatesPath) || countFiles(templatesPath) === 0)) {
|
|
14880
|
-
const tmp =
|
|
14880
|
+
const tmp = join12(DATA_ROOT2, ".tmp-clone");
|
|
14881
14881
|
if (dirExists(tmp))
|
|
14882
14882
|
rmSync3(tmp, { recursive: true, force: true });
|
|
14883
14883
|
const r = spawnSync2("git", ["clone", "--depth", "1", "--filter=blob:none", "--sparse", GITHUB_REPO, tmp], { timeout: 120000 });
|
|
@@ -14885,11 +14885,11 @@ function syncData() {
|
|
|
14885
14885
|
const sparse = spawnSync2("git", ["-C", tmp, "sparse-checkout", "set", "payloads", "templates"], { timeout: 120000 });
|
|
14886
14886
|
if (sparse.status === 0) {
|
|
14887
14887
|
if (!dirExists(payloadsPath) || countFiles(payloadsPath) === 0) {
|
|
14888
|
-
copyDir(
|
|
14888
|
+
copyDir(join12(tmp, "payloads"), payloadsPath);
|
|
14889
14889
|
payloadsSynced = true;
|
|
14890
14890
|
}
|
|
14891
14891
|
if (!dirExists(templatesPath) || countFiles(templatesPath) === 0) {
|
|
14892
|
-
copyDir(
|
|
14892
|
+
copyDir(join12(tmp, "templates"), templatesPath);
|
|
14893
14893
|
templatesSynced = true;
|
|
14894
14894
|
}
|
|
14895
14895
|
}
|
|
@@ -14911,18 +14911,18 @@ function copyDir(src, dst) {
|
|
|
14911
14911
|
function manualCopy(src, dst) {
|
|
14912
14912
|
mkdirSync3(dst, { recursive: true });
|
|
14913
14913
|
for (const e of readdirSync5(src, { withFileTypes: true })) {
|
|
14914
|
-
const s =
|
|
14915
|
-
const d =
|
|
14914
|
+
const s = join12(src, e.name);
|
|
14915
|
+
const d = join12(dst, e.name);
|
|
14916
14916
|
if (e.isDirectory())
|
|
14917
14917
|
manualCopy(s, d);
|
|
14918
14918
|
else
|
|
14919
|
-
writeFileSync4(d,
|
|
14919
|
+
writeFileSync4(d, readFileSync16(s));
|
|
14920
14920
|
}
|
|
14921
14921
|
}
|
|
14922
|
-
var
|
|
14922
|
+
var ROOT6, DATA_ROOT2, GITHUB_REPO = "https://github.com/shinthink/blitzstrike.git";
|
|
14923
14923
|
var init_sync = __esm(() => {
|
|
14924
|
-
|
|
14925
|
-
DATA_ROOT2 = process.env.BLITZSTRIKE_DATA ??
|
|
14924
|
+
ROOT6 = join12(fileURLToPath8(new URL(".", import.meta.url)), "..");
|
|
14925
|
+
DATA_ROOT2 = process.env.BLITZSTRIKE_DATA ?? join12(homedir4(), ".blitzstrike", "data");
|
|
14926
14926
|
});
|
|
14927
14927
|
|
|
14928
14928
|
// src/update.ts
|
|
@@ -29601,6 +29601,7 @@ class StdioServerTransport {
|
|
|
29601
29601
|
|
|
29602
29602
|
// src/server.ts
|
|
29603
29603
|
init_scanner();
|
|
29604
|
+
import { readFileSync as readFileSync17 } from "node:fs";
|
|
29604
29605
|
|
|
29605
29606
|
// src/recon.ts
|
|
29606
29607
|
var TIMEOUT_MS = 25000;
|
|
@@ -50491,13 +50492,23 @@ var jsAdapter = {
|
|
|
50491
50492
|
rhsVars.push(c.name);
|
|
50492
50493
|
assigns.push({ target: name, sources: rhsVars, line: n.loc?.start?.line ?? 0 });
|
|
50493
50494
|
}
|
|
50494
|
-
if (n.type === "AssignmentExpression"
|
|
50495
|
-
|
|
50496
|
-
|
|
50497
|
-
|
|
50498
|
-
|
|
50499
|
-
|
|
50500
|
-
|
|
50495
|
+
if (n.type === "AssignmentExpression") {
|
|
50496
|
+
if (n.left?.type === "MemberExpression" && n.left.property?.name === "innerHTML") {
|
|
50497
|
+
const rhsVars = [];
|
|
50498
|
+
for (const c of jsWalk(n.right, []))
|
|
50499
|
+
if (c.type === "Identifier" && c.name)
|
|
50500
|
+
rhsVars.push(c.name);
|
|
50501
|
+
for (const v of rhsVars)
|
|
50502
|
+
sinks.push({ variable: v, kind: "html_render", line: n.loc?.start?.line ?? 0, cwe: "CWE-79" });
|
|
50503
|
+
}
|
|
50504
|
+
if (n.left?.type === "Identifier") {
|
|
50505
|
+
const name = n.left.name;
|
|
50506
|
+
const rhsVars = [];
|
|
50507
|
+
for (const c of jsWalk(n.right, []))
|
|
50508
|
+
if (c.type === "Identifier" && c.name)
|
|
50509
|
+
rhsVars.push(c.name);
|
|
50510
|
+
assigns.push({ target: name, sources: rhsVars, line: n.loc?.start?.line ?? 0 });
|
|
50511
|
+
}
|
|
50501
50512
|
}
|
|
50502
50513
|
if (n.type === "CallExpression") {
|
|
50503
50514
|
const calleeText = srcText(n.callee);
|
|
@@ -50937,6 +50948,98 @@ function analyzeGitDiff(repo, base = "HEAD~1", head = "HEAD") {
|
|
|
50937
50948
|
return { repo, base, head, files, total_changes, security_sensitive };
|
|
50938
50949
|
}
|
|
50939
50950
|
|
|
50951
|
+
// src/frameworks.ts
|
|
50952
|
+
import { readFileSync as readFileSync14, existsSync as existsSync6 } from "node:fs";
|
|
50953
|
+
import { join as join10 } from "node:path";
|
|
50954
|
+
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
50955
|
+
var ROOT4 = join10(fileURLToPath6(new URL(".", import.meta.url)), "..");
|
|
50956
|
+
var INTEL2 = join10(ROOT4, "intelligence");
|
|
50957
|
+
var cache = null;
|
|
50958
|
+
function load() {
|
|
50959
|
+
if (cache)
|
|
50960
|
+
return cache;
|
|
50961
|
+
const p = join10(INTEL2, "frameworks.json");
|
|
50962
|
+
if (!existsSync6(p))
|
|
50963
|
+
return null;
|
|
50964
|
+
try {
|
|
50965
|
+
cache = JSON.parse(readFileSync14(p, "utf8"));
|
|
50966
|
+
return cache;
|
|
50967
|
+
} catch {
|
|
50968
|
+
return null;
|
|
50969
|
+
}
|
|
50970
|
+
}
|
|
50971
|
+
function listFrameworks() {
|
|
50972
|
+
const data = load();
|
|
50973
|
+
if (!data)
|
|
50974
|
+
return [];
|
|
50975
|
+
return data.frameworks.map((f) => ({ id: f.id, name: f.name, language: f.language }));
|
|
50976
|
+
}
|
|
50977
|
+
function frameworkProfile(id) {
|
|
50978
|
+
const data = load();
|
|
50979
|
+
if (!data)
|
|
50980
|
+
return null;
|
|
50981
|
+
return data.frameworks.find((f) => f.id === id) ?? null;
|
|
50982
|
+
}
|
|
50983
|
+
function detectFramework(code, language) {
|
|
50984
|
+
const data = load();
|
|
50985
|
+
const hay = code.toLowerCase();
|
|
50986
|
+
const candidates = [];
|
|
50987
|
+
if (data) {
|
|
50988
|
+
for (const f of data.frameworks) {
|
|
50989
|
+
if (f.language !== language)
|
|
50990
|
+
continue;
|
|
50991
|
+
const score = f.detect.reduce((n, sig) => hay.includes(sig.toLowerCase()) ? n + 1 : n, 0);
|
|
50992
|
+
if (score > 0)
|
|
50993
|
+
candidates.push({ id: f.id, name: f.name, score });
|
|
50994
|
+
}
|
|
50995
|
+
}
|
|
50996
|
+
candidates.sort((a, b) => b.score - a.score);
|
|
50997
|
+
return { framework: candidates[0]?.id ?? null, language, candidates };
|
|
50998
|
+
}
|
|
50999
|
+
function scanFramework(code, frameworkId) {
|
|
51000
|
+
const profile = frameworkProfile(frameworkId);
|
|
51001
|
+
const empty = {
|
|
51002
|
+
framework: frameworkId,
|
|
51003
|
+
language: profile?.language ?? "unknown",
|
|
51004
|
+
detected: false,
|
|
51005
|
+
sources: [],
|
|
51006
|
+
sinks: [],
|
|
51007
|
+
sanitizers: [],
|
|
51008
|
+
auth: [],
|
|
51009
|
+
summary: { sources: 0, sinks: 0, sanitizers: 0, auth: 0 }
|
|
51010
|
+
};
|
|
51011
|
+
if (!profile)
|
|
51012
|
+
return empty;
|
|
51013
|
+
const lines = code.split(`
|
|
51014
|
+
`);
|
|
51015
|
+
const scan = (patterns) => {
|
|
51016
|
+
const hits = [];
|
|
51017
|
+
for (let i = 0;i < lines.length; i++) {
|
|
51018
|
+
const line = lines[i].toLowerCase();
|
|
51019
|
+
for (const p of patterns) {
|
|
51020
|
+
if (line.includes(p.pattern.toLowerCase())) {
|
|
51021
|
+
hits.push({ line: i + 1, pattern: p.pattern, label: p.label, cwe: p.cwe });
|
|
51022
|
+
}
|
|
51023
|
+
}
|
|
51024
|
+
}
|
|
51025
|
+
return hits;
|
|
51026
|
+
};
|
|
51027
|
+
const sources = scan(profile.sources);
|
|
51028
|
+
const sinks = scan(profile.sinks);
|
|
51029
|
+
const sanitizers = scan(profile.sanitizers);
|
|
51030
|
+
const auth = scan(profile.auth);
|
|
51031
|
+
return {
|
|
51032
|
+
framework: frameworkId,
|
|
51033
|
+
language: profile.language,
|
|
51034
|
+
detected: true,
|
|
51035
|
+
sources,
|
|
51036
|
+
sinks,
|
|
51037
|
+
sanitizers,
|
|
51038
|
+
auth,
|
|
51039
|
+
summary: { sources: sources.length, sinks: sinks.length, sanitizers: sanitizers.length, auth: auth.length }
|
|
51040
|
+
};
|
|
51041
|
+
}
|
|
51042
|
+
|
|
50940
51043
|
// src/guidance.ts
|
|
50941
51044
|
var SINK_DIRECTIVES = {
|
|
50942
51045
|
sql_execution: "Validate the SQL injection live with strike_verify (use a unique MARKER and a NEGATIVE CONTROL that stays inert), or read_tool_manual for sqlmap. Only a reflected marker + inert control = confirmed.",
|
|
@@ -51750,16 +51853,16 @@ function reportJson(findings, opts = {}) {
|
|
|
51750
51853
|
}
|
|
51751
51854
|
|
|
51752
51855
|
// src/benchmark.ts
|
|
51753
|
-
import { readFileSync as
|
|
51754
|
-
import { join as
|
|
51755
|
-
import { fileURLToPath as
|
|
51856
|
+
import { readFileSync as readFileSync15, readdirSync as readdirSync4, writeFileSync as writeFileSync3, rmSync as rmSync2 } from "node:fs";
|
|
51857
|
+
import { join as join11 } from "node:path";
|
|
51858
|
+
import { fileURLToPath as fileURLToPath7 } from "node:url";
|
|
51756
51859
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
51757
|
-
var
|
|
51758
|
-
var CORPUS =
|
|
51860
|
+
var ROOT5 = join11(fileURLToPath7(new URL(".", import.meta.url)), "..");
|
|
51861
|
+
var CORPUS = join11(ROOT5, "bench", "corpus");
|
|
51759
51862
|
function parseCorpusFile(path) {
|
|
51760
51863
|
let text;
|
|
51761
51864
|
try {
|
|
51762
|
-
text =
|
|
51865
|
+
text = readFileSync15(path, "utf8");
|
|
51763
51866
|
} catch {
|
|
51764
51867
|
return null;
|
|
51765
51868
|
}
|
|
@@ -51809,7 +51912,7 @@ function loadCorpus() {
|
|
|
51809
51912
|
for (const f of readdirSync4(CORPUS)) {
|
|
51810
51913
|
if (!f.endsWith(".md"))
|
|
51811
51914
|
continue;
|
|
51812
|
-
const e = parseCorpusFile(
|
|
51915
|
+
const e = parseCorpusFile(join11(CORPUS, f));
|
|
51813
51916
|
if (e)
|
|
51814
51917
|
entries.push(e);
|
|
51815
51918
|
}
|
|
@@ -51821,7 +51924,7 @@ function loadCorpus() {
|
|
|
51821
51924
|
function detect(entry) {
|
|
51822
51925
|
const lang = entry.language;
|
|
51823
51926
|
if (lang === "php") {
|
|
51824
|
-
const tmp =
|
|
51927
|
+
const tmp = join11(tmpdir2(), `blitzstrike-bench-${entry.id}-${process.pid}.php`);
|
|
51825
51928
|
writeFileSync3(tmp, entry.code);
|
|
51826
51929
|
const r = analyzeDataFlow2(tmp);
|
|
51827
51930
|
rmSync2(tmp, { force: true });
|
|
@@ -51946,6 +52049,19 @@ function coverageMatrix() {
|
|
|
51946
52049
|
const covered = langs.map((l) => l.language).filter((lang) => SINK_BY_LANG[lang]?.[sink] === true);
|
|
51947
52050
|
return { sink, languages: covered };
|
|
51948
52051
|
});
|
|
52052
|
+
const frameworkCoverage = listFrameworks().map(({ id, language }) => {
|
|
52053
|
+
const p = frameworkProfile(id);
|
|
52054
|
+
const cwe = p ? [...new Set(p.sinks.map((s) => s.cwe).filter((c) => !!c))].sort() : [];
|
|
52055
|
+
return {
|
|
52056
|
+
framework: id,
|
|
52057
|
+
language,
|
|
52058
|
+
sinks: p?.sinks.length ?? 0,
|
|
52059
|
+
sources: p?.sources.length ?? 0,
|
|
52060
|
+
sanitizers: p?.sanitizers.length ?? 0,
|
|
52061
|
+
auth: p?.auth.length ?? 0,
|
|
52062
|
+
cwe
|
|
52063
|
+
};
|
|
52064
|
+
});
|
|
51949
52065
|
let coveredPairs = 0;
|
|
51950
52066
|
for (const lang of langs) {
|
|
51951
52067
|
for (const sink of SINK_CLASSES) {
|
|
@@ -51957,11 +52073,13 @@ function coverageMatrix() {
|
|
|
51957
52073
|
return {
|
|
51958
52074
|
languages: langs,
|
|
51959
52075
|
sink_coverage: sinkCoverage,
|
|
52076
|
+
framework_coverage: frameworkCoverage,
|
|
51960
52077
|
counts: {
|
|
51961
52078
|
languages: langs.length,
|
|
51962
52079
|
sink_classes: SINK_CLASSES.length,
|
|
51963
52080
|
covered_pairs: coveredPairs,
|
|
51964
|
-
total_pairs: totalPairs
|
|
52081
|
+
total_pairs: totalPairs,
|
|
52082
|
+
frameworks: frameworkCoverage.length
|
|
51965
52083
|
},
|
|
51966
52084
|
coverage_ratio: Number((coveredPairs / totalPairs).toFixed(3))
|
|
51967
52085
|
};
|
|
@@ -52669,6 +52787,62 @@ function createServer() {
|
|
|
52669
52787
|
}
|
|
52670
52788
|
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
52671
52789
|
});
|
|
52790
|
+
server.registerTool("list_frameworks", {
|
|
52791
|
+
title: "List supported frameworks",
|
|
52792
|
+
description: "PHASE 5: list the web frameworks Blitz Strike understands for framework-aware security analysis (Laravel, WordPress, Express, Next.js, Django, FastAPI, Spring)."
|
|
52793
|
+
}, async () => {
|
|
52794
|
+
return { content: [{ type: "text", text: JSON.stringify({ frameworks: listFrameworks() }) }] };
|
|
52795
|
+
});
|
|
52796
|
+
server.registerTool("detect_framework", {
|
|
52797
|
+
title: "Detect web framework",
|
|
52798
|
+
description: "PHASE 5: detect the web framework used by a source file (reads the file, then scores each framework's detection signals for the file's language).",
|
|
52799
|
+
inputSchema: {
|
|
52800
|
+
path: string2().optional().describe("Source file path (alternative: code)"),
|
|
52801
|
+
code: string2().optional().describe("Source code directly (alternative to path)"),
|
|
52802
|
+
language: string2().optional().describe("Language (auto-detected from path if omitted)")
|
|
52803
|
+
}
|
|
52804
|
+
}, async ({ path, code, language }) => {
|
|
52805
|
+
let src = code ?? "";
|
|
52806
|
+
let lang = language;
|
|
52807
|
+
if (path && !code) {
|
|
52808
|
+
try {
|
|
52809
|
+
src = readFileSync17(path, "utf8");
|
|
52810
|
+
} catch {
|
|
52811
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: `cannot read ${path}` }) }] };
|
|
52812
|
+
}
|
|
52813
|
+
lang = lang ?? detectLanguage(path)?.language ?? "php";
|
|
52814
|
+
}
|
|
52815
|
+
const d = detectFramework(src, lang ?? "php");
|
|
52816
|
+
const scan = d.framework ? scanFramework(src, d.framework) : null;
|
|
52817
|
+
return {
|
|
52818
|
+
content: [{
|
|
52819
|
+
type: "text",
|
|
52820
|
+
text: JSON.stringify({
|
|
52821
|
+
framework: d.framework,
|
|
52822
|
+
language: d.language,
|
|
52823
|
+
candidates: d.candidates,
|
|
52824
|
+
scan
|
|
52825
|
+
})
|
|
52826
|
+
}]
|
|
52827
|
+
};
|
|
52828
|
+
});
|
|
52829
|
+
server.registerTool("framework_intel", {
|
|
52830
|
+
title: "Framework security knowledge",
|
|
52831
|
+
description: "PHASE 5: return a framework's security knowledge (sources, sinks, sanitizers, auth gates, routing, ORM, middleware) and optionally scan code with those patterns.",
|
|
52832
|
+
inputSchema: {
|
|
52833
|
+
framework: string2().describe("Framework id (see list_frameworks)"),
|
|
52834
|
+
code: string2().optional().describe("Optional source code to scan with the framework's patterns")
|
|
52835
|
+
}
|
|
52836
|
+
}, async ({ framework, code }) => {
|
|
52837
|
+
const profile = frameworkProfile(framework);
|
|
52838
|
+
if (!profile) {
|
|
52839
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: `unknown framework: ${framework}` }) }] };
|
|
52840
|
+
}
|
|
52841
|
+
const out = { profile };
|
|
52842
|
+
if (code !== undefined)
|
|
52843
|
+
out.scan = scanFramework(code, framework);
|
|
52844
|
+
return { content: [{ type: "text", text: JSON.stringify(out) }] };
|
|
52845
|
+
});
|
|
52672
52846
|
server.registerTool("taint_tree", {
|
|
52673
52847
|
title: "Whole-tree taint scan",
|
|
52674
52848
|
description: "EAGLE-EYE: run inter-procedural taint analysis across an entire source tree.",
|
|
@@ -52822,12 +52996,12 @@ async function serve() {
|
|
|
52822
52996
|
}
|
|
52823
52997
|
|
|
52824
52998
|
// src/cli.ts
|
|
52825
|
-
import { readFileSync as
|
|
52826
|
-
import { join as
|
|
52999
|
+
import { readFileSync as readFileSync18, existsSync as existsSync7, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4 } from "node:fs";
|
|
53000
|
+
import { join as join13, dirname as dirname2 } from "node:path";
|
|
52827
53001
|
import { homedir as homedir5 } from "node:os";
|
|
52828
|
-
import { fileURLToPath as
|
|
53002
|
+
import { fileURLToPath as fileURLToPath9 } from "node:url";
|
|
52829
53003
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
52830
|
-
var
|
|
53004
|
+
var ROOT7 = join13(fileURLToPath9(new URL(".", import.meta.url)), "..");
|
|
52831
53005
|
function which(cmd) {
|
|
52832
53006
|
return spawnSync3(`command -v ${cmd}`, { shell: true }).status === 0;
|
|
52833
53007
|
}
|
|
@@ -52862,7 +53036,7 @@ function runDoctor() {
|
|
|
52862
53036
|
detail: fofa ? "set" : "FOFA_EMAIL/FOFA_KEY not set",
|
|
52863
53037
|
fix: fofa ? undefined : "export FOFA_EMAIL=... && export FOFA_KEY=... (enables fofa_search)"
|
|
52864
53038
|
});
|
|
52865
|
-
const dataOk =
|
|
53039
|
+
const dataOk = existsSync7(join13(ROOT7, "chains.json")) && existsSync7(join13(ROOT7, "tools-catalog.json"));
|
|
52866
53040
|
issues.push({
|
|
52867
53041
|
name: "Data layers (chains + tools-catalog)",
|
|
52868
53042
|
status: dataOk ? "ok" : "fail",
|
|
@@ -52890,21 +53064,21 @@ BlitzStrike — ${verdict}`);
|
|
|
52890
53064
|
function resolveCommand() {
|
|
52891
53065
|
if (which("blitzstrike"))
|
|
52892
53066
|
return { command: "blitzstrike", args: ["serve", "--mcp"] };
|
|
52893
|
-
const src =
|
|
52894
|
-
if (which("bun") &&
|
|
53067
|
+
const src = join13(ROOT7, "src", "index.ts");
|
|
53068
|
+
if (which("bun") && existsSync7(src))
|
|
52895
53069
|
return { command: "bun", args: ["run", src, "serve", "--mcp"] };
|
|
52896
53070
|
if (which("npx"))
|
|
52897
53071
|
return { command: "npx", args: ["-y", "blitzstrike", "serve", "--mcp"] };
|
|
52898
53072
|
if (which("bunx"))
|
|
52899
53073
|
return { command: "bunx", args: ["blitzstrike", "serve", "--mcp"] };
|
|
52900
|
-
const dist =
|
|
52901
|
-
if (
|
|
53074
|
+
const dist = join13(ROOT7, "dist", "index.js");
|
|
53075
|
+
if (existsSync7(dist))
|
|
52902
53076
|
return { command: "bun", args: [dist, "serve", "--mcp"] };
|
|
52903
53077
|
return { command: "npx", args: ["-y", "blitzstrike", "serve", "--mcp"] };
|
|
52904
53078
|
}
|
|
52905
53079
|
function readJson(p) {
|
|
52906
53080
|
try {
|
|
52907
|
-
return JSON.parse(
|
|
53081
|
+
return JSON.parse(readFileSync18(p, "utf8"));
|
|
52908
53082
|
} catch {
|
|
52909
53083
|
return null;
|
|
52910
53084
|
}
|
|
@@ -52912,7 +53086,7 @@ function readJson(p) {
|
|
|
52912
53086
|
function jsonMcpServersWrite(p) {
|
|
52913
53087
|
return (command, args) => {
|
|
52914
53088
|
const dir = dirname2(p);
|
|
52915
|
-
if (!
|
|
53089
|
+
if (!existsSync7(dir))
|
|
52916
53090
|
mkdirSync4(dir, { recursive: true });
|
|
52917
53091
|
const existing = readJson(p) ?? {};
|
|
52918
53092
|
existing.mcpServers = {
|
|
@@ -52925,7 +53099,7 @@ function jsonMcpServersWrite(p) {
|
|
|
52925
53099
|
function opencodeWrite(p) {
|
|
52926
53100
|
return (_command, _args) => {
|
|
52927
53101
|
const dir = dirname2(p);
|
|
52928
|
-
if (!
|
|
53102
|
+
if (!existsSync7(dir))
|
|
52929
53103
|
mkdirSync4(dir, { recursive: true });
|
|
52930
53104
|
const existing = readJson(p) ?? {};
|
|
52931
53105
|
existing.mcp = {
|
|
@@ -52937,26 +53111,26 @@ function opencodeWrite(p) {
|
|
|
52937
53111
|
};
|
|
52938
53112
|
}
|
|
52939
53113
|
function copyOpenCodeAgents() {
|
|
52940
|
-
const srcDir =
|
|
52941
|
-
const dstDir =
|
|
52942
|
-
if (!
|
|
53114
|
+
const srcDir = join13(ROOT7, "opencode-agents");
|
|
53115
|
+
const dstDir = join13(homedir5(), ".config", "opencode", "agents");
|
|
53116
|
+
if (!existsSync7(srcDir))
|
|
52943
53117
|
return;
|
|
52944
|
-
if (!
|
|
53118
|
+
if (!existsSync7(dstDir))
|
|
52945
53119
|
mkdirSync4(dstDir, { recursive: true });
|
|
52946
53120
|
for (const f of ["Blitz Strike.md", "Blitz.md", "Eagle Eye.md", "Strike.md"]) {
|
|
52947
|
-
const src =
|
|
52948
|
-
const dst =
|
|
52949
|
-
if (
|
|
52950
|
-
writeFileSync5(dst,
|
|
53121
|
+
const src = join13(srcDir, f);
|
|
53122
|
+
const dst = join13(dstDir, f);
|
|
53123
|
+
if (existsSync7(src)) {
|
|
53124
|
+
writeFileSync5(dst, readFileSync18(src, "utf8"));
|
|
52951
53125
|
}
|
|
52952
53126
|
}
|
|
52953
53127
|
}
|
|
52954
53128
|
function codexWrite(p) {
|
|
52955
53129
|
return (command, args) => {
|
|
52956
53130
|
const dir = dirname2(p);
|
|
52957
|
-
if (!
|
|
53131
|
+
if (!existsSync7(dir))
|
|
52958
53132
|
mkdirSync4(dir, { recursive: true });
|
|
52959
|
-
let existing =
|
|
53133
|
+
let existing = existsSync7(p) ? readFileSync18(p, "utf8") : "";
|
|
52960
53134
|
if (!existing.trimEnd().endsWith(`
|
|
52961
53135
|
`))
|
|
52962
53136
|
existing += `
|
|
@@ -52973,9 +53147,9 @@ args = ${JSON.stringify(args)}
|
|
|
52973
53147
|
function hermesWrite(p) {
|
|
52974
53148
|
return (command, args) => {
|
|
52975
53149
|
const dir = dirname2(p);
|
|
52976
|
-
if (!
|
|
53150
|
+
if (!existsSync7(dir))
|
|
52977
53151
|
mkdirSync4(dir, { recursive: true });
|
|
52978
|
-
let existing =
|
|
53152
|
+
let existing = existsSync7(p) ? readFileSync18(p, "utf8") : "";
|
|
52979
53153
|
existing = existing.replace(/^ blitzstrike:\n(?: .*\n?)*/m, "");
|
|
52980
53154
|
if (!existing.trimEnd().endsWith(`
|
|
52981
53155
|
`))
|
|
@@ -53002,74 +53176,74 @@ mcp_servers:
|
|
|
53002
53176
|
function detectAgents() {
|
|
53003
53177
|
const home = homedir5();
|
|
53004
53178
|
const agents = [];
|
|
53005
|
-
const claudeJson =
|
|
53179
|
+
const claudeJson = join13(home, ".claude.json");
|
|
53006
53180
|
agents.push({
|
|
53007
53181
|
name: "Claude Code",
|
|
53008
53182
|
detectPath: claudeJson,
|
|
53009
|
-
installed: () =>
|
|
53183
|
+
installed: () => existsSync7(claudeJson) || which("claude"),
|
|
53010
53184
|
write: jsonMcpServersWrite(claudeJson)
|
|
53011
53185
|
});
|
|
53012
|
-
const claudeDesktop =
|
|
53186
|
+
const claudeDesktop = join13(home, ".config", "Claude", "claude_desktop_config.json");
|
|
53013
53187
|
agents.push({
|
|
53014
53188
|
name: "Claude Desktop",
|
|
53015
53189
|
detectPath: claudeDesktop,
|
|
53016
|
-
installed: () =>
|
|
53190
|
+
installed: () => existsSync7(claudeDesktop),
|
|
53017
53191
|
write: jsonMcpServersWrite(claudeDesktop)
|
|
53018
53192
|
});
|
|
53019
|
-
const cursor =
|
|
53193
|
+
const cursor = join13(home, ".cursor", "mcp.json");
|
|
53020
53194
|
agents.push({
|
|
53021
53195
|
name: "Cursor",
|
|
53022
53196
|
detectPath: cursor,
|
|
53023
|
-
installed: () =>
|
|
53197
|
+
installed: () => existsSync7(join13(home, ".cursor")) || which("cursor"),
|
|
53024
53198
|
write: jsonMcpServersWrite(cursor)
|
|
53025
53199
|
});
|
|
53026
|
-
const opencode =
|
|
53200
|
+
const opencode = join13(home, ".config", "opencode", "opencode.jsonc");
|
|
53027
53201
|
agents.push({
|
|
53028
53202
|
name: "OpenCode",
|
|
53029
53203
|
detectPath: opencode,
|
|
53030
|
-
installed: () =>
|
|
53204
|
+
installed: () => existsSync7(join13(home, ".config", "opencode")) || which("opencode"),
|
|
53031
53205
|
write: opencodeWrite(opencode)
|
|
53032
53206
|
});
|
|
53033
|
-
const codex =
|
|
53207
|
+
const codex = join13(home, ".codex", "config.toml");
|
|
53034
53208
|
agents.push({
|
|
53035
53209
|
name: "Codex",
|
|
53036
53210
|
detectPath: codex,
|
|
53037
|
-
installed: () =>
|
|
53211
|
+
installed: () => existsSync7(join13(home, ".codex")) || which("codex"),
|
|
53038
53212
|
write: codexWrite(codex)
|
|
53039
53213
|
});
|
|
53040
|
-
const hermes =
|
|
53214
|
+
const hermes = join13(home, ".hermes", "config.yaml");
|
|
53041
53215
|
agents.push({
|
|
53042
53216
|
name: "Hermes",
|
|
53043
53217
|
detectPath: hermes,
|
|
53044
|
-
installed: () =>
|
|
53218
|
+
installed: () => existsSync7(join13(home, ".hermes")) || which("hermes"),
|
|
53045
53219
|
write: hermesWrite(hermes)
|
|
53046
53220
|
});
|
|
53047
|
-
const gemini =
|
|
53221
|
+
const gemini = join13(home, ".gemini", "settings.json");
|
|
53048
53222
|
agents.push({
|
|
53049
53223
|
name: "Gemini CLI",
|
|
53050
53224
|
detectPath: gemini,
|
|
53051
|
-
installed: () =>
|
|
53225
|
+
installed: () => existsSync7(join13(home, ".gemini")) || which("gemini"),
|
|
53052
53226
|
write: jsonMcpServersWrite(gemini)
|
|
53053
53227
|
});
|
|
53054
|
-
const windsurf =
|
|
53228
|
+
const windsurf = join13(home, ".codeium", "windsurf", "mcp_config.json");
|
|
53055
53229
|
agents.push({
|
|
53056
53230
|
name: "Windsurf",
|
|
53057
53231
|
detectPath: windsurf,
|
|
53058
|
-
installed: () =>
|
|
53232
|
+
installed: () => existsSync7(join13(home, ".codeium")) || which("windsurf"),
|
|
53059
53233
|
write: jsonMcpServersWrite(windsurf)
|
|
53060
53234
|
});
|
|
53061
|
-
const copilot =
|
|
53235
|
+
const copilot = join13(home, ".copilot", "mcp.json");
|
|
53062
53236
|
agents.push({
|
|
53063
53237
|
name: "Copilot",
|
|
53064
53238
|
detectPath: copilot,
|
|
53065
|
-
installed: () =>
|
|
53239
|
+
installed: () => existsSync7(join13(home, ".copilot")),
|
|
53066
53240
|
write: jsonMcpServersWrite(copilot)
|
|
53067
53241
|
});
|
|
53068
|
-
const cline =
|
|
53242
|
+
const cline = join13(home, ".cline", "mcp_settings.json");
|
|
53069
53243
|
agents.push({
|
|
53070
53244
|
name: "Cline",
|
|
53071
53245
|
detectPath: cline,
|
|
53072
|
-
installed: () =>
|
|
53246
|
+
installed: () => existsSync7(join13(home, ".cline")),
|
|
53073
53247
|
write: jsonMcpServersWrite(cline)
|
|
53074
53248
|
});
|
|
53075
53249
|
return agents;
|
|
@@ -53113,15 +53287,15 @@ Registered with ${ok}/${installed.length} agent(s).`);
|
|
|
53113
53287
|
}
|
|
53114
53288
|
|
|
53115
53289
|
// src/index.ts
|
|
53116
|
-
import { readFileSync as
|
|
53117
|
-
import { join as
|
|
53118
|
-
import { fileURLToPath as
|
|
53119
|
-
var
|
|
53290
|
+
import { readFileSync as readFileSync19, existsSync as existsSync8 } from "node:fs";
|
|
53291
|
+
import { join as join14 } from "node:path";
|
|
53292
|
+
import { fileURLToPath as fileURLToPath10 } from "node:url";
|
|
53293
|
+
var ROOT8 = join14(fileURLToPath10(new URL(".", import.meta.url)), "..");
|
|
53120
53294
|
function readVersion() {
|
|
53121
53295
|
try {
|
|
53122
|
-
const p =
|
|
53123
|
-
if (
|
|
53124
|
-
return JSON.parse(
|
|
53296
|
+
const p = join14(ROOT8, "package.json");
|
|
53297
|
+
if (existsSync8(p))
|
|
53298
|
+
return JSON.parse(readFileSync19(p, "utf8")).version ?? "1.0.0";
|
|
53125
53299
|
} catch {}
|
|
53126
53300
|
return "1.0.0";
|
|
53127
53301
|
}
|