brainblast 0.6.4 → 0.7.1
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/batchScan-JR2G5JCF.js +14 -0
- package/dist/chunk-2UZGWXIX.js +77 -0
- package/dist/{chunk-HXQNNGSC.js → chunk-B2M3TZSA.js} +32 -433
- package/dist/chunk-DQ4KAYKQ.js +111 -0
- package/dist/chunk-HL7NVANZ.js +331 -0
- package/dist/{chunk-2Y6UILTZ.js → chunk-IY52XKWL.js} +149 -1
- package/dist/chunk-O5Z4ZJHC.js +89 -0
- package/dist/chunk-QC27GNQ7.js +101 -0
- package/dist/chunk-SVSVVW6U.js +187 -0
- package/dist/chunk-UWE6HAGS.js +176 -0
- package/dist/chunk-VG5FMOLW.js +61 -0
- package/dist/chunk-WX3IR7LK.js +148 -0
- package/dist/chunk-XSVQSK53.js +100 -0
- package/dist/cli.js +238 -9
- package/dist/firewall-HN5XJLGC.js +18 -0
- package/dist/idlRules-3KZML4NL.js +17 -0
- package/dist/index.d.ts +238 -1
- package/dist/index.js +105 -34
- package/dist/{mcp-FCKMS2MQ.js → mcp-AM7MTCSZ.js} +3 -2
- package/dist/pumpCheck-K2ESOBNU.js +16 -0
- package/dist/rpc-W5F4KXS2.js +18 -0
- package/dist/rules/anchor-pda-find-program-address.yaml +28 -0
- package/dist/rules/anchor-signer-constraint-missing.yaml +27 -0
- package/dist/rules/anchor-unchecked-account-type.yaml +27 -0
- package/dist/score-VLKER37D.js +18 -0
- package/dist/trustGraph-4SSJOQKT.js +49 -0
- package/dist/watchChain-F6INXAPA.js +13 -0
- package/package.json +2 -1
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import {
|
|
2
|
+
base58Encode,
|
|
3
|
+
isValidSolanaAddress
|
|
4
|
+
} from "./chunk-VG5FMOLW.js";
|
|
5
|
+
|
|
6
|
+
// src/trustGraph/rpc.ts
|
|
7
|
+
var BPF_UPGRADEABLE_LOADER = "BPFLoaderUpgradeab1e11111111111111111111111";
|
|
8
|
+
var BPF_LOADER_2 = "BPFLoader2111111111111111111111111111111111";
|
|
9
|
+
var NATIVE_LOADER = "NativeLoader1111111111111111111111111111111";
|
|
10
|
+
var DEFAULT_RPC = "https://api.mainnet-beta.solana.com";
|
|
11
|
+
async function rpc(method, params, opts) {
|
|
12
|
+
const url = opts.rpcUrl ?? DEFAULT_RPC;
|
|
13
|
+
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
14
|
+
const ac = new AbortController();
|
|
15
|
+
const t = setTimeout(() => ac.abort(), opts.timeoutMs ?? 1e4);
|
|
16
|
+
try {
|
|
17
|
+
const res = await fetchImpl(url, {
|
|
18
|
+
method: "POST",
|
|
19
|
+
headers: { "content-type": "application/json" },
|
|
20
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params }),
|
|
21
|
+
signal: ac.signal
|
|
22
|
+
});
|
|
23
|
+
if (!res.ok) throw new Error(`rpc ${method}: HTTP ${res.status}`);
|
|
24
|
+
const body = await res.json();
|
|
25
|
+
if (body.error) throw new Error(`rpc ${method}: ${body.error.message}`);
|
|
26
|
+
if (body.result === void 0) throw new Error(`rpc ${method}: empty result`);
|
|
27
|
+
return body.result;
|
|
28
|
+
} finally {
|
|
29
|
+
clearTimeout(t);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
async function getAccountInfo(address, opts = {}) {
|
|
33
|
+
if (!isValidSolanaAddress(address)) throw new Error(`invalid Solana address: ${address}`);
|
|
34
|
+
const result = await rpc(
|
|
35
|
+
"getAccountInfo",
|
|
36
|
+
[address, { encoding: "base64", commitment: "confirmed" }],
|
|
37
|
+
opts
|
|
38
|
+
);
|
|
39
|
+
if (!result || !result.value) return null;
|
|
40
|
+
const v = result.value;
|
|
41
|
+
const [b64] = v.data;
|
|
42
|
+
return {
|
|
43
|
+
owner: v.owner,
|
|
44
|
+
data: Buffer.from(b64, "base64"),
|
|
45
|
+
executable: v.executable,
|
|
46
|
+
lamports: v.lamports
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
async function probeUpgradeAuthority(programId, opts = {}) {
|
|
50
|
+
const acct = await getAccountInfo(programId, opts);
|
|
51
|
+
if (!acct) {
|
|
52
|
+
return {
|
|
53
|
+
kind: "unknown",
|
|
54
|
+
address: null,
|
|
55
|
+
source: "rpc",
|
|
56
|
+
checkedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
if (acct.owner === BPF_LOADER_2 || acct.owner === NATIVE_LOADER) {
|
|
60
|
+
return {
|
|
61
|
+
kind: "renounced",
|
|
62
|
+
address: null,
|
|
63
|
+
source: "rpc",
|
|
64
|
+
checkedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
if (acct.owner !== BPF_UPGRADEABLE_LOADER) {
|
|
68
|
+
throw new Error(
|
|
69
|
+
`program ${programId} is owned by ${acct.owner}, not a known loader; not a deployed program?`
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
if (acct.data.length < 36) throw new Error(`program account too small: ${acct.data.length}`);
|
|
73
|
+
const tag = acct.data[0] | acct.data[1] << 8 | acct.data[2] << 16 | acct.data[3] << 24;
|
|
74
|
+
if (tag !== 2) throw new Error(`expected Program (tag=2) state, got tag=${tag}`);
|
|
75
|
+
const programDataAddr = base58Encode(acct.data.subarray(4, 36));
|
|
76
|
+
const pd = await getAccountInfo(programDataAddr, opts);
|
|
77
|
+
if (!pd) {
|
|
78
|
+
throw new Error(`program ${programId} ProgramData ${programDataAddr} not found`);
|
|
79
|
+
}
|
|
80
|
+
if (pd.data.length < 45) throw new Error(`ProgramData account too small: ${pd.data.length}`);
|
|
81
|
+
const pdTag = pd.data[0] | pd.data[1] << 8 | pd.data[2] << 16 | pd.data[3] << 24;
|
|
82
|
+
if (pdTag !== 3) throw new Error(`expected ProgramData (tag=3), got tag=${pdTag}`);
|
|
83
|
+
const optionTag = pd.data[12];
|
|
84
|
+
const checkedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
85
|
+
if (optionTag === 0) {
|
|
86
|
+
return { kind: "renounced", address: null, source: "rpc", checkedAt };
|
|
87
|
+
}
|
|
88
|
+
if (optionTag !== 1) throw new Error(`unexpected Option tag in ProgramData: ${optionTag}`);
|
|
89
|
+
const authority = base58Encode(pd.data.subarray(13, 45));
|
|
90
|
+
return { kind: "unknown", address: authority, source: "rpc", checkedAt };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export {
|
|
94
|
+
BPF_UPGRADEABLE_LOADER,
|
|
95
|
+
BPF_LOADER_2,
|
|
96
|
+
NATIVE_LOADER,
|
|
97
|
+
DEFAULT_RPC,
|
|
98
|
+
getAccountInfo,
|
|
99
|
+
probeUpgradeAuthority
|
|
100
|
+
};
|
package/dist/cli.js
CHANGED
|
@@ -2,28 +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-
|
|
14
|
+
} from "./chunk-B2M3TZSA.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-
|
|
28
|
+
} from "./chunk-IY52XKWL.js";
|
|
26
29
|
import "./chunk-2XJORJPQ.js";
|
|
30
|
+
import "./chunk-O5Z4ZJHC.js";
|
|
31
|
+
import "./chunk-XSVQSK53.js";
|
|
32
|
+
import {
|
|
33
|
+
isValidSolanaAddress
|
|
34
|
+
} from "./chunk-VG5FMOLW.js";
|
|
27
35
|
import "./chunk-3RG5ZIWI.js";
|
|
28
36
|
|
|
29
37
|
// src/cli.ts
|
|
@@ -120,7 +128,7 @@ if (args[0] === "drift") {
|
|
|
120
128
|
process.exit(0);
|
|
121
129
|
}
|
|
122
130
|
if (args[0] === "mcp") {
|
|
123
|
-
const { startMcpServer } = await import("./mcp-
|
|
131
|
+
const { startMcpServer } = await import("./mcp-AM7MTCSZ.js");
|
|
124
132
|
await startMcpServer();
|
|
125
133
|
process.exit(0);
|
|
126
134
|
}
|
|
@@ -144,10 +152,57 @@ if (args[0] === "watch") {
|
|
|
144
152
|
await new Promise(() => {
|
|
145
153
|
});
|
|
146
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
|
+
}
|
|
147
182
|
if (args[0] === "rico") {
|
|
148
183
|
await runRico(args.slice(1));
|
|
149
184
|
process.exit(0);
|
|
150
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
|
+
}
|
|
151
206
|
if (args[0] === "fix") {
|
|
152
207
|
await runFix(args.slice(1));
|
|
153
208
|
process.exit(0);
|
|
@@ -620,3 +675,177 @@ ${renderRicoText(ricoResult.result)}`);
|
|
|
620
675
|
process.exit(1);
|
|
621
676
|
}
|
|
622
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
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -624,4 +624,241 @@ declare function analyzeToken(mint: string, opts?: {
|
|
|
624
624
|
baseUrl?: string;
|
|
625
625
|
}): Promise<RicoOutcome>;
|
|
626
626
|
|
|
627
|
-
|
|
627
|
+
declare const KNOWN_PROGRAMS: Record<string, string>;
|
|
628
|
+
type FirewallSeverity = "info" | "warn" | "critical";
|
|
629
|
+
type FirewallVerdict = "allow" | "warn" | "block";
|
|
630
|
+
interface FirewallFinding {
|
|
631
|
+
severity: FirewallSeverity;
|
|
632
|
+
kind: string;
|
|
633
|
+
detail: string;
|
|
634
|
+
}
|
|
635
|
+
interface FirewallProgram {
|
|
636
|
+
id: string;
|
|
637
|
+
label: string | null;
|
|
638
|
+
known: boolean;
|
|
639
|
+
topLevel: boolean;
|
|
640
|
+
}
|
|
641
|
+
interface DecodedInstruction {
|
|
642
|
+
programIdIndex: number;
|
|
643
|
+
programId: string;
|
|
644
|
+
accountIndexes: number[];
|
|
645
|
+
data: Uint8Array;
|
|
646
|
+
}
|
|
647
|
+
interface DecodedTx {
|
|
648
|
+
version: "legacy" | number;
|
|
649
|
+
numRequiredSignatures: number;
|
|
650
|
+
numReadonlySigned: number;
|
|
651
|
+
numReadonlyUnsigned: number;
|
|
652
|
+
staticAccountKeys: string[];
|
|
653
|
+
recentBlockhash: string;
|
|
654
|
+
instructions: DecodedInstruction[];
|
|
655
|
+
addressTableLookups: {
|
|
656
|
+
accountKey: string;
|
|
657
|
+
writableCount: number;
|
|
658
|
+
readonlyCount: number;
|
|
659
|
+
}[];
|
|
660
|
+
}
|
|
661
|
+
interface FirewallReport {
|
|
662
|
+
version: "legacy" | number;
|
|
663
|
+
feePayer: string;
|
|
664
|
+
numSigners: number;
|
|
665
|
+
staticAccounts: number;
|
|
666
|
+
programs: FirewallProgram[];
|
|
667
|
+
usesAddressLookupTables: boolean;
|
|
668
|
+
simulation: {
|
|
669
|
+
ran: boolean;
|
|
670
|
+
ok?: boolean;
|
|
671
|
+
err?: unknown;
|
|
672
|
+
unitsConsumed?: number;
|
|
673
|
+
logsCount?: number;
|
|
674
|
+
cpiPrograms?: string[];
|
|
675
|
+
};
|
|
676
|
+
findings: FirewallFinding[];
|
|
677
|
+
verdict: FirewallVerdict;
|
|
678
|
+
}
|
|
679
|
+
declare function decodeTransaction(base64: string, opts?: {
|
|
680
|
+
messageOnly?: boolean;
|
|
681
|
+
}): DecodedTx;
|
|
682
|
+
declare function analyzeInstructions(decoded: DecodedTx, known: Record<string, string>): FirewallFinding[];
|
|
683
|
+
declare function parseCpiPrograms(logs: string[]): string[];
|
|
684
|
+
interface FirewallOpts extends RpcOpts {
|
|
685
|
+
simulate?: boolean;
|
|
686
|
+
messageOnly?: boolean;
|
|
687
|
+
knownPrograms?: Record<string, string>;
|
|
688
|
+
}
|
|
689
|
+
declare function inspectTransaction(base64: string, opts?: FirewallOpts): Promise<FirewallReport>;
|
|
690
|
+
declare function renderFirewallText(r: FirewallReport): string;
|
|
691
|
+
|
|
692
|
+
interface IdlAccount {
|
|
693
|
+
name: string;
|
|
694
|
+
isMut?: boolean;
|
|
695
|
+
isSigner?: boolean;
|
|
696
|
+
accounts?: IdlAccount[];
|
|
697
|
+
}
|
|
698
|
+
interface IdlInstruction {
|
|
699
|
+
name: string;
|
|
700
|
+
accounts: IdlAccount[];
|
|
701
|
+
}
|
|
702
|
+
interface AnchorIdl {
|
|
703
|
+
name?: string;
|
|
704
|
+
metadata?: {
|
|
705
|
+
name?: string;
|
|
706
|
+
version?: string;
|
|
707
|
+
};
|
|
708
|
+
version?: string;
|
|
709
|
+
instructions: IdlInstruction[];
|
|
710
|
+
}
|
|
711
|
+
declare function toSnakeCase(s: string): string;
|
|
712
|
+
declare function idlProgramName(idl: AnchorIdl): string;
|
|
713
|
+
declare function parseIdl(json: unknown): AnchorIdl;
|
|
714
|
+
interface IdlConstraintParams {
|
|
715
|
+
idlName: string;
|
|
716
|
+
instructions: {
|
|
717
|
+
name: string;
|
|
718
|
+
signers: string[];
|
|
719
|
+
mutable: string[];
|
|
720
|
+
}[];
|
|
721
|
+
}
|
|
722
|
+
declare function buildConstraintParams(idl: AnchorIdl): IdlConstraintParams;
|
|
723
|
+
declare function generateRulesFromIdl(idl: AnchorIdl): Rule[];
|
|
724
|
+
declare function renderRulesYaml(rules: Rule[]): string;
|
|
725
|
+
|
|
726
|
+
type Grade = "A" | "B" | "C" | "D" | "F";
|
|
727
|
+
interface ScoreFactor {
|
|
728
|
+
name: string;
|
|
729
|
+
weight: number;
|
|
730
|
+
points: number;
|
|
731
|
+
detail: string;
|
|
732
|
+
}
|
|
733
|
+
interface TrustScore {
|
|
734
|
+
programId: string;
|
|
735
|
+
resolved: boolean;
|
|
736
|
+
score: number | null;
|
|
737
|
+
grade: Grade | "unrated";
|
|
738
|
+
factors: ScoreFactor[];
|
|
739
|
+
summary: string;
|
|
740
|
+
program?: OnChainProgram;
|
|
741
|
+
unresolvedReason?: string;
|
|
742
|
+
}
|
|
743
|
+
declare function gradeForScore(score: number): Grade;
|
|
744
|
+
declare function scoreFromProgram(program: OnChainProgram): TrustScore;
|
|
745
|
+
declare function scoreProgram(programId: string, opts?: BuildOpts): Promise<TrustScore>;
|
|
746
|
+
declare function renderScoreText(s: TrustScore): string;
|
|
747
|
+
declare function gradeAtLeast(grade: Grade | "unrated", min: Grade): boolean;
|
|
748
|
+
|
|
749
|
+
type ChainEvent = {
|
|
750
|
+
type: "watch_started";
|
|
751
|
+
programId: string;
|
|
752
|
+
headSignature: string | null;
|
|
753
|
+
baselineAuthority: string | null;
|
|
754
|
+
ts: string;
|
|
755
|
+
} | {
|
|
756
|
+
type: "new_activity";
|
|
757
|
+
programId: string;
|
|
758
|
+
newCount: number;
|
|
759
|
+
signatures: string[];
|
|
760
|
+
ts: string;
|
|
761
|
+
} | {
|
|
762
|
+
type: "authority_changed";
|
|
763
|
+
programId: string;
|
|
764
|
+
from: string | null;
|
|
765
|
+
to: string | null;
|
|
766
|
+
ts: string;
|
|
767
|
+
} | {
|
|
768
|
+
type: "poll_error";
|
|
769
|
+
programId: string;
|
|
770
|
+
message: string;
|
|
771
|
+
ts: string;
|
|
772
|
+
};
|
|
773
|
+
interface ChainWatchState {
|
|
774
|
+
lastSignature: string | null;
|
|
775
|
+
baselineAuthority: string | null;
|
|
776
|
+
initialized: boolean;
|
|
777
|
+
}
|
|
778
|
+
interface ChainWatchOpts extends RpcOpts {
|
|
779
|
+
limit?: number;
|
|
780
|
+
intervalMs?: number;
|
|
781
|
+
emit?: (e: ChainEvent) => void;
|
|
782
|
+
probeAuthority?: (programId: string, opts: RpcOpts) => Promise<{
|
|
783
|
+
address: string | null;
|
|
784
|
+
}>;
|
|
785
|
+
}
|
|
786
|
+
declare function pollChainOnce(programId: string, state: ChainWatchState, opts?: ChainWatchOpts): Promise<{
|
|
787
|
+
events: ChainEvent[];
|
|
788
|
+
state: ChainWatchState;
|
|
789
|
+
}>;
|
|
790
|
+
declare function initialChainWatchState(): ChainWatchState;
|
|
791
|
+
declare function startChainWatch(programId: string, opts?: ChainWatchOpts): {
|
|
792
|
+
stop: () => void;
|
|
793
|
+
};
|
|
794
|
+
|
|
795
|
+
type PreflightStatus = "pass" | "warn" | "fail" | "skip";
|
|
796
|
+
type PreflightVerdict = "GO" | "CAUTION" | "NO-GO";
|
|
797
|
+
interface PreflightCheck {
|
|
798
|
+
id: string;
|
|
799
|
+
label: string;
|
|
800
|
+
status: PreflightStatus;
|
|
801
|
+
detail: string;
|
|
802
|
+
}
|
|
803
|
+
interface MintInfo {
|
|
804
|
+
mintAuthorityRevoked: boolean;
|
|
805
|
+
freezeAuthorityRevoked: boolean;
|
|
806
|
+
mintAuthority: string | null;
|
|
807
|
+
freezeAuthority: string | null;
|
|
808
|
+
supply: string;
|
|
809
|
+
decimals: number;
|
|
810
|
+
isInitialized: boolean;
|
|
811
|
+
}
|
|
812
|
+
interface PreflightReport {
|
|
813
|
+
mint: string;
|
|
814
|
+
verdict: PreflightVerdict;
|
|
815
|
+
checks: PreflightCheck[];
|
|
816
|
+
mintInfo?: MintInfo;
|
|
817
|
+
identity?: TokenIdentity;
|
|
818
|
+
quality?: RicoResult;
|
|
819
|
+
}
|
|
820
|
+
declare function parseMintAccount(data: Uint8Array): MintInfo;
|
|
821
|
+
interface PreflightOpts extends RpcOpts {
|
|
822
|
+
apiKey?: string;
|
|
823
|
+
ricoBaseUrl?: string;
|
|
824
|
+
jupBaseUrl?: string;
|
|
825
|
+
offline?: boolean;
|
|
826
|
+
failOnRisk?: number;
|
|
827
|
+
}
|
|
828
|
+
declare function pumpPreflight(mint: string, opts?: PreflightOpts): Promise<PreflightReport>;
|
|
829
|
+
declare function renderPreflightText(r: PreflightReport): string;
|
|
830
|
+
|
|
831
|
+
interface BatchRow {
|
|
832
|
+
mint: string;
|
|
833
|
+
identityStatus: IdentityStatus | "error";
|
|
834
|
+
impersonation: boolean;
|
|
835
|
+
symbol?: string;
|
|
836
|
+
riskScore?: number;
|
|
837
|
+
snipers?: boolean;
|
|
838
|
+
bundleClusters?: boolean;
|
|
839
|
+
deployerFlags?: string[];
|
|
840
|
+
error?: string;
|
|
841
|
+
rank: number;
|
|
842
|
+
}
|
|
843
|
+
interface BatchResult {
|
|
844
|
+
rows: BatchRow[];
|
|
845
|
+
summary: {
|
|
846
|
+
total: number;
|
|
847
|
+
impersonators: number;
|
|
848
|
+
highRisk: number;
|
|
849
|
+
errored: number;
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
interface BatchScanOpts {
|
|
853
|
+
apiKey?: string;
|
|
854
|
+
ricoBaseUrl?: string;
|
|
855
|
+
jupBaseUrl?: string;
|
|
856
|
+
offline?: boolean;
|
|
857
|
+
concurrency?: number;
|
|
858
|
+
failOnRisk?: number;
|
|
859
|
+
}
|
|
860
|
+
declare function batchScan(mints: string[], opts?: BatchScanOpts): Promise<BatchResult>;
|
|
861
|
+
declare function parseMintList(content: string): string[];
|
|
862
|
+
declare function renderBatchText(result: BatchResult): string;
|
|
863
|
+
|
|
864
|
+
export { type AccountFlow, type AnchorIdl, type AuditRef, type BatchResult, type BatchRow, type BatchScanOpts, type BuildOpts, CANONICAL_BY_MINT, CANONICAL_MINTS, type Candidate, type CanonicalMint, type ChainEvent, type ChainWatchOpts, type ChainWatchState, type ChangedRanges, type CheckOutcome, type CheckResult, type CheckResultKind, type Checker, type ConfigCandidate, type ConfigChecker, type CostReport, DEFAULT_REGISTRY_URL, DEFAULT_TTL_HOURS, type DecodedInstruction, type DecodedTx, type DiffResult, type DriftAdvisory, type DriftBaseline, type DriftPackage, type DriftResult, type FirewallFinding, type FirewallOpts, type FirewallProgram, type FirewallReport, type FirewallSeverity, type FirewallVerdict, type Grade, type GraduationEvent, type IdentityStatus, type IdlAccount, type IdlConstraintParams, type IdlInstruction, KNOWN_PROGRAMS, type MintInfo, type OnChainProgram, type OsvAdvisory, PACK_MANIFEST_FILE, type PackInitOptions, type PackManifest, type PackRuleValidation, type PackValidateResult, type ParityNote, type ParsedDiff, type PreflightCheck, type PreflightOpts, type PreflightReport, type PreflightStatus, type PreflightVerdict, type PriorityFeePosture, type ProgramCache, type ProgramCacheEntry, type Recoverability, type RicoOutcome, type RicoResult, type RicoTokenSecurity, type Rule, type RustAccountField, type RustCandidate, type RustChecker, type ScoreFactor, type Severity, type TelemetrySubmitResult, type TokenIdentity, type TrustGraph, type TrustScore, type UpgradeAuthority, type UpgradeAuthorityKind, type UpgradeAuthoritySource, type VerifiedBuildState, type VerifyOpts, type WatchEvent, type WatchOptions, analyzeCosts, analyzeInstructions, analyzeToken, applyDiffToFile, audit, auditWithRule, base58Decode, base58Encode, batchScan, buildConstraintParams, buildTrustGraph, rules as bundledRules, cacheSize, canonicalMintForSymbol, checkDrift, checkerKinds, decodeTransaction, defaultCachePath, deployerFlagsFrom, diffVersions, fileChanged, findCandidates, findConfigCandidates, generateRulesFromIdl, generateTestForResult, getCacheEntry, getCacheEntryMeta, getChangedRanges, getRepoHash, getUserHash, getWorkingTreeChanges, gradeAtLeast, gradeForScore, idlProgramName, initPack, initialChainWatchState, inspectTransaction, isCanonicalMint, isEntryExpired, isTelemetryEnabled, isValidSolanaAddress, lamportsToSol, loadDirectory, loadPack, loadPacksFromDir, loadProgramCache, loadRules, parseCpiPrograms, parseDiff, parseIdl, parseMintAccount, parseMintList, pollChainOnce, pumpPreflight, putCacheEntry, queryOsv, rangeChanged, recordGraduationEvents, renderBatchText, renderCostReportMd, renderDiffMd, renderDiffText, renderDriftText, renderFirewallText, renderPreflightText, renderRicoText, renderRulesYaml, renderScoreText, renderTest, renderTrustGraphMd, rentExemptMinimum, resolveRules, riskScore, runChecker, runIncrementalScan, saveProgramCache, scoreFromProgram, scoreProgram, seedPackages, startChainWatch, startWatch, submitTelemetry, telemetryFilePath, testKinds, toSnakeCase, validatePack, validatePackManifest, verifyTokenIdentity };
|