brainblast 0.6.3 → 0.7.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/cli.js CHANGED
@@ -2,27 +2,36 @@
2
2
  import {
3
3
  analyzeCosts,
4
4
  applyDiffToFile,
5
- buildTrustGraph,
6
- cacheSize,
7
- defaultCachePath,
8
5
  initPack,
9
6
  isTelemetryEnabled,
10
- isValidSolanaAddress,
11
- loadProgramCache,
12
7
  parseDiff,
13
8
  recordGraduationEvents,
14
9
  renderCostReportMd,
15
- renderTrustGraphMd,
16
10
  startWatch,
17
11
  submitTelemetry,
18
12
  telemetryFilePath,
19
13
  validatePack
20
- } from "./chunk-ZZ6LBZV5.js";
14
+ } from "./chunk-34VXOLJF.js";
15
+ import {
16
+ renderTrustGraphMd
17
+ } from "./chunk-2UZGWXIX.js";
18
+ import {
19
+ buildTrustGraph,
20
+ cacheSize,
21
+ defaultCachePath,
22
+ loadProgramCache
23
+ } from "./chunk-SVSVVW6U.js";
21
24
  import {
22
25
  audit,
23
26
  getChangedRanges,
24
27
  resolveRules
25
- } from "./chunk-A56IF3UX.js";
28
+ } from "./chunk-CRYFCQYM.js";
29
+ import "./chunk-2XJORJPQ.js";
30
+ import "./chunk-O5Z4ZJHC.js";
31
+ import "./chunk-XSVQSK53.js";
32
+ import {
33
+ isValidSolanaAddress
34
+ } from "./chunk-VG5FMOLW.js";
26
35
  import "./chunk-3RG5ZIWI.js";
27
36
 
28
37
  // src/cli.ts
@@ -119,7 +128,7 @@ if (args[0] === "drift") {
119
128
  process.exit(0);
120
129
  }
121
130
  if (args[0] === "mcp") {
122
- const { startMcpServer } = await import("./mcp-AFYJQ7K6.js");
131
+ const { startMcpServer } = await import("./mcp-ML2X44WE.js");
123
132
  await startMcpServer();
124
133
  process.exit(0);
125
134
  }
@@ -143,6 +152,57 @@ if (args[0] === "watch") {
143
152
  await new Promise(() => {
144
153
  });
145
154
  }
155
+ if (args[0] === "watch-chain") {
156
+ const rest = args.slice(1);
157
+ const programId = rest.find((a) => !a.startsWith("--"));
158
+ if (!programId) {
159
+ console.error("usage: brainblast watch-chain <program-id> [--rpc URL] [--interval <seconds>] [--limit N]");
160
+ console.error(" Poll a deployed program for new activity and upgrade-authority changes. Emits NDJSON.");
161
+ process.exit(2);
162
+ }
163
+ const { startChainWatch } = await import("./watchChain-F6INXAPA.js");
164
+ const rpcIdx = rest.indexOf("--rpc");
165
+ const rpcUrl = rpcIdx >= 0 ? rest[rpcIdx + 1] : void 0;
166
+ const intIdx = rest.indexOf("--interval");
167
+ const intervalMs = intIdx >= 0 ? parseInt(rest[intIdx + 1], 10) * 1e3 : void 0;
168
+ const limIdx = rest.indexOf("--limit");
169
+ const limit = limIdx >= 0 ? parseInt(rest[limIdx + 1], 10) : void 0;
170
+ const handle = startChainWatch(programId, { rpcUrl, intervalMs, limit });
171
+ process.on("SIGINT", () => {
172
+ handle.stop();
173
+ process.exit(0);
174
+ });
175
+ process.on("SIGTERM", () => {
176
+ handle.stop();
177
+ process.exit(0);
178
+ });
179
+ await new Promise(() => {
180
+ });
181
+ }
182
+ if (args[0] === "rico") {
183
+ await runRico(args.slice(1));
184
+ process.exit(0);
185
+ }
186
+ if (args[0] === "firewall") {
187
+ await runFirewall(args.slice(1));
188
+ process.exit(0);
189
+ }
190
+ if (args[0] === "idl-rules") {
191
+ await runIdlRules(args.slice(1));
192
+ process.exit(0);
193
+ }
194
+ if (args[0] === "score") {
195
+ await runScore(args.slice(1));
196
+ process.exit(0);
197
+ }
198
+ if (args[0] === "pump-check") {
199
+ await runPumpCheck(args.slice(1));
200
+ process.exit(0);
201
+ }
202
+ if (args[0] === "batch") {
203
+ await runBatch(args.slice(1));
204
+ process.exit(0);
205
+ }
146
206
  if (args[0] === "fix") {
147
207
  await runFix(args.slice(1));
148
208
  process.exit(0);
@@ -510,7 +570,7 @@ async function runDiff(argv) {
510
570
  console.error(" e.g.: brainblast diff lodash@4.17.20 lodash@4.17.21");
511
571
  process.exit(2);
512
572
  }
513
- const { diffVersions, renderDiffText, renderDiffMd, riskScore } = await import("./diff-KIR4PCBC.js");
573
+ const { diffVersions, renderDiffText, renderDiffMd: _renderDiffMd, riskScore } = await import("./diff-KIR4PCBC.js");
514
574
  let result;
515
575
  try {
516
576
  result = await diffVersions(ecosystem, pkgName, fromVersion, toVersion);
@@ -538,3 +598,254 @@ Risk profile unchanged (${result.unchanged.length} advisory${result.unchanged.le
538
598
  console.log("\nNo known advisories for either version.");
539
599
  }
540
600
  }
601
+ async function runRico(argv) {
602
+ const { analyzeToken, renderRicoText } = await import("./ricomaps-WTMWBBOY.js");
603
+ const { verifyTokenIdentity } = await import("./tokenRegistry-CYIUZHAZ.js");
604
+ const mint = argv.find((a) => !a.startsWith("--"));
605
+ if (!mint) {
606
+ console.error("usage: brainblast rico <mint> [--expect SYMBOL] [--api-key KEY] [--fail-on SCORE] [--offline] [--json]");
607
+ process.exit(2);
608
+ }
609
+ const expectIdx = argv.indexOf("--expect");
610
+ const expectSymbol = expectIdx >= 0 ? argv[expectIdx + 1] : void 0;
611
+ const apiKeyIdx = argv.indexOf("--api-key");
612
+ let apiKey = apiKeyIdx >= 0 ? argv[apiKeyIdx + 1] : void 0;
613
+ const failOnIdx = argv.indexOf("--fail-on");
614
+ const failOn = failOnIdx >= 0 ? parseInt(argv[failOnIdx + 1], 10) : 70;
615
+ const offline = argv.includes("--offline");
616
+ const jsonOut = argv.includes("--json");
617
+ let ricoResult = null;
618
+ if (!offline) {
619
+ ricoResult = await analyzeToken(mint, { apiKey });
620
+ if (!ricoResult.ok && ricoResult.kind === "auth") {
621
+ const readline = await import("readline");
622
+ const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
623
+ const answer = await new Promise((resolve) => {
624
+ rl.question(
625
+ "\nRico Maps API key missing or invalid.\n [s] Skip quality scan\n [k] Enter API key\nChoice: ",
626
+ resolve
627
+ );
628
+ });
629
+ rl.close();
630
+ if (answer.trim().toLowerCase().startsWith("k")) {
631
+ const rl2 = readline.createInterface({ input: process.stdin, output: process.stderr });
632
+ apiKey = await new Promise((resolve) => {
633
+ rl2.question("API key: ", resolve);
634
+ });
635
+ rl2.close();
636
+ ricoResult = await analyzeToken(mint, { apiKey });
637
+ } else {
638
+ ricoResult = null;
639
+ }
640
+ }
641
+ }
642
+ const claimedSymbol = ricoResult?.ok ? ricoResult.result.symbol : void 0;
643
+ const identity = await verifyTokenIdentity(mint, { expectSymbol, claimedSymbol, offline });
644
+ if (jsonOut) {
645
+ console.log(JSON.stringify({ identity, quality: ricoResult?.ok ? ricoResult.result : null }, null, 2));
646
+ } else {
647
+ const impTag = identity.impersonation ? " \u26A0 IMPERSONATION" : "";
648
+ const expectTag = identity.expectMismatch ? " \u26A0 EXPECT MISMATCH" : "";
649
+ console.log(`
650
+ Identity [${identity.status}]${impTag}${expectTag}`);
651
+ if (identity.symbol) console.log(` Symbol: ${identity.symbol}`);
652
+ if (identity.name) console.log(` Name: ${identity.name}`);
653
+ console.log(` Source: ${identity.source}`);
654
+ if (identity.impersonation && identity.canonicalMint) {
655
+ console.log(` Canonical ${identity.symbol} mint: ${identity.canonicalMint}`);
656
+ console.log(` This token: ${mint}`);
657
+ }
658
+ if (identity.detail) console.log(` Note: ${identity.detail}`);
659
+ if (ricoResult === null) {
660
+ console.log("\nQuality [skipped]");
661
+ } else if (!ricoResult.ok) {
662
+ console.log(`
663
+ Quality [error: ${ricoResult.kind}] ${ricoResult.error}`);
664
+ if (ricoResult.kind === "rate-limit" && ricoResult.retryAfterMs) {
665
+ console.log(` Retry after: ${Math.ceil(ricoResult.retryAfterMs / 1e3)}s`);
666
+ }
667
+ } else {
668
+ console.log(`
669
+ ${renderRicoText(ricoResult.result)}`);
670
+ }
671
+ console.log("");
672
+ }
673
+ const highRisk = ricoResult?.ok && ricoResult.result.riskScore >= failOn;
674
+ if (identity.impersonation || identity.expectMismatch || highRisk) {
675
+ process.exit(1);
676
+ }
677
+ }
678
+ async function runFirewall(argv) {
679
+ const { inspectTransaction, renderFirewallText } = await import("./firewall-HN5XJLGC.js");
680
+ const tx = argv.find((a) => !a.startsWith("--"));
681
+ if (!tx) {
682
+ console.error("usage: brainblast firewall <base64-tx> [--rpc URL] [--no-simulate] [--message-only] [--strict] [--json]");
683
+ console.error(" Inspect a serialized Solana transaction before an agent signs it.");
684
+ console.error(" Exit 1 on BLOCK verdict (or any WARN with --strict).");
685
+ process.exit(2);
686
+ }
687
+ const rpcIdx = argv.indexOf("--rpc");
688
+ const rpcUrl = rpcIdx >= 0 ? argv[rpcIdx + 1] : void 0;
689
+ const noSimulate = argv.includes("--no-simulate");
690
+ const messageOnly = argv.includes("--message-only");
691
+ const strict2 = argv.includes("--strict");
692
+ const jsonOut = argv.includes("--json");
693
+ let report2;
694
+ try {
695
+ report2 = await inspectTransaction(tx, { rpcUrl, simulate: !noSimulate, messageOnly });
696
+ } catch (e) {
697
+ console.error(`brainblast firewall: ${e?.message ?? String(e)}`);
698
+ process.exit(2);
699
+ }
700
+ if (jsonOut) {
701
+ console.log(JSON.stringify(report2, null, 2));
702
+ } else {
703
+ console.log(renderFirewallText(report2));
704
+ }
705
+ if (report2.verdict === "block" || strict2 && report2.verdict === "warn") {
706
+ process.exit(1);
707
+ }
708
+ }
709
+ async function runIdlRules(argv) {
710
+ const { readFileSync: readFileSync2, writeFileSync: writeFileSync3, mkdirSync: mkdirSync3 } = await import("fs");
711
+ const { join: join3 } = await import("path");
712
+ const { parseIdl, generateRulesFromIdl, renderRulesYaml } = await import("./idlRules-3KZML4NL.js");
713
+ const idlPath = argv.find((a) => !a.startsWith("--"));
714
+ if (!idlPath) {
715
+ console.error("usage: brainblast idl-rules <idl.json> [--out <dir>] [--json]");
716
+ console.error(" Generate brainblast rules from an Anchor IDL's account constraints.");
717
+ process.exit(2);
718
+ }
719
+ const outIdx = argv.indexOf("--out");
720
+ const outDir2 = outIdx >= 0 ? argv[outIdx + 1] : void 0;
721
+ const jsonOut = argv.includes("--json");
722
+ let idl;
723
+ try {
724
+ idl = parseIdl(JSON.parse(readFileSync2(idlPath, "utf8")));
725
+ } catch (e) {
726
+ console.error(`brainblast idl-rules: ${e?.message ?? String(e)}`);
727
+ process.exit(2);
728
+ }
729
+ const rules2 = generateRulesFromIdl(idl);
730
+ if (rules2.length === 0) {
731
+ console.error("brainblast idl-rules: IDL produced no rules (no instructions?)");
732
+ process.exit(1);
733
+ }
734
+ if (jsonOut) {
735
+ console.log(JSON.stringify(rules2, null, 2));
736
+ return;
737
+ }
738
+ const yaml = renderRulesYaml(rules2);
739
+ if (outDir2) {
740
+ mkdirSync3(outDir2, { recursive: true });
741
+ const file = join3(outDir2, `${rules2[0].id}.yaml`);
742
+ writeFileSync3(file, yaml);
743
+ console.log(`Generated ${rules2.length} rule(s) \u2192 ${file}`);
744
+ console.log(` Run against your program: npx brainblast <program-dir> --packs <pack-with-this-rule>`);
745
+ } else {
746
+ console.log(yaml);
747
+ }
748
+ }
749
+ async function runScore(argv) {
750
+ const { scoreProgram, renderScoreText, gradeAtLeast } = await import("./score-VLKER37D.js");
751
+ const { isValidSolanaAddress: isValidSolanaAddress2 } = await import("./trustGraph-4SSJOQKT.js");
752
+ const programId = argv.find((a) => !a.startsWith("--"));
753
+ if (!programId) {
754
+ console.error("usage: brainblast score <program-id> [--rpc URL] [--no-probe] [--min A|B|C|D|F] [--json]");
755
+ console.error(" Compute a 0-100 trust score + A-F grade for a deployed Solana program.");
756
+ process.exit(2);
757
+ }
758
+ if (!isValidSolanaAddress2(programId)) {
759
+ console.error(`brainblast score: not a valid Solana address: ${programId}`);
760
+ process.exit(2);
761
+ }
762
+ const rpcIdx = argv.indexOf("--rpc");
763
+ const rpcUrl = rpcIdx >= 0 ? argv[rpcIdx + 1] : void 0;
764
+ const noProbe = argv.includes("--no-probe");
765
+ const minIdx = argv.indexOf("--min");
766
+ const min = minIdx >= 0 ? argv[minIdx + 1] : void 0;
767
+ const jsonOut = argv.includes("--json");
768
+ let result;
769
+ try {
770
+ result = await scoreProgram(programId, { rpcUrl, probeRpc: !noProbe });
771
+ } catch (e) {
772
+ console.error(`brainblast score: ${e?.message ?? String(e)}`);
773
+ process.exit(1);
774
+ }
775
+ if (jsonOut) {
776
+ console.log(JSON.stringify(result, null, 2));
777
+ } else {
778
+ console.log(renderScoreText(result));
779
+ }
780
+ if (min && !gradeAtLeast(result.grade, min)) {
781
+ process.exit(1);
782
+ }
783
+ }
784
+ async function runPumpCheck(argv) {
785
+ const { pumpPreflight, renderPreflightText } = await import("./pumpCheck-K2ESOBNU.js");
786
+ const mint = argv.find((a) => !a.startsWith("--"));
787
+ if (!mint) {
788
+ console.error("usage: brainblast pump-check <mint> [--rpc URL] [--api-key KEY] [--fail-on SCORE] [--offline] [--json]");
789
+ console.error(" Launch pre-flight: mint/freeze authority, identity, and Rico Maps forensics \u2192 GO/CAUTION/NO-GO.");
790
+ process.exit(2);
791
+ }
792
+ const rpcIdx = argv.indexOf("--rpc");
793
+ const rpcUrl = rpcIdx >= 0 ? argv[rpcIdx + 1] : void 0;
794
+ const keyIdx = argv.indexOf("--api-key");
795
+ const apiKey = keyIdx >= 0 ? argv[keyIdx + 1] : void 0;
796
+ const failIdx = argv.indexOf("--fail-on");
797
+ const failOnRisk = failIdx >= 0 ? parseInt(argv[failIdx + 1], 10) : void 0;
798
+ const offline = argv.includes("--offline");
799
+ const jsonOut = argv.includes("--json");
800
+ let report2;
801
+ try {
802
+ report2 = await pumpPreflight(mint, { rpcUrl, apiKey, failOnRisk, offline });
803
+ } catch (e) {
804
+ console.error(`brainblast pump-check: ${e?.message ?? String(e)}`);
805
+ process.exit(2);
806
+ }
807
+ if (jsonOut) {
808
+ console.log(JSON.stringify(report2, null, 2));
809
+ } else {
810
+ console.log(renderPreflightText(report2));
811
+ }
812
+ if (report2.verdict === "NO-GO") process.exit(1);
813
+ }
814
+ async function runBatch(argv) {
815
+ const { readFileSync: readFileSync2 } = await import("fs");
816
+ const { batchScan, parseMintList, renderBatchText } = await import("./batchScan-JR2G5JCF.js");
817
+ const file = argv.find((a) => !a.startsWith("--"));
818
+ if (!file) {
819
+ console.error("usage: brainblast batch <file> [--concurrency N] [--api-key KEY] [--fail-on SCORE] [--offline] [--json]");
820
+ console.error(" Risk-rank a list of contract addresses (newline-separated or JSON array).");
821
+ process.exit(2);
822
+ }
823
+ let mints;
824
+ try {
825
+ mints = parseMintList(readFileSync2(file, "utf8"));
826
+ } catch (e) {
827
+ console.error(`brainblast batch: ${e?.message ?? String(e)}`);
828
+ process.exit(2);
829
+ }
830
+ if (mints.length === 0) {
831
+ console.error("brainblast batch: no addresses found in file");
832
+ process.exit(2);
833
+ }
834
+ const concIdx = argv.indexOf("--concurrency");
835
+ const concurrency = concIdx >= 0 ? parseInt(argv[concIdx + 1], 10) : void 0;
836
+ const keyIdx = argv.indexOf("--api-key");
837
+ const apiKey = keyIdx >= 0 ? argv[keyIdx + 1] : void 0;
838
+ const failIdx = argv.indexOf("--fail-on");
839
+ const failOnRisk = failIdx >= 0 ? parseInt(argv[failIdx + 1], 10) : void 0;
840
+ const offline = argv.includes("--offline");
841
+ const jsonOut = argv.includes("--json");
842
+ const result = await batchScan(mints, { concurrency, apiKey, failOnRisk, offline });
843
+ if (jsonOut) {
844
+ console.log(JSON.stringify(result, null, 2));
845
+ } else {
846
+ console.log(renderBatchText(result));
847
+ }
848
+ if (result.summary.impersonators > 0 || result.summary.highRisk > 0) {
849
+ process.exit(1);
850
+ }
851
+ }
@@ -0,0 +1,18 @@
1
+ import {
2
+ KNOWN_PROGRAMS,
3
+ analyzeInstructions,
4
+ decodeTransaction,
5
+ inspectTransaction,
6
+ parseCpiPrograms,
7
+ renderFirewallText
8
+ } from "./chunk-HL7NVANZ.js";
9
+ import "./chunk-VG5FMOLW.js";
10
+ import "./chunk-3RG5ZIWI.js";
11
+ export {
12
+ KNOWN_PROGRAMS,
13
+ analyzeInstructions,
14
+ decodeTransaction,
15
+ inspectTransaction,
16
+ parseCpiPrograms,
17
+ renderFirewallText
18
+ };
@@ -0,0 +1,17 @@
1
+ import {
2
+ buildConstraintParams,
3
+ generateRulesFromIdl,
4
+ idlProgramName,
5
+ parseIdl,
6
+ renderRulesYaml,
7
+ toSnakeCase
8
+ } from "./chunk-O5Z4ZJHC.js";
9
+ import "./chunk-3RG5ZIWI.js";
10
+ export {
11
+ buildConstraintParams,
12
+ generateRulesFromIdl,
13
+ idlProgramName,
14
+ parseIdl,
15
+ renderRulesYaml,
16
+ toSnakeCase
17
+ };