blitzstrike 2.4.70 → 2.4.71

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 CHANGED
@@ -5,6 +5,26 @@ All notable changes to Blitz Strike are documented here.
5
5
  The format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/).
7
7
 
8
+ ## [2.4.71] — 2026-09-14
9
+
10
+ ### Added — Rust language support (adapter + detectors)
11
+
12
+ - Rust is now the 5th taint-engine language (php/javascript/python/java/rust): a new
13
+ regex/line-oriented adapter maps Rust sources (env::args, stdin, axum/actix
14
+ Query/Form/Json/Path extractors, env::var) and sinks (std::process::Command,
15
+ sqlx/diesel/rusqlite, reqwest/hyper, std::fs, serde/bincode) onto the existing
16
+ sink classes. The sqlx `query!`/`query_as!` compile-time macro + `canonicalize()`
17
+ are treated as safe (suppressed), so runtime `sqlx::query(&format!(...))` is the
18
+ signal that fires.
19
+ - Two Rust-specific detectors: `rust_unsafe` (transmute / assume_init /
20
+ from_raw_parts / ptr::* — memory-unsafety surface, CWE-119) and
21
+ `rust_format_string` (println!/format! with a VARIABLE as the format string —
22
+ CWE-134, distinct from the safe `println!("{}", v)`).
23
+ - Full pipeline wired: CWE map (66), patterns (24, grounded in RustSec / OWASP
24
+ WSTG-INPV-13 / Rust Foundation "Unsafe Rust in the Wild" / rust-security-handbook),
25
+ variants (78), synonyms, severity VRT, coverage matrix (5 languages), and the
26
+ verification harness (23/23 detectors TP+TN).
27
+
8
28
  ## [2.4.70] — 2026-09-14
9
29
 
10
30
  ### Added — Per-finding per-scope reports (HackerOne-grade)
package/dist/index.js CHANGED
@@ -16265,6 +16265,8 @@ function detectComplexBugs(code, file) {
16265
16265
  detectGrpcReflection(code, file, out);
16266
16266
  detectDependencyConfusion(code, file, out);
16267
16267
  detectMlSupplyChain(code, file, out);
16268
+ detectRustUnsafe(code, file, out);
16269
+ detectRustFormatString(code, file, out);
16268
16270
  out.push(...detectWrongSanitizer(code, file));
16269
16271
  out.push(...detectMissingAuthz(code, file));
16270
16272
  const seen = new Set;
@@ -16753,6 +16755,46 @@ function detectMlSupplyChain(code, file, out) {
16753
16755
  }
16754
16756
  }
16755
16757
  }
16758
+ function detectRustUnsafe(code, file, out) {
16759
+ if (!/\.rs$/i.test(file))
16760
+ return;
16761
+ const ls = lines2(code);
16762
+ const clean = stripCommentsOnly(code);
16763
+ const re = /std::mem::transmute|\btransmute\s*\(|\bassume_init\s*\(|from_raw_parts(?:_mut)?\s*\(|ptr::(copy|copy_nonoverlapping|read|write|swap)\s*\(|from_utf8_unchecked\s*\(|\bunreachable_unchecked\s*\(/g;
16764
+ let m;
16765
+ while ((m = re.exec(clean)) !== null) {
16766
+ const ln = lineNo2(code, m.index);
16767
+ out.push({
16768
+ file,
16769
+ line: ln,
16770
+ type: "rust_unsafe",
16771
+ category: "Rust unsafe / memory-unsafety surface",
16772
+ severity: "medium",
16773
+ evidence: (ls[ln - 1] ?? "").trim(),
16774
+ detail: `\`${m[0]}\` bypasses the borrow checker's memory-safety guarantees — type confusion (transmute), uninitialized reads (assume_init), or unchecked pointer/slice construction (from_raw_parts / ptr::*) can lead to out-of-bounds access, use-after-free, or UB (CWE-119/787/416). Verify the input is bounds-checked and no attacker-controlled length/index reaches it.`
16775
+ });
16776
+ }
16777
+ }
16778
+ function detectRustFormatString(code, file, out) {
16779
+ if (!/\.rs$/i.test(file))
16780
+ return;
16781
+ const ls = lines2(code);
16782
+ const clean = stripCommentsOnly(code);
16783
+ const re = /\b(format!|println!|print!|eprintln!|eprint!)\s*\(\s*([a-z_][a-z0-9_]*)\s*\)/g;
16784
+ let m;
16785
+ while ((m = re.exec(clean)) !== null) {
16786
+ const ln = lineNo2(code, m.index);
16787
+ out.push({
16788
+ file,
16789
+ line: ln,
16790
+ type: "rust_format_string",
16791
+ category: "Rust format string injection",
16792
+ severity: "high",
16793
+ evidence: (ls[ln - 1] ?? "").trim(),
16794
+ detail: `\`${m[1]}\` passes a variable (\`${m[2]}\`) as the FORMAT string instead of a literal — if attacker-controlled, format specifiers (positional args, width, precision) can read or corrupt memory (CWE-134). Use \`${m[1]}!("{}", value)\` with the input as an argument, never as the format string.`
16795
+ });
16796
+ }
16797
+ }
16756
16798
  var SOURCE_TOKENS3, MAGIC_METHODS, SECRET_FIELD_RE, SECRET_PLACEHOLDER_RE, ENTROPY_EXCLUDE_VAR;
16757
16799
  var init_complex_bugs = __esm(() => {
16758
16800
  init_security_state();
@@ -16948,6 +16990,20 @@ $r = openai.chat.completions.create(['messages' => [['role' => 'user', 'content'
16948
16990
  file: "train.py",
16949
16991
  positive: `import torch\\nmodel = torch.load(downloaded_model)\\n`,
16950
16992
  negative: `import torch\\nmodel = torch.load("model.pt", weights_only=True)\\n`
16993
+ },
16994
+ {
16995
+ id: "rust-unsafe",
16996
+ detector: "rust_unsafe",
16997
+ file: "lib.rs",
16998
+ positive: `fn f() { let x: &[u8] = std::mem::transmute(user_data); }\\n`,
16999
+ negative: `fn f() { let x: &[u8] = user_data.as_bytes(); }\\n`
17000
+ },
17001
+ {
17002
+ id: "rust-format-string",
17003
+ detector: "rust_format_string",
17004
+ file: "main.rs",
17005
+ positive: `fn f() { println!(user_input); }\\n`,
17006
+ negative: `fn f() { println!("{}", user_input); }\\n`
16951
17007
  }
16952
17008
  ];
16953
17009
  });
@@ -56680,10 +56736,97 @@ var javaAdapter = {
56680
56736
  return { sources, sinks, sanitizers, calls, assigns };
56681
56737
  }
56682
56738
  };
56739
+ var RUST_SOURCES = [
56740
+ [/env::args\s*\(|args_os\s*\(/, "cli_argument"],
56741
+ [/stdin\s*\(\s*\)|read_line\s*\(/, "raw_body"],
56742
+ [/\b(?:Query|Form|Json|Path)\b|web::(?:Query|Json|Form|Path)|req\.(?:query|params|param|form|query_string)/, "http_parameter"],
56743
+ [/env::var\s*\(|env::var_os\s*\(/, "env_variable"]
56744
+ ];
56745
+ var RUST_SINKS = [
56746
+ [/Command::new|process::Command/, "command_execution", "CWE-78"],
56747
+ [/sqlx::query(?:_as)?\s*\(|sqlx::raw_sql|\.query\s*\(|\.execute\s*\(|rusqlite|diesel::/, "sql_execution", "CWE-89"],
56748
+ [/reqwest::get|reqwest::Client|hyper::|\.get\s*\(|\.post\s*\(|\.put\s*\(|\.request\s*\(/, "http_request", "CWE-918"],
56749
+ [/std::fs::(read_to_string|read|write|remove_file|copy|rename)|File::open\s*\(|fs::(read|write|remove_file|copy)\s*\(/, "path_traversal", "CWE-22"],
56750
+ [/serde_json::from_(str|slice)|bincode::deserialize|serde_yaml::from_str|toml::from_str|\.deserialize\s*\(/, "deserialization", "CWE-502"]
56751
+ ];
56752
+ var RUST_SANITIZERS = [
56753
+ [/canonicalize\s*\(/, ["path_traversal"]],
56754
+ [/query(_as)?!\s*\(/, ["sql_execution"]]
56755
+ ];
56756
+ var rustAdapter = {
56757
+ language: "rust",
56758
+ extensions: [".rs"],
56759
+ parse(code, _file) {
56760
+ const sources = [];
56761
+ const sinks = [];
56762
+ const sanitizers = [];
56763
+ const calls = [];
56764
+ const assigns = [];
56765
+ const assignRe = /let\s+(?:mut\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.+?);/g;
56766
+ let m;
56767
+ while ((m = assignRe.exec(code)) !== null) {
56768
+ const varName = m[1];
56769
+ const rhs = m[2];
56770
+ const line = code.slice(0, m.index).split(`
56771
+ `).length;
56772
+ const src = RUST_SOURCES.find(([re]) => re.test(rhs));
56773
+ if (src)
56774
+ sources.push({ variable: varName, kind: src[1], line, attacker_controlled: src[1] !== "cli_argument" && src[1] !== "env_variable" });
56775
+ const rhsVars = [];
56776
+ const vre = /([a-z_][a-z0-9_]*)/g;
56777
+ let vm;
56778
+ while ((vm = vre.exec(rhs)) !== null) {
56779
+ const v = vm[1];
56780
+ if (!RUST_SOURCES.some(([re]) => re.test(v)))
56781
+ rhsVars.push(v);
56782
+ }
56783
+ assigns.push({ target: varName, sources: rhsVars.slice(0, 8), line });
56784
+ }
56785
+ const extractorRe = /\b(?:Query|Form|Json|Path)\s*\(\s*([a-z_][a-z0-9_]*)\s*\)/g;
56786
+ while ((m = extractorRe.exec(code)) !== null) {
56787
+ const line = code.slice(0, m.index).split(`
56788
+ `).length;
56789
+ sources.push({ variable: m[1], kind: "http_parameter", line, attacker_controlled: true });
56790
+ assigns.push({ target: m[1], sources: [], line });
56791
+ }
56792
+ const lines = code.split(`
56793
+ `);
56794
+ for (let i = 0;i < lines.length; i++) {
56795
+ const line = lines[i];
56796
+ for (const [re, kind, cwe] of RUST_SINKS) {
56797
+ if (!re.test(line))
56798
+ continue;
56799
+ if (kind === "sql_execution" && /query(_as)?!\s*\(/.test(line))
56800
+ continue;
56801
+ const argVars = [];
56802
+ const vre = /([a-z_][a-z0-9_]*)/g;
56803
+ let vm;
56804
+ while ((vm = vre.exec(line)) !== null) {
56805
+ const id = vm[1];
56806
+ if (/^(new|let|mut|arg|args|command|process|std|sqlx|query|query_as|execute|reqwest|client|get|post|put|request|fs|file|open|read_to_string|read|write|remove_file|copy|rename|serde_json|from_str|from_slice|bincode|deserialize|toml|yaml|format|unwrap|expect|env|args|stdin|read_line)$/.test(id))
56807
+ continue;
56808
+ argVars.push(id);
56809
+ }
56810
+ for (const v of argVars.slice(0, 8))
56811
+ sinks.push({ variable: v, kind, line: i + 1, cwe });
56812
+ }
56813
+ for (const [re, kinds] of RUST_SANITIZERS) {
56814
+ if (re.test(line)) {
56815
+ const vre = /([a-z_][a-z0-9_]*)/g;
56816
+ let vm;
56817
+ while ((vm = vre.exec(line)) !== null)
56818
+ sanitizers.push({ variable: vm[1], neutralizes: kinds, line: i + 1 });
56819
+ }
56820
+ }
56821
+ }
56822
+ return { sources, sinks, sanitizers, calls, assigns };
56823
+ }
56824
+ };
56683
56825
  registerLanguage(phpAdapter);
56684
56826
  registerLanguage(jsAdapter);
56685
56827
  registerLanguage(pythonAdapter);
56686
56828
  registerLanguage(javaAdapter);
56829
+ registerLanguage(rustAdapter);
56687
56830
 
56688
56831
  // src/incremental.ts
56689
56832
  var SOURCE_EXTS = [".php", ".phtml", ".js", ".mjs", ".ts", ".tsx", ".py", ".pyw", ".java"];
@@ -58011,7 +58154,14 @@ var ALIASES = {
58011
58154
  "ai supply chain": "ml_supply_chain",
58012
58155
  "model poisoning": "ml_supply_chain",
58013
58156
  "ml security": "ml_supply_chain",
58014
- "ai ml security": "ml_supply_chain"
58157
+ "ai ml security": "ml_supply_chain",
58158
+ "rust unsafe": "rust_unsafe",
58159
+ "unsafe rust": "rust_unsafe",
58160
+ "rust memory safety": "rust_unsafe",
58161
+ transmute: "rust_unsafe",
58162
+ "rust format string": "rust_format_string",
58163
+ "format string": "rust_format_string",
58164
+ "format string injection": "rust_format_string"
58015
58165
  };
58016
58166
  function normalize(term) {
58017
58167
  return term.trim().toLowerCase().replace(/[_-]+/g, " ").replace(/\s+/g, " ").trim();
@@ -58302,7 +58452,9 @@ var VRT_MAP = {
58302
58452
  oauth_misconfig: { vrt: "authentication > oauth_misconfiguration", priority: "P2", severity: "high", rationale: "An OAuth/open-redirect misconfiguration can leak authorization codes → account takeover." },
58303
58453
  grpc_reflection: { vrt: "misconfiguration > debug_information_exposure", priority: "P5", severity: "low", rationale: "gRPC reflection exposes the API contract but is not directly exploitable." },
58304
58454
  dependency_confusion: { vrt: "insecure_design > dependency_confusion", priority: "P2", severity: "high", rationale: "An internal package resolves from a squattable public registry → attacker-controlled code in the build." },
58305
- ml_supply_chain: { vrt: "injection > insecure_deserialization", priority: "P1", severity: "critical", rationale: "Untrusted model loading via pickle (torch/joblib/numpy) → arbitrary code execution on load." }
58455
+ ml_supply_chain: { vrt: "injection > insecure_deserialization", priority: "P1", severity: "critical", rationale: "Untrusted model loading via pickle (torch/joblib/numpy) → arbitrary code execution on load." },
58456
+ rust_unsafe: { vrt: "insecure_design > unsafe_memory_operations", priority: "P3", severity: "medium", rationale: "transmute/from_raw_parts/assume_init bypass the borrow checker — a hint that needs bounds verification; memory corruption if attacker-controlled." },
58457
+ rust_format_string: { vrt: "injection > format_string_injection", priority: "P2", severity: "high", rationale: "An attacker-controlled format string can read or corrupt memory via format specifiers." }
58306
58458
  };
58307
58459
  function severityCalibrate(input) {
58308
58460
  const type = input.type?.toLowerCase() ?? "";
@@ -59758,11 +59910,29 @@ var JAVA_SINKS2 = {
59758
59910
  xpath_injection: true,
59759
59911
  ldap_injection: true
59760
59912
  };
59913
+ var RUST_SINKS2 = {
59914
+ sql_execution: true,
59915
+ code_execution: false,
59916
+ command_execution: true,
59917
+ file_operations: true,
59918
+ file_inclusion: false,
59919
+ deserialization: true,
59920
+ http_request: true,
59921
+ redirect: true,
59922
+ html_render: false,
59923
+ xml_processing: false,
59924
+ archive_extraction: false,
59925
+ path_traversal: true,
59926
+ template_injection: false,
59927
+ xpath_injection: false,
59928
+ ldap_injection: false
59929
+ };
59761
59930
  var SINK_BY_LANG = {
59762
59931
  php: PHP_SINKS2,
59763
59932
  javascript: JS_SINKS2,
59764
59933
  python: PY_SINKS2,
59765
- java: JAVA_SINKS2
59934
+ java: JAVA_SINKS2,
59935
+ rust: RUST_SINKS2
59766
59936
  };
59767
59937
  function coverageMatrix() {
59768
59938
  const langs = listLanguages();
@@ -59822,7 +59992,7 @@ function createServer() {
59822
59992
  ` + `- A hard block (Cloudflare/WAF/IP-block/captcha) is NOT the end: mark the finding blocked + note the required vantage (residential / authorized / correct-SNI / --resolve direct-to-origin), then CONTINUE to the next lead (origin bypass, non-prod cluster, mobile APK, subdomain).
59823
59993
  ` + `- Keep trying every lead AND every chain step until each is verified/executed or blocked, THEN generate_report. Never deliver a bare surface map, and never stop at 'found X' when X enables more.
59824
59994
 
59825
- ` + "TWO PATHS:\\n" + "- Source path on disk -> blitz_scan / taint_file / eagle_eye2 (static).\\n" + "- Live URL -> live_recon / active_scan / strike_verify (web-hunting).\\n\\n" + "DEEPEN A FINDING: attack_plan(target, tech, params) to get the prioritized vector plan (decide what to test next with data); list_chains (escalation), chain_links (compose), technique_lookup(cls), payload_lookup, bypass_lookup (blocked). Ground any detector hit empirically with pattern_lookup(id) — it returns the class's CWE, real-world signature, escalation chains, citable references, per-class bypass techniques, and the high-value assets (crown jewels) that class hits, so a raw hit becomes a class-aware finding. Resolve free-form terminology first with synonym_lookup(term) (IDOR/BOLA/SQLi/prompt-injection → canonical id). For an llm_injection hit, its pattern carries a validation discipline — the RUN-TWICE RULE (reproduce token-for-token across two fresh sessions), NON-GUESSABLE ANCHOR (must leak a real key/URL, not a plausible guess), and OOB PROOF (a verifiable callback log) — apply all three before reporting. BEFORE generating a report, run submission_gate(...) and only ship a finding that passes all 7 questions (reproducible, in-scope, real-impact, no-privileged-assumption, verified-live, evidence-redacted, severity-derived); one false = kill it. Calibrate severity with severity_calibrate(type, cvss) to get a P1–P5 tier + VRT-style category. Start each engagement with engagement_scaffold(target, scope) for the folder + scope.md structure, and check data_refresh() to confirm the data layers are consistent. Phase doctrine: read_skill(bs-orchestrate-engagement | bs-web-hunting | bs-source-audit | bs-verify-finding). Campaign plan: read_playbook(target-type). On any failure call retry_guidance(signal) / model_fallback(signal) before giving up.\n" + "KNOWLEDGE BASE (discover, don't guess): skillLookup(topic) / listSkills for the 40 attack-path + hunting skills (ap-*, hp-*, bs-*); list_attack_vectors / attack_vectors(cat) for the 34-category vector taxonomy; taxonomy(kind) for OWASP/CWE/ASVS/API-top-10; read_playbook(type) for the 17 campaign playbooks; list_manuals / read_tool_manual for 317 tool references; list_tools / run_catalog_tool for the 141-tool catalog. Every tool, skill, and data layer is reachable from these. CROSS-TARGET INTELLIGENCE: strike_resolve auto-records confirmed/false_positive verdicts into the empirical ledger; query it with intelligence(action=hit_rates/top_vectors/recall) so a new target on a known tech inherits historical hit-rates (attack_plan already blends them into success_probability). intelligence(action=recall, tech, target) is the per-engagement pattern recall — it returns which patterns historically CONFIRMED on that tech (prioritize them) versus which only ever produced false positives (avoid them), so a new engagement starts from the empirical playbook of past ones. LIVE PENTEST + TARGETING (black-box, WAF-safe): cache_gap(base_url, endpoint) fuzzes path-normalization + static-extension variants for cache deception/poisoning; blind_oracle(base_url, param, value) detects which SINK a param reaches (sql/ssti/xss) via benign arithmetic (id=1+0, test${7*7}) WITHOUT a malicious payload; param_precedence(base_url, param, A, B) maps duplicate-parameter precedence (first/last-wins) for WAF-bypass. SOURCE TARGETING: action_surface(code) maps every wp_ajax_* action to its vuln class + DERIVED priority (what to look for WHERE); security_state(sanitizers, sink) gives a wrong-context sanitization verdict; framework_knowledge(framework) returns auth/nonce primitives for 9 frameworks; variant_scan(root, signature) mass-sweeps an install base for ONE bug family; patch_analyze(old, new) REVERSES a security patch into the 0-day, and its mine_signature feeds variant_scan to sweep unpatched copies. All of these are DETECTION HINTS — verify each live (strike_verify / browser_validate) before it enters the report. NEWEST DETECTORS — understand AND try them: variant_scan also emits priv_esc (privilege escalation — a role/capability/user change fed by request input, traced cross-file via reverse call-graph, e.g. set_role($id, sanitize_key($_POST['um-role'])) or update_user_meta(...,'wp_capabilities', $_POST[...])) and flags uploadAllow=>array('all') unrestricted-upload configs as file_upload (the file-manager/connector shape). TO TRY a priv_esc hit: submit the role field with 'administrator' (or send wp_capabilities meta) and confirm the account actually gains admin. TO TRY an unrestricted-upload hit: POST a .php/.phtml and confirm it lands web-reachable + executes. variant_scan also emits hardcoded_secret (CWE-798) in three tiers: a secret field (api_key/db_password/access_token/aws_*) assigned a literal (HIGH), a known secret FORMAT (JWT eyJ…/AWS AKIA…/GitHub ghp_…/Slack xoxb-…/…) regardless of name (HIGH), or a high-entropy suspect token (MEDIUM — verify before reporting). Verify a frontend/JS secret live via drive_devtools (inspect the JS bundle / network response and confirm the key is actually exposed), and for a backend secret confirm it is reachable/valid. A static hit is a HYPOTHESIS until verified live — never report unverified. For a repo, also scan_git_history(root) — secrets DELETED from HEAD still live in .git history (the removed-later-is-not-fixed trap), and it feeds every recovered pre-deletion blob through the same secret detector. Once you recover a credential, prove it is still ACTIVE with validate_leaked_key(apiBase, endpoints, key|blobUrl) — a READ-ONLY differential test (401-without-key vs 200-with-key = auth bypass PROVEN). Two supply-chain detectors: dependency_confusion (a manifest/build file resolving a package from a public registry — pypi.org/npmjs.org --extra-index-url, or a scoped @company/pkg with no private registry) and ml_supply_chain (torch.load without weights_only=True / joblib.load / np.load(allow_pickle=True) / keras load_model on an untrusted model → pickle RCE).\n" + `PRECISION ANALYSIS — chrome-devtools MCP is MANDATORY, not optional:
59995
+ ` + "TWO PATHS:\\n" + "- Source path on disk -> blitz_scan / taint_file / eagle_eye2 (static).\\n" + "- Live URL -> live_recon / active_scan / strike_verify (web-hunting).\\n\\n" + "DEEPEN A FINDING: attack_plan(target, tech, params) to get the prioritized vector plan (decide what to test next with data); list_chains (escalation), chain_links (compose), technique_lookup(cls), payload_lookup, bypass_lookup (blocked). Ground any detector hit empirically with pattern_lookup(id) — it returns the class's CWE, real-world signature, escalation chains, citable references, per-class bypass techniques, and the high-value assets (crown jewels) that class hits, so a raw hit becomes a class-aware finding. Resolve free-form terminology first with synonym_lookup(term) (IDOR/BOLA/SQLi/prompt-injection → canonical id). For an llm_injection hit, its pattern carries a validation discipline — the RUN-TWICE RULE (reproduce token-for-token across two fresh sessions), NON-GUESSABLE ANCHOR (must leak a real key/URL, not a plausible guess), and OOB PROOF (a verifiable callback log) — apply all three before reporting. BEFORE generating a report, run submission_gate(...) and only ship a finding that passes all 7 questions (reproducible, in-scope, real-impact, no-privileged-assumption, verified-live, evidence-redacted, severity-derived); one false = kill it. Calibrate severity with severity_calibrate(type, cvss) to get a P1–P5 tier + VRT-style category. Start each engagement with engagement_scaffold(target, scope) for the folder + scope.md structure, and check data_refresh() to confirm the data layers are consistent. Phase doctrine: read_skill(bs-orchestrate-engagement | bs-web-hunting | bs-source-audit | bs-verify-finding). Campaign plan: read_playbook(target-type). On any failure call retry_guidance(signal) / model_fallback(signal) before giving up.\n" + "KNOWLEDGE BASE (discover, don't guess): skillLookup(topic) / listSkills for the 40 attack-path + hunting skills (ap-*, hp-*, bs-*); list_attack_vectors / attack_vectors(cat) for the 34-category vector taxonomy; taxonomy(kind) for OWASP/CWE/ASVS/API-top-10; read_playbook(type) for the 17 campaign playbooks; list_manuals / read_tool_manual for 317 tool references; list_tools / run_catalog_tool for the 141-tool catalog. Every tool, skill, and data layer is reachable from these. CROSS-TARGET INTELLIGENCE: strike_resolve auto-records confirmed/false_positive verdicts into the empirical ledger; query it with intelligence(action=hit_rates/top_vectors/recall) so a new target on a known tech inherits historical hit-rates (attack_plan already blends them into success_probability). intelligence(action=recall, tech, target) is the per-engagement pattern recall — it returns which patterns historically CONFIRMED on that tech (prioritize them) versus which only ever produced false positives (avoid them), so a new engagement starts from the empirical playbook of past ones. LIVE PENTEST + TARGETING (black-box, WAF-safe): cache_gap(base_url, endpoint) fuzzes path-normalization + static-extension variants for cache deception/poisoning; blind_oracle(base_url, param, value) detects which SINK a param reaches (sql/ssti/xss) via benign arithmetic (id=1+0, test${7*7}) WITHOUT a malicious payload; param_precedence(base_url, param, A, B) maps duplicate-parameter precedence (first/last-wins) for WAF-bypass. SOURCE TARGETING: action_surface(code) maps every wp_ajax_* action to its vuln class + DERIVED priority (what to look for WHERE); security_state(sanitizers, sink) gives a wrong-context sanitization verdict; framework_knowledge(framework) returns auth/nonce primitives for 9 frameworks; variant_scan(root, signature) mass-sweeps an install base for ONE bug family; patch_analyze(old, new) REVERSES a security patch into the 0-day, and its mine_signature feeds variant_scan to sweep unpatched copies. All of these are DETECTION HINTS — verify each live (strike_verify / browser_validate) before it enters the report. NEWEST DETECTORS — understand AND try them: variant_scan also emits priv_esc (privilege escalation — a role/capability/user change fed by request input, traced cross-file via reverse call-graph, e.g. set_role($id, sanitize_key($_POST['um-role'])) or update_user_meta(...,'wp_capabilities', $_POST[...])) and flags uploadAllow=>array('all') unrestricted-upload configs as file_upload (the file-manager/connector shape). TO TRY a priv_esc hit: submit the role field with 'administrator' (or send wp_capabilities meta) and confirm the account actually gains admin. TO TRY an unrestricted-upload hit: POST a .php/.phtml and confirm it lands web-reachable + executes. variant_scan also emits hardcoded_secret (CWE-798) in three tiers: a secret field (api_key/db_password/access_token/aws_*) assigned a literal (HIGH), a known secret FORMAT (JWT eyJ…/AWS AKIA…/GitHub ghp_…/Slack xoxb-…/…) regardless of name (HIGH), or a high-entropy suspect token (MEDIUM — verify before reporting). Verify a frontend/JS secret live via drive_devtools (inspect the JS bundle / network response and confirm the key is actually exposed), and for a backend secret confirm it is reachable/valid. A static hit is a HYPOTHESIS until verified live — never report unverified. For a repo, also scan_git_history(root) — secrets DELETED from HEAD still live in .git history (the removed-later-is-not-fixed trap), and it feeds every recovered pre-deletion blob through the same secret detector. Once you recover a credential, prove it is still ACTIVE with validate_leaked_key(apiBase, endpoints, key|blobUrl) — a READ-ONLY differential test (401-without-key vs 200-with-key = auth bypass PROVEN). Two supply-chain detectors: dependency_confusion (a manifest/build file resolving a package from a public registry — pypi.org/npmjs.org --extra-index-url, or a scoped @company/pkg with no private registry) and ml_supply_chain (torch.load without weights_only=True / joblib.load / np.load(allow_pickle=True) / keras load_model on an untrusted model → pickle RCE). Rust is supported end-to-end: the taint engine (blitz_scan / taint_file) maps Rust sinks (std::process::Command, sqlx/diesel, reqwest/hyper, std::fs, serde/bincode) to the classic classes, plus two Rust-specific detectors — rust_unsafe (transmute / assume_init / from_raw_parts / ptr::* — memory-unsafety surface, CWE-119) and rust_format_string (println!/format! with a variable as the format string — CWE-134). The sqlx query! compile-time macro is treated as safe (suppressed).\n" + `PRECISION ANALYSIS — chrome-devtools MCP is MANDATORY, not optional:
59826
59996
  ` + `- When a lead needs DOM/JS precision — DOM-XSS sink execution, AJAX endpoint + payload interception, JS runtime errors, redirect chains, SSO/token flow, or anything a static HTML glance cannot prove — call drive_devtools(target). It spawns chrome-devtools-mcp and drives a REAL headless browser (navigate -> list_network_requests -> list_console_messages -> evaluate_script), giving reproducible evidence instead of a guess.
59827
59997
  ` + `- Before calling it, check_mcp() to confirm the browser MCP is available; if not, blitzstrike install-tools or npm i -g chrome-devtools-mcp. Never substitute eyeballing minified JS or a static sink guess when a live browser can PROVE it. This is the precision + consistency layer.
59828
59998
  ` + "EXTERNAL MCP SERVERS (verified, connect them — NOT one-shot CLIs): chrome-devtools-mcp (29 tools: list_network_requests/get_network_request for raw HTTP interception, list_console_messages for JS errors + DOM-XSS, evaluate_script to run JS, take_screenshot) -> stdio chrome-devtools-mcp --headless --chromeArg=--no-sandbox --chromeArg=--disable-gpu (as root/container this is MANDATORY or the browser crashes; add --proxyServer http://127.0.0.1:8080 to route through Burp, --acceptInsecureCerts for bad TLS). burp-suite-mcp (proxy history + active scan + repeater) -> NOT npm: build the JAR (git clone https://github.com/PortSwigger/mcp-server.git && cd mcp-server && ./gradlew embedProxyJar), load it in Burp Extensions, then connect the client to http://127.0.0.1:9876 (SSE) or the stdio proxy java -jar mcp-proxy-all.jar --sse-url http://127.0.0.1:9876."
@@ -320,6 +320,16 @@
320
320
  "id": "ml_supply_chain",
321
321
  "cwe": "CWE-502",
322
322
  "name": "Deserialization of Untrusted Data"
323
+ },
324
+ {
325
+ "id": "rust_unsafe",
326
+ "cwe": "CWE-119",
327
+ "name": "Improper Restriction of Operations within the Bounds of a Memory Buffer"
328
+ },
329
+ {
330
+ "id": "rust_format_string",
331
+ "cwe": "CWE-134",
332
+ "name": "Use of Externally-Controlled Format String"
323
333
  }
324
334
  ]
325
335
  }
@@ -634,6 +634,63 @@
634
634
  "training data / weights",
635
635
  "the inference API surface"
636
636
  ]
637
+ },
638
+ {
639
+ "id": "rust_unsafe",
640
+ "cwe": "CWE-119",
641
+ "real_world": "A Rust library uses std::mem::transmute to reinterpret an attacker-influenced value as a reference, or builds a slice via from_raw_parts with a caller-supplied length, producing out-of-bounds access / use-after-free inside an unsafe block.",
642
+ "detection_signatures": [
643
+ "std::mem::transmute(...)",
644
+ "MaybeUninit::assume_init()",
645
+ "from_raw_parts(ptr, len)",
646
+ "ptr::copy / ptr::copy_nonoverlapping / ptr::read / ptr::write",
647
+ "from_utf8_unchecked / unreachable_unchecked"
648
+ ],
649
+ "chain_templates": [],
650
+ "references": [
651
+ "CWE-119",
652
+ "CWE-787",
653
+ "CWE-416",
654
+ "Rust Foundation — Unsafe Rust in the Wild",
655
+ "guardsarm/rust-memory-safety-examples",
656
+ "doc.rust-lang.org/rustc/exploit-mitigations.html"
657
+ ],
658
+ "bypass_techniques": [
659
+ "transmute to a larger/smaller layout to defeat the borrow checker",
660
+ "from_raw_parts with an attacker-controlled length",
661
+ "unsafe + raw pointer arithmetic past the bounds",
662
+ "FFI (extern C) without validating C-side lengths"
663
+ ],
664
+ "crown_jewels": [
665
+ "any host RCE / memory corruption",
666
+ "the unsafe block's invariants (what must hold for soundness)",
667
+ "FFI boundaries (the highest-yield audit surface)"
668
+ ]
669
+ },
670
+ {
671
+ "id": "rust_format_string",
672
+ "cwe": "CWE-134",
673
+ "real_world": "A server logs or prints a user-supplied string as the format argument — println!(user_input) — so the attacker's format specifiers (positional args, width/precision) read or corrupt memory.",
674
+ "detection_signatures": [
675
+ "format!(var) / println!(var) / print!(var) / eprintln!(var) with a VARIABLE as the format string (not a literal)"
676
+ ],
677
+ "chain_templates": [],
678
+ "references": [
679
+ "CWE-134",
680
+ "OWASP WSTG-INPV-13 Format String Injection",
681
+ "yevh/rust-security-handbook (injection attack prevention)",
682
+ "StackHawk — Rust command injection examples"
683
+ ],
684
+ "bypass_techniques": [
685
+ "width/precision specifiers to read past the intended argument",
686
+ "positional args ({0} {1} ...) to reach unintended arguments",
687
+ "indirect format via a helper that passes user input straight into format!"
688
+ ],
689
+ "crown_jewels": [
690
+ "the formatting arguments on the stack",
691
+ "any panic path that formats attacker input",
692
+ "logging/telemetry that echoes request data"
693
+ ]
637
694
  }
638
695
  ]
639
696
  }
@@ -374,6 +374,30 @@
374
374
  "class": "ml_supply_chain",
375
375
  "code": "arr = np.load(path, allow_pickle=True)",
376
376
  "note": "np.load allow_pickle=True"
377
+ },
378
+ {
379
+ "class": "rust_unsafe",
380
+ "code": "let x: &[u8] = std::mem::transmute(user_data);",
381
+ "note": "transmute attacker data",
382
+ "file": "lib.rs"
383
+ },
384
+ {
385
+ "class": "rust_unsafe",
386
+ "code": "let s = unsafe { std::slice::from_raw_parts(ptr, user_len) };",
387
+ "note": "from_raw_parts with caller length",
388
+ "file": "lib.rs"
389
+ },
390
+ {
391
+ "class": "rust_format_string",
392
+ "code": "println!(user_input);",
393
+ "note": "variable as format string",
394
+ "file": "main.rs"
395
+ },
396
+ {
397
+ "class": "rust_format_string",
398
+ "code": "let s = format!(user_msg);",
399
+ "note": "format! with variable",
400
+ "file": "main.rs"
377
401
  }
378
402
  ]
379
403
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blitzstrike",
3
- "version": "2.4.70",
3
+ "version": "2.4.71",
4
4
  "description": "Blitz Strike — a universal MCP security-audit toolbelt. BLITZ sweeps the attack surface, EAGLE-EYE traces source-to-sink, STRIKE verifies live. 57 attack chains, 130-tool catalog, intelligence data layer. One server, every agent.",
5
5
  "type": "module",
6
6
  "bin": {