blitzstrike 1.0.51 → 1.0.52

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,20 @@ 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
+ ## [1.0.52] — 2026-09-13
9
+
10
+ ### Added — Autonomous Security Engine (phase 12: browser automation agent)
11
+
12
+ - **Browser Automation Agent** (`src/browser-agent.ts`, §16):
13
+ - Session-based (one persistent page) so multi-step flows work.
14
+ - Ops: `open`/`navigate`/`click`/`type`/`evaluate`/`screenshot`/`back`/`forward`/
15
+ `get_cookie`/`set_cookie`, plus `detectAuthForm()` and `browserDiagnostics()`.
16
+ - Graceful degradation — every op reports `unavailable` when `playwright-core`
17
+ (optional peer) is missing, never throws.
18
+ - New `@browser` mission agent (URL-scope-gated) — detects login forms and JS
19
+ runtime errors and emits auth-flow hypotheses.
20
+ - New MCP tool `browser_agent` (session ops + detect_auth_form + diagnostics + close).
21
+
8
22
  ## [1.0.51] — 2026-09-13
9
23
 
10
24
  ### Added — Autonomous Security Engine (phase 11: adversarial reviewer)
package/dist/index.js CHANGED
@@ -17722,6 +17722,219 @@ var init_universal_taint = __esm(() => {
17722
17722
  extensionMap = new Map;
17723
17723
  });
17724
17724
 
17725
+ // src/browser-agent.ts
17726
+ async function ensureChromium2() {
17727
+ if (chromiumCache2)
17728
+ return chromiumCache2;
17729
+ try {
17730
+ chromiumCache2 = await import("playwright-core");
17731
+ return chromiumCache2;
17732
+ } catch {
17733
+ return null;
17734
+ }
17735
+ }
17736
+ async function ensureSession() {
17737
+ if (page)
17738
+ return { available: true };
17739
+ const chr = await ensureChromium2();
17740
+ if (!chr)
17741
+ return { available: false, reason: "playwright-core not installed (optional peer dependency)" };
17742
+ try {
17743
+ browser = await chr.chromium.launch({ headless: true });
17744
+ page = await browser.newPage();
17745
+ page.on("console", (m) => {
17746
+ const msg = m;
17747
+ if (msg.type() === "error")
17748
+ consoleErrors.push(msg.text());
17749
+ });
17750
+ page.on("pageerror", (e) => pageErrors.push(String(e)));
17751
+ return { available: true };
17752
+ } catch (e) {
17753
+ return { available: false, reason: e instanceof Error ? e.message : String(e) };
17754
+ }
17755
+ }
17756
+ function unavailable2(op, reason) {
17757
+ return { op, ok: false, unavailable: true, detail: reason };
17758
+ }
17759
+ function fail(op, e) {
17760
+ return { op, ok: false, unavailable: false, detail: e instanceof Error ? e.message : String(e) };
17761
+ }
17762
+ async function browserOpen(url) {
17763
+ const s = await ensureSession();
17764
+ if (!s.available || !page)
17765
+ return unavailable2("open", s.reason ?? "unavailable");
17766
+ try {
17767
+ await page.goto(url, { waitUntil: "domcontentloaded", timeout: 20000 });
17768
+ return { op: "open", ok: true, unavailable: false, detail: `opened ${redactSecrets(page.url())}` };
17769
+ } catch (e) {
17770
+ return fail("open", e);
17771
+ }
17772
+ }
17773
+ async function browserNavigate(url) {
17774
+ return browserOpen(url);
17775
+ }
17776
+ async function browserClick(selector) {
17777
+ const s = await ensureSession();
17778
+ if (!s.available || !page)
17779
+ return unavailable2("click", s.reason ?? "unavailable");
17780
+ try {
17781
+ await page.click(selector);
17782
+ return { op: "click", ok: true, unavailable: false, detail: `clicked ${selector}` };
17783
+ } catch (e) {
17784
+ return fail("click", e);
17785
+ }
17786
+ }
17787
+ async function browserType(selector, text) {
17788
+ const s = await ensureSession();
17789
+ if (!s.available || !page)
17790
+ return unavailable2("type", s.reason ?? "unavailable");
17791
+ try {
17792
+ await page.fill(selector, text);
17793
+ return { op: "type", ok: true, unavailable: false, detail: `typed into ${selector}` };
17794
+ } catch (e) {
17795
+ return fail("type", e);
17796
+ }
17797
+ }
17798
+ async function browserEvaluate(js) {
17799
+ const s = await ensureSession();
17800
+ if (!s.available || !page)
17801
+ return unavailable2("evaluate", s.reason ?? "unavailable");
17802
+ try {
17803
+ const value = await page.evaluate((code) => {
17804
+ const fn = new Function(`"use strict"; return (${code})`)();
17805
+ return typeof fn === "function" ? fn() : fn;
17806
+ }, js);
17807
+ return { op: "evaluate", ok: true, unavailable: false, detail: "evaluated", value };
17808
+ } catch (e) {
17809
+ return fail("evaluate", e);
17810
+ }
17811
+ }
17812
+ async function browserScreenshot(path) {
17813
+ const s = await ensureSession();
17814
+ if (!s.available || !page)
17815
+ return unavailable2("screenshot", s.reason ?? "unavailable");
17816
+ try {
17817
+ const buf = await page.screenshot(path ? { path } : {});
17818
+ return { op: "screenshot", ok: true, unavailable: false, detail: path ? `saved ${path}` : "captured (in-memory)", value: path ? undefined : buf.byteLength };
17819
+ } catch (e) {
17820
+ return fail("screenshot", e);
17821
+ }
17822
+ }
17823
+ async function browserBack() {
17824
+ const s = await ensureSession();
17825
+ if (!s.available || !page)
17826
+ return unavailable2("back", s.reason ?? "unavailable");
17827
+ try {
17828
+ await page.goBack();
17829
+ return { op: "back", ok: true, unavailable: false, detail: `back to ${redactSecrets(page.url())}` };
17830
+ } catch (e) {
17831
+ return fail("back", e);
17832
+ }
17833
+ }
17834
+ async function browserForward() {
17835
+ const s = await ensureSession();
17836
+ if (!s.available || !page)
17837
+ return unavailable2("forward", s.reason ?? "unavailable");
17838
+ try {
17839
+ await page.goForward();
17840
+ return { op: "forward", ok: true, unavailable: false, detail: `forward to ${redactSecrets(page.url())}` };
17841
+ } catch (e) {
17842
+ return fail("forward", e);
17843
+ }
17844
+ }
17845
+ async function browserGetCookie(name) {
17846
+ const s = await ensureSession();
17847
+ if (!s.available || !page)
17848
+ return unavailable2("get_cookie", s.reason ?? "unavailable");
17849
+ try {
17850
+ const cookies = await page.context().cookies();
17851
+ const hit = cookies.filter((c) => c.name === name);
17852
+ return { op: "get_cookie", ok: true, unavailable: false, detail: hit.length ? "found" : "not found", value: hit };
17853
+ } catch (e) {
17854
+ return fail("get_cookie", e);
17855
+ }
17856
+ }
17857
+ async function browserSetCookie(name, value) {
17858
+ const s = await ensureSession();
17859
+ if (!s.available || !page)
17860
+ return unavailable2("set_cookie", s.reason ?? "unavailable");
17861
+ try {
17862
+ const url = page.url();
17863
+ await page.context().addCookies([{ name, value, url }]);
17864
+ return { op: "set_cookie", ok: true, unavailable: false, detail: `set ${name}` };
17865
+ } catch (e) {
17866
+ return fail("set_cookie", e);
17867
+ }
17868
+ }
17869
+ async function browserClose() {
17870
+ if (browser) {
17871
+ await browser.close().catch(() => {
17872
+ return;
17873
+ });
17874
+ browser = null;
17875
+ page = null;
17876
+ }
17877
+ }
17878
+ async function detectAuthForm() {
17879
+ const s = await ensureSession();
17880
+ if (!s.available || !page)
17881
+ return unavailable2("evaluate", s.reason ?? "unavailable");
17882
+ try {
17883
+ const value = await page.evaluate(() => {
17884
+ const forms = Array.from(document.querySelectorAll("form"));
17885
+ const loginForm = forms.find((f) => /login|signin|auth/i.test(f.action || "") || f.querySelector('input[type="password"]'));
17886
+ if (!loginForm)
17887
+ return { found: false };
17888
+ const userField = loginForm.querySelector('input[type="email"], input[type="text"], input[name*="user"], input[name*="email"], input[name*="login"]');
17889
+ const passField = loginForm.querySelector('input[type="password"]');
17890
+ return {
17891
+ found: true,
17892
+ action: loginForm.action || "(self)",
17893
+ has_user: Boolean(userField),
17894
+ has_pass: Boolean(passField),
17895
+ user_selector: userField ? `input[name="${userField.name || ""}"], input[type="${userField.type}"]` : null,
17896
+ pass_selector: passField ? 'input[type="password"]' : null
17897
+ };
17898
+ });
17899
+ return { op: "evaluate", ok: true, unavailable: false, detail: value.found ? "login form detected" : "no login form", value };
17900
+ } catch (e) {
17901
+ return fail("evaluate", e);
17902
+ }
17903
+ }
17904
+ async function browserAgent(op, args = []) {
17905
+ switch (op) {
17906
+ case "open":
17907
+ return browserOpen(args[0] ?? "");
17908
+ case "navigate":
17909
+ return browserNavigate(args[0] ?? "");
17910
+ case "click":
17911
+ return browserClick(args[0] ?? "");
17912
+ case "type":
17913
+ return browserType(args[0] ?? "", args[1] ?? "");
17914
+ case "evaluate":
17915
+ return browserEvaluate(args[0] ?? "");
17916
+ case "screenshot":
17917
+ return browserScreenshot(args[0] || undefined);
17918
+ case "back":
17919
+ return browserBack();
17920
+ case "forward":
17921
+ return browserForward();
17922
+ case "get_cookie":
17923
+ return browserGetCookie(args[0] ?? "");
17924
+ case "set_cookie":
17925
+ return browserSetCookie(args[0] ?? "", args[1] ?? "");
17926
+ }
17927
+ }
17928
+ function browserDiagnostics() {
17929
+ return { console_errors: [...consoleErrors], page_errors: [...pageErrors] };
17930
+ }
17931
+ var chromiumCache2 = null, browser = null, page = null, consoleErrors, pageErrors;
17932
+ var init_browser_agent = __esm(() => {
17933
+ init_evidence();
17934
+ consoleErrors = [];
17935
+ pageErrors = [];
17936
+ });
17937
+
17725
17938
  // src/sync.ts
17726
17939
  import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync4, readFileSync as readFileSync16, readdirSync as readdirSync5, statSync as statSync3, rmSync as rmSync3 } from "node:fs";
17727
17940
  import { join as join12 } from "node:path";
@@ -18359,7 +18572,7 @@ function sourceFiles(root, max = 500) {
18359
18572
  function agentFor(type) {
18360
18573
  return AGENTS[type];
18361
18574
  }
18362
- var reconAgent, webAgent, sourceAgent, analysisAgent, authAgent, validatorAgent, reviewerAgent, reporterAgent, mobileAgent, cloudAgent, commanderAgent, apiAgent, AGENTS;
18575
+ var reconAgent, webAgent, sourceAgent, analysisAgent, authAgent, validatorAgent, reviewerAgent, browserAgent2, reporterAgent, mobileAgent, cloudAgent, commanderAgent, apiAgent, AGENTS;
18363
18576
  var init_agents = __esm(() => {
18364
18577
  init_planner();
18365
18578
  init_reviewer();
@@ -18556,6 +18769,52 @@ var init_agents = __esm(() => {
18556
18769
  return { ok: true, output: { outcomes: results.map((r) => ({ id: r.hypothesis_id, outcome: r.outcome })) }, hypotheses, graph: emptyGraph() };
18557
18770
  }
18558
18771
  };
18772
+ browserAgent2 = {
18773
+ type: "browser",
18774
+ responsibility: "Drive a real browser to detect DOM issues and auth flows.",
18775
+ tools: ["browser_open", "browser_click", "browser_type", "browser_evaluate", "browser_screenshot", "browser_navigate", "browser_back", "browser_forward", "browser_get_cookie", "browser_set_cookie"],
18776
+ async execute(_task, ctx) {
18777
+ if (!/^https?:\/\//i.test(ctx.mission.target) || !ctx.scopeAllowed) {
18778
+ return { ok: false, output: {}, hypotheses: [], graph: emptyGraph(), error: "browser agent requires an authorized URL target" };
18779
+ }
18780
+ await Promise.resolve().then(() => init_browser_agent());
18781
+ const opened = await browserOpen(ctx.mission.target);
18782
+ if (opened.unavailable) {
18783
+ return { ok: false, output: {}, hypotheses: [], graph: emptyGraph(), error: opened.detail };
18784
+ }
18785
+ const auth = await detectAuthForm();
18786
+ const diag = browserDiagnostics();
18787
+ await browserClose();
18788
+ const hypotheses = [];
18789
+ const authInfo = auth.value ?? {};
18790
+ if (authInfo.found) {
18791
+ hypotheses.push(makeHypothesis({
18792
+ type: "auth_flow_detected",
18793
+ target: ctx.mission.target,
18794
+ evidence: `login form detected (user_field=${authInfo.has_user}, pass_field=${authInfo.has_pass})`,
18795
+ confidence: 0.6,
18796
+ required_validation: "login flow",
18797
+ potential_impact: "auth boundary"
18798
+ }));
18799
+ }
18800
+ if (diag.console_errors.length > 0 || diag.page_errors.length > 0) {
18801
+ hypotheses.push(makeHypothesis({
18802
+ type: "js_runtime_error",
18803
+ target: ctx.mission.target,
18804
+ evidence: `console_errors=${diag.console_errors.length} page_errors=${diag.page_errors.length}`,
18805
+ confidence: 0.4,
18806
+ required_validation: "review",
18807
+ potential_impact: "unspecified"
18808
+ }));
18809
+ }
18810
+ return {
18811
+ ok: true,
18812
+ output: { opened: opened.ok, auth_form: authInfo.found ?? false, console_errors: diag.console_errors.length, page_errors: diag.page_errors.length },
18813
+ hypotheses,
18814
+ graph: emptyGraph()
18815
+ };
18816
+ }
18817
+ };
18559
18818
  reporterAgent = {
18560
18819
  type: "reporter",
18561
18820
  responsibility: "Produce the final structured mission report.",
@@ -18666,7 +18925,8 @@ ${m.hypotheses.map((h) => `- [${h.status}] ${h.type} @ ${h.target} (confidence $
18666
18925
  analysis: analysisAgent,
18667
18926
  validator: validatorAgent,
18668
18927
  reviewer: reviewerAgent,
18669
- reporter: reporterAgent
18928
+ reporter: reporterAgent,
18929
+ browser: browserAgent2
18670
18930
  };
18671
18931
  });
18672
18932
 
@@ -18792,7 +19052,8 @@ var init_recovery = __esm(() => {
18792
19052
  analysis: ["source"],
18793
19053
  validator: ["reviewer"],
18794
19054
  reviewer: ["validator"],
18795
- reporter: []
19055
+ reporter: [],
19056
+ browser: ["web"]
18796
19057
  };
18797
19058
  });
18798
19059
 
@@ -53121,6 +53382,7 @@ async function browserValidate(check, target, payload) {
53121
53382
  }
53122
53383
 
53123
53384
  // src/server.ts
53385
+ init_browser_agent();
53124
53386
  init_universal_taint();
53125
53387
 
53126
53388
  // src/guidance.ts
@@ -54684,6 +54946,28 @@ function createServer() {
54684
54946
  const result = await browserValidate(check, target, payload);
54685
54947
  return { content: [{ type: "text", text: JSON.stringify(result) }] };
54686
54948
  });
54949
+ server.registerTool("browser_agent", {
54950
+ title: "Browser automation agent (session)",
54951
+ description: "PHASE 12: drive a real headless-Chromium session. Ops: open, navigate, click, type, evaluate, screenshot, back, forward, get_cookie, set_cookie, plus detect_auth_form, diagnostics, close. Enables multi-step flows (login/MFA) and DOM issue detection. Requires playwright-core (optional peer dependency).",
54952
+ inputSchema: {
54953
+ op: _enum(["open", "navigate", "click", "type", "evaluate", "screenshot", "back", "forward", "get_cookie", "set_cookie", "detect_auth_form", "diagnostics", "close"]),
54954
+ args: array(string2()).optional().describe("Positional args (url, selector, text, js, cookie name/value, screenshot path)")
54955
+ }
54956
+ }, async ({ op, args }) => {
54957
+ if (op === "detect_auth_form") {
54958
+ const r = await detectAuthForm();
54959
+ return { content: [{ type: "text", text: JSON.stringify(r) }] };
54960
+ }
54961
+ if (op === "diagnostics") {
54962
+ return { content: [{ type: "text", text: JSON.stringify(browserDiagnostics()) }] };
54963
+ }
54964
+ if (op === "close") {
54965
+ await browserClose();
54966
+ return { content: [{ type: "text", text: JSON.stringify({ ok: true, detail: "session closed" }) }] };
54967
+ }
54968
+ const r = await browserAgent(op, args ?? []);
54969
+ return { content: [{ type: "text", text: JSON.stringify(r) }] };
54970
+ });
54687
54971
  server.registerTool("taint_tree", {
54688
54972
  title: "Whole-tree taint scan",
54689
54973
  description: "EAGLE-EYE: run inter-procedural taint analysis across an entire source tree.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blitzstrike",
3
- "version": "1.0.51",
3
+ "version": "1.0.52",
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": {