brainblast 0.2.0 → 0.4.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.
@@ -0,0 +1,179 @@
1
+ # Curated Solana program directory. Human-authored facts; never LLM-edited in
2
+ # place. Every entry is a program brainblast considers "known enough to
3
+ # describe without an RPC call." For anything not in this file, the
4
+ # trust-graph builder falls back to live RPC probing for upgrade authority and
5
+ # reports verifiedBuild=unknown / audits=[] unless the research skill
6
+ # enriches it.
7
+ #
8
+ # Add an entry only when you can cite a primary source for every field.
9
+ # Comments above each entry name those sources.
10
+
11
+ programs:
12
+ # System Program — owns wallets, transfers SOL, allocates accounts. Core
13
+ # runtime; not an upgradeable program. https://docs.solana.com/runtime/programs
14
+ - programId: "11111111111111111111111111111111"
15
+ name: System Program
16
+ kind: runtime
17
+ upgradeAuthority:
18
+ kind: renounced
19
+ address: null
20
+ source: directory
21
+ verifiedBuild:
22
+ state: verified
23
+ registryUrl: https://github.com/anza-xyz/agave
24
+ audits: []
25
+ parity:
26
+ mainnet: present
27
+ devnet: present
28
+ testnet: present
29
+ notes: Identical on every cluster; part of the validator binary.
30
+
31
+ # SPL Token — the legacy SPL token program. Native, non-upgradeable.
32
+ # https://spl.solana.com/token
33
+ - programId: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
34
+ name: SPL Token
35
+ kind: token
36
+ upgradeAuthority:
37
+ kind: renounced
38
+ address: null
39
+ source: directory
40
+ verifiedBuild:
41
+ state: verified
42
+ registryUrl: https://github.com/solana-program/token
43
+ audits:
44
+ - firm: Kudelski Security
45
+ date: "2020-08-01"
46
+ reportUrl: https://github.com/solana-program/token/tree/main/audits
47
+ parity:
48
+ mainnet: present
49
+ devnet: present
50
+ testnet: present
51
+ notes: Same address everywhere. Distinct from Token-2022; consumers must pick one and never mix.
52
+
53
+ # SPL Token-2022 — the new token program with transfer hooks, fees, and
54
+ # confidential transfers. DIFFERENT program id from legacy Token; mixing the
55
+ # two is the #1 trap in the deep-dive research.
56
+ # https://spl.solana.com/token-2022
57
+ - programId: "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"
58
+ name: SPL Token-2022
59
+ kind: token
60
+ upgradeAuthority:
61
+ kind: multisig
62
+ address: "9YBjtad6ZxR7hxNXyTjRRPnPipFbuAU6c2EVHTSVTQAW"
63
+ source: directory
64
+ checkedAt: "2025-04-15"
65
+ verifiedBuild:
66
+ state: verified
67
+ registryUrl: https://github.com/solana-program/token-2022
68
+ audits:
69
+ - firm: OtterSec
70
+ date: "2024-02-01"
71
+ reportUrl: https://github.com/solana-program/token-2022/tree/main/audits
72
+ parity:
73
+ mainnet: present
74
+ devnet: present
75
+ testnet: present
76
+ notes: >-
77
+ Same address on every cluster. Behavior identical, but extensions like transfer-fee
78
+ and transfer-hook can encode mainnet-only economics that don't show up in devnet
79
+ testing of a fresh mint.
80
+
81
+ # Metaplex Token Metadata. The metadata account for every NFT and most
82
+ # SPL-token branding goes through this program. isMutable / collection
83
+ # verification are immutable-after-mint decisions worth flagging.
84
+ # https://developers.metaplex.com/token-metadata
85
+ - programId: "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"
86
+ name: Metaplex Token Metadata
87
+ kind: metadata
88
+ upgradeAuthority:
89
+ kind: multisig
90
+ address: "BFV9MAfDjT8azkAArh9k4iAjwboTBA9z3PNK7BFNKaiK"
91
+ source: directory
92
+ checkedAt: "2025-04-15"
93
+ verifiedBuild:
94
+ state: verified
95
+ registryUrl: https://github.com/metaplex-foundation/mpl-token-metadata
96
+ audits:
97
+ - firm: OtterSec
98
+ date: "2023-09-01"
99
+ reportUrl: https://github.com/metaplex-foundation/mpl-token-metadata/tree/main/programs/token-metadata/audits
100
+ parity:
101
+ mainnet: present
102
+ devnet: present
103
+ testnet: present
104
+ notes: Same address everywhere; metadata is the standard NFT/token-info layer.
105
+
106
+ # Memo Program. Lets transactions include UTF-8 notes; no state. Useful in
107
+ # CPI but trivially safe.
108
+ - programId: "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"
109
+ name: SPL Memo
110
+ kind: utility
111
+ upgradeAuthority:
112
+ kind: renounced
113
+ address: null
114
+ source: directory
115
+ verifiedBuild:
116
+ state: verified
117
+ registryUrl: https://github.com/solana-program/memo
118
+ audits: []
119
+ parity:
120
+ mainnet: present
121
+ devnet: present
122
+ testnet: present
123
+
124
+ # Compute Budget Program. Lets a tx set its compute unit price/limit. Pure
125
+ # runtime; not upgradeable. Worth knowing for fee posture in the cost report.
126
+ - programId: "ComputeBudget111111111111111111111111111111"
127
+ name: Compute Budget Program
128
+ kind: runtime
129
+ upgradeAuthority:
130
+ kind: renounced
131
+ address: null
132
+ source: directory
133
+ verifiedBuild:
134
+ state: verified
135
+ registryUrl: https://github.com/anza-xyz/agave
136
+ audits: []
137
+ parity:
138
+ mainnet: present
139
+ devnet: present
140
+ testnet: present
141
+
142
+ # Address Lookup Table Program. Lets transactions reference more accounts
143
+ # than the legacy 32-account limit allows.
144
+ - programId: "AddressLookupTab1e1111111111111111111111111"
145
+ name: Address Lookup Table Program
146
+ kind: runtime
147
+ upgradeAuthority:
148
+ kind: renounced
149
+ address: null
150
+ source: directory
151
+ verifiedBuild:
152
+ state: verified
153
+ registryUrl: https://github.com/anza-xyz/agave
154
+ audits: []
155
+ parity:
156
+ mainnet: present
157
+ devnet: present
158
+ testnet: present
159
+
160
+ # BPF Upgradeable Loader. The program that owns every upgradeable program;
161
+ # `setAuthority` and `upgrade` instructions go through here. The trust
162
+ # graph routinely reads ProgramData accounts owned by this address to
163
+ # resolve the upgrade authority of any other upgradeable program.
164
+ - programId: "BPFLoaderUpgradeab1e11111111111111111111111"
165
+ name: BPF Upgradeable Loader
166
+ kind: loader
167
+ upgradeAuthority:
168
+ kind: renounced
169
+ address: null
170
+ source: directory
171
+ verifiedBuild:
172
+ state: verified
173
+ registryUrl: https://github.com/anza-xyz/agave
174
+ audits: []
175
+ parity:
176
+ mainnet: present
177
+ devnet: present
178
+ testnet: present
179
+ notes: The loader itself is part of the validator and not upgradeable in the BPF sense.
@@ -0,0 +1,47 @@
1
+ # Pure-data rule (facts). Binds to vetted templates by `check.kind`/`test.kind`.
2
+ # Promoted from findings/anchor-init-if-needed-reinit.json after the
3
+ # proof-as-classifier loop (synth-prove) closed RED->GREEN against the
4
+ # new `anchor-init-if-needed-guarded` checker kind (tree-sitter-rust based).
5
+ id: anchor-init-if-needed-guarded
6
+ severity: critical
7
+ title: Anchor init_if_needed account has a reinitialization guard
8
+ component:
9
+ name: Anchor Framework
10
+ type: Blockchain
11
+ version: ">=0.26.0"
12
+ sourceUrl: https://www.anchor-lang.com/docs/the-accounts-struct#init_if_needed
13
+ detect:
14
+ # This rule operates on Rust source files — requires tree-sitter-rust.
15
+ lang: rust
16
+ modules:
17
+ - "@coral-xyz/anchor"
18
+ - "@project-serum/anchor"
19
+ # Match instruction handlers that initialize accounts. Detection also
20
+ # triggers on any function whose Accounts struct has init_if_needed.
21
+ nameRegex: "initialize|init_if_needed"
22
+ triggerCalls:
23
+ - init_if_needed
24
+ check:
25
+ kind: anchor-init-if-needed-guarded
26
+ params:
27
+ passDetail: >-
28
+ Handler has #[account(init_if_needed)] and a reinitialization guard
29
+ (require!, data_is_empty, or is_initialized check). A second invocation
30
+ will be rejected before writing to the account.
31
+ failAbsentDetail: >-
32
+ Handler uses #[account(init_if_needed)] without a reinitialization guard.
33
+ A malicious caller can invoke the instruction a second time to overwrite
34
+ the account's data — a full state-wipe with no on-chain recourse.
35
+ Add: require!(account_field.field == initial_value, ErrorCode::AlreadyInitialized)
36
+ at the top of the handler body.
37
+ absentDetail: >-
38
+ Handler has no #[account(init_if_needed)] fields; the reinit-guard rule
39
+ does not apply.
40
+ test:
41
+ kind: anchor-program-test
42
+ params:
43
+ scenario: reinit-attempt
44
+ description: >-
45
+ Calls the instruction twice on the same account; the second call must
46
+ fail with AlreadyInitialized (or equivalent). If it succeeds, the
47
+ account state has been silently overwritten.
@@ -9,7 +9,12 @@ component:
9
9
  sourceUrl: https://docs.bags.fm/changelog/changelog
10
10
  detect:
11
11
  modules: ["@bagsfm/bags-sdk"]
12
- nameRegex: "feeshare|feeclaimer|bagsfee|launchtoken|tokenlaunch"
12
+ # Keep the regex tight to Bags-specific terms; broad terms like
13
+ # "launchtoken"/"tokenlaunch" cross-matched non-Bags Solana handlers
14
+ # (e.g. Token-2022 launch helpers) and produced spurious "no Bags fee
15
+ # config" failures. Bags candidates still resolve via the import or the
16
+ # createBagsFeeShareConfig trigger call.
17
+ nameRegex: "feeshare|feeclaimer|bagsfee"
13
18
  triggerCalls: [createBagsFeeShareConfig]
14
19
  check:
15
20
  kind: fee-allocation-shape
@@ -0,0 +1,55 @@
1
+ # Pure-data rule (facts). Binds to vetted templates by `check.kind`/`test.kind`.
2
+ # Promoted from findings/metaplex-metadata-immutable.json after the
3
+ # proof-as-classifier loop (synth-prove) closed RED->GREEN against the
4
+ # new `object-arg-property-literal-equals` checker kind.
5
+ id: metaplex-metadata-immutable
6
+ severity: critical
7
+ title: Metaplex createV1 called with isMutable false to seal token metadata at mint
8
+ component:
9
+ name: Metaplex Token Metadata
10
+ type: Blockchain
11
+ version: unversioned
12
+ sourceUrl: https://developers.metaplex.com/token-metadata
13
+ detect:
14
+ modules: ["@metaplex-foundation/mpl-token-metadata"]
15
+ # Brand-specific terms only. Bare "token"/"mint" cross-match SPL-token,
16
+ # Bags, and other handlers. Metaplex candidates still resolve via the
17
+ # @metaplex-foundation/mpl-token-metadata import or the createV1/createNft
18
+ # trigger calls.
19
+ nameRegex: "metaplex|mpl|createNft"
20
+ triggerCalls: [createV1, createNft, createAndMint, createMetadataAccountV3]
21
+ check:
22
+ kind: object-arg-property-literal-equals
23
+ params:
24
+ call: createV1
25
+ argIndex: 1
26
+ propName: isMutable
27
+ expectedValue: false
28
+ passDetail: >-
29
+ createV1 passes isMutable: false — token metadata is permanently sealed
30
+ at mint time. Name, symbol, and URI cannot be changed regardless of who
31
+ holds the update authority.
32
+ failAbsentDetail: >-
33
+ createV1 omits isMutable (SDK defaults to true). The update authority
34
+ retains the ability to change the token name, symbol, and URI after mint —
35
+ permanently. Seal the metadata by passing isMutable: false. There is no
36
+ on-chain migration path once metadata is minted as mutable.
37
+ failWrongDetail: >-
38
+ createV1 passes isMutable: true explicitly. Token metadata will remain
39
+ mutable after mint — the update authority key becomes a permanent attack
40
+ surface. Pass isMutable: false to seal it at mint time.
41
+ failArgDetail: >-
42
+ createV1's options argument is not an inline object literal; cannot
43
+ statically verify isMutable. Inline the options object so static analysis
44
+ can confirm isMutable: false.
45
+ failDynamicDetail: >-
46
+ isMutable is set via a variable or expression; cannot confirm it resolves
47
+ to false statically. Use the boolean literal false directly.
48
+ absentCallDetail: >-
49
+ Handler is in Metaplex scope but does not call createV1; the
50
+ metadata-immutability rule does not apply here.
51
+ scopeNotMetDetail: >-
52
+ File does not import from @metaplex-foundation/mpl-token-metadata; the
53
+ metadata-immutability rule does not apply.
54
+ test:
55
+ kind: metaplex-immutable-metadata
@@ -9,8 +9,24 @@ component:
9
9
  sourceUrl: https://docs.privy.io/authentication/user-authentication/access-tokens
10
10
  detect:
11
11
  modules: [jose, jsonwebtoken, "@privy-io/node", "@privy-io/server-auth"]
12
- nameRegex: "token|auth|verify|privy|jwt"
13
- triggerCalls: [decodeJwt, jwtVerify, verify, decode]
12
+ # Brand-specific terms only. Bare "token"/"auth" cross-match unrelated
13
+ # Solana handlers (launchToken, etc.) and produce spurious cant_tell noise.
14
+ # Privy handlers are still picked up by the @privy-io/* / jose / jsonwebtoken
15
+ # imports, and by triggerCalls like decodeJwt / jwtVerify.
16
+ nameRegex: "privy|jwt"
17
+ # Only decodeJwt triggers detection; it's specific to jose and unambiguously
18
+ # indicates a decode-only pattern. jwtVerify/verify/decode were removed because:
19
+ # - "verify" matches Node.js crypto.verify(), jsonwebtoken jwt.verify(), etc.
20
+ # - "decode" matches bs58.decode() (Solana), Buffer.from(), etc.
21
+ # - "jwtVerify" matches Fastify's request.jwtVerify() plugin method, which is
22
+ # not a jose-based call and requires no audience/issuer options.
23
+ # Functions that import jose/@privy-io/* and are named with "privy|jwt" are still
24
+ # detected via requiresImport + (importsModule && hasName), the primary detection path.
25
+ triggerCalls: [decodeJwt]
26
+ # A module import is REQUIRED to be a candidate. Without this guard, a Fastify
27
+ # middleware named "verifyJwt" (which calls request.jwtVerify(), a completely
28
+ # different library) would match the nameRegex and be flagged as missing aud/iss.
29
+ requiresImport: true
14
30
  check:
15
31
  kind: required-call-with-options
16
32
  params:
@@ -11,6 +11,11 @@ detect:
11
11
  modules: [stripe]
12
12
  nameRegex: webhook
13
13
  triggerCalls: [constructEvent]
14
+ # Require the file to import stripe. Without this guard, any function whose
15
+ # name contains "webhook" — even LemonSqueezy, Polar, or Sendgrid handlers —
16
+ # is flagged for missing stripe.webhooks.constructEvent, producing false
17
+ # positives on non-Stripe payment integrations.
18
+ requiresImport: true
14
19
  check:
15
20
  kind: positional-arg-identity
16
21
  params:
@@ -0,0 +1,55 @@
1
+ # Pure-data rule (facts). Binds to vetted templates by `check.kind`/`test.kind`.
2
+ # Promoted from findings/token-2022-program-id-mismatch.json after the
3
+ # proof-as-classifier loop (synth-prove) closed RED->GREEN against the
4
+ # new `arg-equals-constant-identifier` checker kind.
5
+ id: token-2022-program-id-pinned
6
+ severity: critical
7
+ title: Token-2022 createMint passes TOKEN_2022_PROGRAM_ID as the program ID argument
8
+ component:
9
+ name: SPL Token-2022
10
+ type: Blockchain
11
+ version: unversioned
12
+ sourceUrl: https://spl.solana.com/token-2022
13
+ detect:
14
+ modules: ["@solana/spl-token"]
15
+ # Keep the regex narrow to terms specific to SPL-token-mint construction;
16
+ # bare "token"/"launch" would cross-match unrelated handlers (Privy
17
+ # verifyPrivyToken, etc.). Real Token-2022 handlers still resolve via the
18
+ # @solana/spl-token import or the createMint trigger call.
19
+ nameRegex: "createMint|mint22|token2022"
20
+ triggerCalls: [createMint]
21
+ check:
22
+ kind: arg-equals-constant-identifier
23
+ params:
24
+ call: createMint
25
+ argIndex: 7
26
+ expectedIdentifier: TOKEN_2022_PROGRAM_ID
27
+ forbiddenIdentifiers: [TOKEN_PROGRAM_ID]
28
+ # Scope predicate: only enforce when the file imports TOKEN_2022_PROGRAM_ID.
29
+ # Without this, every legacy-mint codebase would false-positive.
30
+ requireImport: TOKEN_2022_PROGRAM_ID
31
+ passDetail: >-
32
+ createMint passes {expected} as its programId argument — Token-2022
33
+ features will work as designed.
34
+ failForbiddenDetail: >-
35
+ createMint passes {got} (legacy Token program) where Token-2022 was
36
+ intended. The mint will be owned by the legacy program; Token-2022
37
+ features (transfer hooks, transfer fees, confidential transfers) will
38
+ not work and there is NO on-chain fix — a mint's owner program is part
39
+ of its identity.
40
+ failMissingDetail: >-
41
+ createMint omits its programId argument (or passes undefined), which
42
+ silently defaults to the LEGACY Token program even though the file
43
+ imports {expected}. Pass {expected} explicitly.
44
+ failOtherDetail: >-
45
+ createMint passes '{got}' as programId; expected {expected}. If this is
46
+ a custom constant, alias it to {expected} or pass the canonical name so
47
+ the trap detector and downstream readers agree.
48
+ absentCallDetail: >-
49
+ Handler is in the SPL-token scope but does not call createMint; the
50
+ Token-2022 pin rule does not apply here.
51
+ scopeNotMetDetail: >-
52
+ File does not import TOKEN_2022_PROGRAM_ID; legacy-token codebase — the
53
+ Token-2022 pin rule does not apply.
54
+ test:
55
+ kind: token-program-consistency
package/package.json CHANGED
@@ -1,35 +1,76 @@
1
1
  {
2
2
  "name": "brainblast",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "description": "Deterministic auditor for catastrophic AI-integration bugs: scan a repo, find the silent money/auth traps, and generate the behavioral test that proves they're fixed.",
6
- "keywords": ["security", "static-analysis", "stripe", "webhook", "jwt", "solana", "bags", "ci", "ai", "audit"],
6
+ "keywords": [
7
+ "security",
8
+ "static-analysis",
9
+ "audit",
10
+ "stripe",
11
+ "webhook",
12
+ "jwt",
13
+ "solana",
14
+ "anchor",
15
+ "metaplex",
16
+ "token-2022",
17
+ "trust-graph",
18
+ "bags",
19
+ "rent",
20
+ "ci",
21
+ "ai"
22
+ ],
7
23
  "license": "MIT",
8
24
  "author": "DSB-117",
9
25
  "homepage": "https://github.com/DSB-117/brainblast/tree/main/packages/core#readme",
10
- "bugs": { "url": "https://github.com/DSB-117/brainblast/issues" },
11
- "repository": { "type": "git", "url": "git+https://github.com/DSB-117/brainblast.git", "directory": "packages/core" },
12
- "bin": { "brainblast": "dist/cli.js" },
26
+ "bugs": {
27
+ "url": "https://github.com/DSB-117/brainblast/issues"
28
+ },
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/DSB-117/brainblast.git",
32
+ "directory": "packages/core"
33
+ },
34
+ "bin": {
35
+ "brainblast": "dist/cli.js"
36
+ },
13
37
  "exports": {
14
- ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" }
38
+ ".": {
39
+ "types": "./dist/index.d.ts",
40
+ "import": "./dist/index.js"
41
+ }
42
+ },
43
+ "files": [
44
+ "dist",
45
+ "README.md"
46
+ ],
47
+ "engines": {
48
+ "node": ">=18"
49
+ },
50
+ "publishConfig": {
51
+ "access": "public",
52
+ "provenance": true
15
53
  },
16
- "files": ["dist", "README.md"],
17
- "engines": { "node": ">=18" },
18
- "publishConfig": { "access": "public", "provenance": true },
19
54
  "scripts": {
20
55
  "build": "tsup",
21
56
  "prepublishOnly": "npm run typecheck && npm test && npm run build",
22
57
  "audit": "tsx src/cli.ts",
23
58
  "prove": "tsx scripts/prove.ts",
59
+ "synth": "tsx scripts/synth-prove.ts",
60
+ "synth:bags": "tsx scripts/synth-prove.ts findings/bags-known-answer.json",
24
61
  "test": "vitest run",
25
62
  "coverage": "vitest run --coverage",
26
63
  "typecheck": "tsc --noEmit -p tsconfig.json"
27
64
  },
28
65
  "dependencies": {
66
+ "tree-sitter": "^0.22.4",
67
+ "tree-sitter-rust": "^0.24.0",
29
68
  "ts-morph": "^23",
30
69
  "yaml": "^2"
31
70
  },
32
71
  "devDependencies": {
72
+ "@metaplex-foundation/mpl-token-metadata": "^3.4.0",
73
+ "@solana/spl-token": "^0.4.14",
33
74
  "@types/node": "^22",
34
75
  "@vitest/coverage-v8": "^2",
35
76
  "jose": "^5",