brainblast 0.5.1 → 0.5.3

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.
@@ -421,6 +421,66 @@ var objectArgPropertyLiteralEquals = (c, p) => {
421
421
  };
422
422
  };
423
423
 
424
+ // src/checkers/objectArgPropertyForbiddenLiteral.ts
425
+ import { SyntaxKind as SyntaxKind7 } from "ts-morph";
426
+ var objectArgPropertyForbiddenLiteral = (c, p) => {
427
+ const calls = c.fn.getDescendantsOfKind(SyntaxKind7.CallExpression).filter((ce2) => {
428
+ const expr = ce2.getExpression();
429
+ if (expr.getKind() === SyntaxKind7.Identifier) return expr.getText() === p.call;
430
+ if (expr.getKind() === SyntaxKind7.PropertyAccessExpression) {
431
+ return expr.asKind(SyntaxKind7.PropertyAccessExpression).getName() === p.call;
432
+ }
433
+ return false;
434
+ });
435
+ if (calls.length === 0) {
436
+ return {
437
+ result: "cant_tell",
438
+ detail: p.absentCallDetail ?? `no ${p.call} call found`
439
+ };
440
+ }
441
+ const ce = calls[0];
442
+ const args = ce.getArguments();
443
+ const arg = args[p.argIndex];
444
+ const objLit = arg?.asKind(SyntaxKind7.ObjectLiteralExpression);
445
+ if (!objLit) {
446
+ return {
447
+ result: "cant_tell",
448
+ detail: p.absentArgDetail ?? `${p.call} arg[${p.argIndex}] is not an inline object literal \u2014 cannot statically inspect ${p.propName}`
449
+ };
450
+ }
451
+ const propAssignment = objLit.getProperties().map((prop) => prop.asKind(SyntaxKind7.PropertyAssignment)).find((pa) => pa?.getName() === p.propName);
452
+ if (!propAssignment) {
453
+ return {
454
+ result: "cant_tell",
455
+ detail: p.absentArgDetail ?? `${p.propName} is absent from the ${p.call} options`
456
+ };
457
+ }
458
+ const init = propAssignment.getInitializer();
459
+ if (!init) {
460
+ return { result: "cant_tell", detail: `${p.propName} has no initializer` };
461
+ }
462
+ const kind = init.getKind();
463
+ const text = init.getText();
464
+ const forbidden = JSON.stringify(p.forbiddenValue);
465
+ const isForbidden = (kind === SyntaxKind7.NumericLiteral || kind === SyntaxKind7.StringLiteral) && (text === forbidden || text === String(p.forbiddenValue));
466
+ if (isForbidden) {
467
+ return {
468
+ result: "fail",
469
+ detail: p.failDetail ?? `${p.propName} is ${p.forbiddenValue}`
470
+ };
471
+ }
472
+ if (kind === SyntaxKind7.NumericLiteral || kind === SyntaxKind7.StringLiteral) {
473
+ return {
474
+ result: "pass",
475
+ detail: p.passDetail ?? `${p.propName} is ${text}`
476
+ };
477
+ }
478
+ return {
479
+ result: "cant_tell",
480
+ detail: `${p.propName} is a non-literal expression \u2014 cannot determine statically`
481
+ };
482
+ };
483
+
424
484
  // src/checkers/anchorInitIfNeededGuarded.ts
425
485
  var GUARD_PATTERNS = [
426
486
  /\brequire!\s*\(/,
@@ -488,18 +548,18 @@ var envSecretsCommitted = (c, p) => {
488
548
  };
489
549
 
490
550
  // src/checkers/taintToSink.ts
491
- import { SyntaxKind as SyntaxKind7 } from "ts-morph";
551
+ import { SyntaxKind as SyntaxKind8 } from "ts-morph";
492
552
  function calleeName(call) {
493
553
  const exp = call.getExpression();
494
- if (exp.getKind() === SyntaxKind7.Identifier) return exp.getText();
495
- if (exp.getKind() === SyntaxKind7.PropertyAccessExpression) {
496
- return exp.asKind(SyntaxKind7.PropertyAccessExpression).getName();
554
+ if (exp.getKind() === SyntaxKind8.Identifier) return exp.getText();
555
+ if (exp.getKind() === SyntaxKind8.PropertyAccessExpression) {
556
+ return exp.asKind(SyntaxKind8.PropertyAccessExpression).getName();
497
557
  }
498
558
  return "";
499
559
  }
500
560
  function calleeIdentifierName(call) {
501
561
  const exp = call.getExpression();
502
- return exp.getKind() === SyntaxKind7.Identifier ? exp.getText() : void 0;
562
+ return exp.getKind() === SyntaxKind8.Identifier ? exp.getText() : void 0;
503
563
  }
504
564
  function wordIn(text, name) {
505
565
  return new RegExp(`\\b${name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`).test(text);
@@ -509,14 +569,14 @@ function matchesSource(text, sourceRes) {
509
569
  }
510
570
  function localTaintedNames(fn, sourceRes) {
511
571
  const names = /* @__PURE__ */ new Set();
512
- for (const decl of fn.getDescendantsOfKind(SyntaxKind7.VariableDeclaration)) {
572
+ for (const decl of fn.getDescendantsOfKind(SyntaxKind8.VariableDeclaration)) {
513
573
  const init = decl.getInitializer();
514
574
  if (init && matchesSource(init.getText(), sourceRes)) names.add(decl.getName());
515
575
  }
516
576
  return names;
517
577
  }
518
578
  function findDirectLeak(fn, sinkCalls, sourceRes, taintedNames) {
519
- for (const call of fn.getDescendantsOfKind(SyntaxKind7.CallExpression)) {
579
+ for (const call of fn.getDescendantsOfKind(SyntaxKind8.CallExpression)) {
520
580
  const name = calleeName(call);
521
581
  if (!sinkCalls.has(name)) continue;
522
582
  for (const arg of call.getArguments()) {
@@ -548,7 +608,7 @@ function resolveFunction(sourceFile, name) {
548
608
  }
549
609
  function findForwardLeak(fn, rootName, sinkCalls, sourceRes, taintedNames, hopsLeft, visited) {
550
610
  if (hopsLeft <= 0) return void 0;
551
- for (const call of fn.getDescendantsOfKind(SyntaxKind7.CallExpression)) {
611
+ for (const call of fn.getDescendantsOfKind(SyntaxKind8.CallExpression)) {
552
612
  const name = calleeIdentifierName(call);
553
613
  if (!name || name === rootName) continue;
554
614
  const args = call.getArguments();
@@ -590,7 +650,7 @@ function findForwardLeak(fn, rootName, sinkCalls, sourceRes, taintedNames, hopsL
590
650
  function paramsUsedInSink(fn, sinkCalls) {
591
651
  const params = new Set(fn.getParameters().map((p) => p.getName()));
592
652
  const sinked = /* @__PURE__ */ new Set();
593
- for (const call of fn.getDescendantsOfKind(SyntaxKind7.CallExpression)) {
653
+ for (const call of fn.getDescendantsOfKind(SyntaxKind8.CallExpression)) {
594
654
  if (!sinkCalls.has(calleeName(call))) continue;
595
655
  for (const arg of call.getArguments()) {
596
656
  const text = arg.getText();
@@ -603,13 +663,13 @@ function paramsUsedInSink(fn, sinkCalls) {
603
663
  }
604
664
  function enclosingFunction(node) {
605
665
  return node.getFirstAncestor(
606
- (a) => a.getKind() === SyntaxKind7.FunctionDeclaration || a.getKind() === SyntaxKind7.ArrowFunction
666
+ (a) => a.getKind() === SyntaxKind8.FunctionDeclaration || a.getKind() === SyntaxKind8.ArrowFunction
607
667
  );
608
668
  }
609
669
  function findBackwardLeak(candidateFn, fnName, candidateFile, params, sinkedParams, sourceRes) {
610
670
  const project = candidateFn.getProject();
611
671
  for (const sf of project.getSourceFiles()) {
612
- for (const call of sf.getDescendantsOfKind(SyntaxKind7.CallExpression)) {
672
+ for (const call of sf.getDescendantsOfKind(SyntaxKind8.CallExpression)) {
613
673
  if (calleeIdentifierName(call) !== fnName) continue;
614
674
  if (call.getFirstAncestor((a) => a === candidateFn)) continue;
615
675
  const args = call.getArguments();
@@ -621,7 +681,7 @@ function findBackwardLeak(candidateFn, fnName, candidateFile, params, sinkedPara
621
681
  if (matchesSource(text, sourceRes)) {
622
682
  return `'${fnName}' is called from ${sf.getFilePath()}:${call.getStartLineNumber()} with '${text}' as '${pname}', which this function passes to a sink.`;
623
683
  }
624
- if (arg.getKind() === SyntaxKind7.Identifier) {
684
+ if (arg.getKind() === SyntaxKind8.Identifier) {
625
685
  const callerFn = enclosingFunction(arg);
626
686
  if (callerFn) {
627
687
  const callerTainted = localTaintedNames(callerFn, sourceRes);
@@ -663,21 +723,21 @@ var taintToSink = (c, p) => {
663
723
  };
664
724
 
665
725
  // src/checkers/literalMultiplierWrongConstant.ts
666
- import { SyntaxKind as SyntaxKind8 } from "ts-morph";
726
+ import { SyntaxKind as SyntaxKind9 } from "ts-morph";
667
727
  function callName4(call) {
668
728
  const exp = call.getExpression();
669
- if (exp.getKind() === SyntaxKind8.Identifier) return exp.getText();
670
- if (exp.getKind() === SyntaxKind8.PropertyAccessExpression) {
671
- return exp.asKind(SyntaxKind8.PropertyAccessExpression).getName();
729
+ if (exp.getKind() === SyntaxKind9.Identifier) return exp.getText();
730
+ if (exp.getKind() === SyntaxKind9.PropertyAccessExpression) {
731
+ return exp.asKind(SyntaxKind9.PropertyAccessExpression).getName();
672
732
  }
673
733
  return "";
674
734
  }
675
735
  function containsIdentifier(node, name) {
676
- if (node.getKind() === SyntaxKind8.Identifier && node.getText() === name) return true;
677
- return node.getDescendantsOfKind(SyntaxKind8.Identifier).some((id) => id.getText() === name);
736
+ if (node.getKind() === SyntaxKind9.Identifier && node.getText() === name) return true;
737
+ return node.getDescendantsOfKind(SyntaxKind9.Identifier).some((id) => id.getText() === name);
678
738
  }
679
739
  var literalMultiplierWrongConstant = (c, p) => {
680
- const calls = c.fn.getDescendantsOfKind(SyntaxKind8.CallExpression).filter((x) => callName4(x) === p.call);
740
+ const calls = c.fn.getDescendantsOfKind(SyntaxKind9.CallExpression).filter((x) => callName4(x) === p.call);
681
741
  if (calls.length === 0) {
682
742
  return { result: "cant_tell", detail: p.absentCallDetail };
683
743
  }
@@ -700,6 +760,35 @@ var literalMultiplierWrongConstant = (c, p) => {
700
760
  return { result: "cant_tell", detail: p.cantTellDetail };
701
761
  };
702
762
 
763
+ // src/checkers/forbiddenCallReplacement.ts
764
+ import { SyntaxKind as SyntaxKind10 } from "ts-morph";
765
+ function callName5(call) {
766
+ const exp = call.getExpression();
767
+ if (exp.getKind() === SyntaxKind10.Identifier) return exp.getText();
768
+ if (exp.getKind() === SyntaxKind10.PropertyAccessExpression) {
769
+ return exp.asKind(SyntaxKind10.PropertyAccessExpression).getName();
770
+ }
771
+ return "";
772
+ }
773
+ var forbiddenCallReplacement = (c, p) => {
774
+ const calls = c.fn.getDescendantsOfKind(SyntaxKind10.CallExpression);
775
+ const names = calls.map(callName5);
776
+ const forbidden = p.forbiddenCalls ?? [];
777
+ const safer = p.saferCalls ?? [];
778
+ const usesSafer = names.some((n) => safer.includes(n));
779
+ if (usesSafer) {
780
+ return { result: "pass", detail: p.passDetail ?? `uses ${safer.join("/")}` };
781
+ }
782
+ const usesForbidden = names.some((n) => forbidden.includes(n));
783
+ if (usesForbidden) {
784
+ return { result: "fail", detail: p.failDetail ?? `uses ${forbidden.join("/")}` };
785
+ }
786
+ return {
787
+ result: "cant_tell",
788
+ detail: p.absentDetail ?? `neither ${forbidden.join("/")} nor ${safer.join("/")} found`
789
+ };
790
+ };
791
+
703
792
  // src/checkers/index.ts
704
793
  var registry = {
705
794
  "positional-arg-identity": positionalArgIdentity,
@@ -707,10 +796,12 @@ var registry = {
707
796
  "fee-allocation-shape": feeAllocationShape,
708
797
  "arg-equals-constant-identifier": argEqualsConstantIdentifier,
709
798
  "object-arg-property-literal-equals": objectArgPropertyLiteralEquals,
799
+ "object-arg-property-forbidden-literal": objectArgPropertyForbiddenLiteral,
710
800
  "anchor-init-if-needed-guarded": anchorInitIfNeededGuarded,
711
801
  "env-secrets-committed": envSecretsCommitted,
712
802
  "taint-to-sink": taintToSink,
713
- "literal-multiplier-wrong-constant": literalMultiplierWrongConstant
803
+ "literal-multiplier-wrong-constant": literalMultiplierWrongConstant,
804
+ "forbidden-call-replacement": forbiddenCallReplacement
714
805
  };
715
806
  function runChecker(kind, c, params) {
716
807
  const fn = registry[kind];
@@ -926,7 +1017,7 @@ function findRustCandidates(targetDir, rule) {
926
1017
  }
927
1018
 
928
1019
  // src/fixers/positionalArgIdentity.ts
929
- import { SyntaxKind as SyntaxKind9 } from "ts-morph";
1020
+ import { SyntaxKind as SyntaxKind11 } from "ts-morph";
930
1021
 
931
1022
  // src/fixers/diffUtil.ts
932
1023
  function buildDiff(node, replacement) {
@@ -951,9 +1042,9 @@ function buildDiff(node, replacement) {
951
1042
  // src/fixers/positionalArgIdentity.ts
952
1043
  var fixPositionalArgIdentity = (c, p, outcome) => {
953
1044
  if (outcome.result !== "fail") return void 0;
954
- const calls = c.fn.getDescendantsOfKind(SyntaxKind9.CallExpression).filter((call) => {
1045
+ const calls = c.fn.getDescendantsOfKind(SyntaxKind11.CallExpression).filter((call) => {
955
1046
  const exp = call.getExpression();
956
- return exp.getKind() === SyntaxKind9.PropertyAccessExpression && exp.asKind(SyntaxKind9.PropertyAccessExpression).getName() === p.call;
1047
+ return exp.getKind() === SyntaxKind11.PropertyAccessExpression && exp.asKind(SyntaxKind11.PropertyAccessExpression).getName() === p.call;
957
1048
  });
958
1049
  if (calls.length === 0) {
959
1050
  const wantParam2 = c.params[p.paramIndex] ?? "<rawBodyParam>";
@@ -968,7 +1059,7 @@ Do not call JSON.parse() on the body before this verification step.`
968
1059
  }
969
1060
  const arg = calls[0].getArguments()[p.argIndex];
970
1061
  const wantParam = c.params[p.paramIndex];
971
- if (arg && wantParam && arg.getKind() === SyntaxKind9.CallExpression) {
1062
+ if (arg && wantParam && arg.getKind() === SyntaxKind11.CallExpression) {
972
1063
  return {
973
1064
  summary: `Pass the raw body parameter '${wantParam}' to ${p.call} instead of a parsed value`,
974
1065
  diff: buildDiff(arg, wantParam)
@@ -978,12 +1069,12 @@ Do not call JSON.parse() on the body before this verification step.`
978
1069
  };
979
1070
 
980
1071
  // src/fixers/requiredCallWithOptions.ts
981
- import { SyntaxKind as SyntaxKind10 } from "ts-morph";
982
- function callName5(call) {
1072
+ import { SyntaxKind as SyntaxKind12 } from "ts-morph";
1073
+ function callName6(call) {
983
1074
  const exp = call.getExpression();
984
- if (exp.getKind() === SyntaxKind10.Identifier) return exp.getText();
985
- if (exp.getKind() === SyntaxKind10.PropertyAccessExpression) {
986
- return exp.asKind(SyntaxKind10.PropertyAccessExpression).getName();
1075
+ if (exp.getKind() === SyntaxKind12.Identifier) return exp.getText();
1076
+ if (exp.getKind() === SyntaxKind12.PropertyAccessExpression) {
1077
+ return exp.asKind(SyntaxKind12.PropertyAccessExpression).getName();
987
1078
  }
988
1079
  return "";
989
1080
  }
@@ -1001,15 +1092,15 @@ function placeholderFor(propName) {
1001
1092
  }
1002
1093
  var fixRequiredCallWithOptions = (c, p, outcome) => {
1003
1094
  if (outcome.result !== "fail") return void 0;
1004
- const calls = c.fn.getDescendantsOfKind(SyntaxKind10.CallExpression);
1005
- const verify = calls.filter((x) => p.verifyCalls.includes(callName5(x)));
1095
+ const calls = c.fn.getDescendantsOfKind(SyntaxKind12.CallExpression);
1096
+ const verify = calls.filter((x) => p.verifyCalls.includes(callName6(x)));
1006
1097
  if (verify.length > 0) {
1007
1098
  const call = verify[0];
1008
1099
  const args = call.getArguments();
1009
1100
  const lastArg = args[args.length - 1];
1010
- const obj = lastArg?.asKind(SyntaxKind10.ObjectLiteralExpression);
1101
+ const obj = lastArg?.asKind(SyntaxKind12.ObjectLiteralExpression);
1011
1102
  const presentNames = obj ? obj.getProperties().map((pr) => {
1012
- const pa = pr.asKind(SyntaxKind10.PropertyAssignment) ?? pr.asKind(SyntaxKind10.ShorthandPropertyAssignment);
1103
+ const pa = pr.asKind(SyntaxKind12.PropertyAssignment) ?? pr.asKind(SyntaxKind12.ShorthandPropertyAssignment);
1013
1104
  return pa?.getName() ?? "";
1014
1105
  }) : [];
1015
1106
  const missingGroups = p.requiredProps.filter(
@@ -1017,7 +1108,7 @@ var fixRequiredCallWithOptions = (c, p, outcome) => {
1017
1108
  );
1018
1109
  if (missingGroups.length === 0) return void 0;
1019
1110
  const newProps = missingGroups.map((g) => placeholderFor(g[0])).join(", ");
1020
- const summary = `Add ${missingGroups.map((g) => g[0]).join(" and ")} to the ${callName5(call)} call`;
1111
+ const summary = `Add ${missingGroups.map((g) => g[0]).join(" and ")} to the ${callName6(call)} call`;
1021
1112
  if (obj) {
1022
1113
  const inner = obj.getText().slice(1, -1).trim();
1023
1114
  const newText = inner.length > 0 ? `{ ${inner}, ${newProps} }` : `{ ${newProps} }`;
@@ -1029,7 +1120,7 @@ var fixRequiredCallWithOptions = (c, p, outcome) => {
1029
1120
  }
1030
1121
  return {
1031
1122
  summary,
1032
- suggestion: `Add an options object ({ ${newProps} }) as an argument to ${callName5(call)}.`
1123
+ suggestion: `Add an options object ({ ${newProps} }) as an argument to ${callName6(call)}.`
1033
1124
  };
1034
1125
  }
1035
1126
  return {
@@ -1042,10 +1133,48 @@ JWKS must come from Privy's published JWKS endpoint for your app.`
1042
1133
  };
1043
1134
  };
1044
1135
 
1136
+ // src/fixers/literalMultiplierWrongConstant.ts
1137
+ import { SyntaxKind as SyntaxKind13 } from "ts-morph";
1138
+ function callName7(call) {
1139
+ const exp = call.getExpression();
1140
+ if (exp.getKind() === SyntaxKind13.Identifier) return exp.getText();
1141
+ if (exp.getKind() === SyntaxKind13.PropertyAccessExpression) {
1142
+ return exp.asKind(SyntaxKind13.PropertyAccessExpression).getName();
1143
+ }
1144
+ return "";
1145
+ }
1146
+ function findIdentifier(node, name) {
1147
+ if (node.getKind() === SyntaxKind13.Identifier && node.getText() === name) return node;
1148
+ return node.getDescendantsOfKind(SyntaxKind13.Identifier).find((id) => id.getText() === name);
1149
+ }
1150
+ var fixLiteralMultiplierWrongConstant = (c, p, outcome) => {
1151
+ if (outcome.result !== "fail") return void 0;
1152
+ const calls = c.fn.getDescendantsOfKind(SyntaxKind13.CallExpression).filter((call) => callName7(call) === p.call);
1153
+ if (calls.length === 0) return void 0;
1154
+ const arg = calls[0].getArguments()[p.argIndex];
1155
+ if (!arg) return void 0;
1156
+ const forbidden = Array.isArray(p.forbiddenIdentifiers) ? p.forbiddenIdentifiers : [];
1157
+ const forbiddenNode = forbidden.map((name) => findIdentifier(arg, name)).find((n) => n);
1158
+ if (!forbiddenNode) return void 0;
1159
+ const expected = Array.isArray(p.expectedIdentifiers) ? p.expectedIdentifiers : [];
1160
+ const expectedName = expected.find((name) => c.params.includes(name));
1161
+ if (!expectedName) {
1162
+ return {
1163
+ summary: `Scale by the mint's decimals instead of ${forbiddenNode.getText()}`,
1164
+ suggestion: `'${forbiddenNode.getText()}' is the native-SOL lamports constant (1e9), not the mint's decimals. Add a 'decimals: number' parameter to this function (threaded from the mint's configuration) and scale the amount with '10 ** decimals' instead of '${forbiddenNode.getText()}'.`
1165
+ };
1166
+ }
1167
+ return {
1168
+ summary: `Scale by 10 ** ${expectedName} instead of ${forbiddenNode.getText()}`,
1169
+ diff: buildDiff(forbiddenNode, `10 ** ${expectedName}`)
1170
+ };
1171
+ };
1172
+
1045
1173
  // src/fixers/index.ts
1046
1174
  var registry2 = {
1047
1175
  "positional-arg-identity": fixPositionalArgIdentity,
1048
- "required-call-with-options": fixRequiredCallWithOptions
1176
+ "required-call-with-options": fixRequiredCallWithOptions,
1177
+ "literal-multiplier-wrong-constant": fixLiteralMultiplierWrongConstant
1049
1178
  };
1050
1179
  function runFixer(kind, c, params, outcome) {
1051
1180
  if (outcome.result !== "fail") return void 0;
@@ -1995,7 +2124,7 @@ function renderTrustGraphMd(g) {
1995
2124
  }
1996
2125
 
1997
2126
  // src/costAnalysis.ts
1998
- import { Project as Project2, SyntaxKind as SyntaxKind11 } from "ts-morph";
2127
+ import { Project as Project2, SyntaxKind as SyntaxKind14 } from "ts-morph";
1999
2128
  var LAMPORTS_PER_BYTE_YEAR = 3480;
2000
2129
  var EXEMPTION_THRESHOLD = 2;
2001
2130
  var OVERHEAD_BYTES = 128;
@@ -2076,11 +2205,11 @@ var KNOWN_FLOWS = [
2076
2205
  }
2077
2206
  ];
2078
2207
  var LOOP_NODE_KINDS = /* @__PURE__ */ new Set([
2079
- SyntaxKind11.ForStatement,
2080
- SyntaxKind11.ForOfStatement,
2081
- SyntaxKind11.ForInStatement,
2082
- SyntaxKind11.WhileStatement,
2083
- SyntaxKind11.DoStatement
2208
+ SyntaxKind14.ForStatement,
2209
+ SyntaxKind14.ForOfStatement,
2210
+ SyntaxKind14.ForInStatement,
2211
+ SyntaxKind14.WhileStatement,
2212
+ SyntaxKind14.DoStatement
2084
2213
  ]);
2085
2214
  var ARRAY_METHOD_LOOPS = /* @__PURE__ */ new Set(["map", "forEach", "flatMap", "reduce", "filter"]);
2086
2215
  function isInsideLoop(node) {
@@ -2088,12 +2217,12 @@ function isInsideLoop(node) {
2088
2217
  while (cur) {
2089
2218
  const k = cur.getKind?.();
2090
2219
  if (k !== void 0 && LOOP_NODE_KINDS.has(k)) {
2091
- return { scalable: true, note: `call is inside a ${SyntaxKind11[k]} \u2014 cost scales with loop iterations` };
2220
+ return { scalable: true, note: `call is inside a ${SyntaxKind14[k]} \u2014 cost scales with loop iterations` };
2092
2221
  }
2093
- if (k === SyntaxKind11.CallExpression) {
2222
+ if (k === SyntaxKind14.CallExpression) {
2094
2223
  const expr = cur.getExpression?.();
2095
- if (expr?.getKind?.() === SyntaxKind11.PropertyAccessExpression) {
2096
- const name = expr.asKind?.(SyntaxKind11.PropertyAccessExpression)?.getName?.();
2224
+ if (expr?.getKind?.() === SyntaxKind14.PropertyAccessExpression) {
2225
+ const name = expr.asKind?.(SyntaxKind14.PropertyAccessExpression)?.getName?.();
2097
2226
  if (name && ARRAY_METHOD_LOOPS.has(name)) {
2098
2227
  return { scalable: true, note: `call is inside .${name}() \u2014 cost scales with array length` };
2099
2228
  }
@@ -2107,7 +2236,7 @@ function detectPriorityFee(targetDir) {
2107
2236
  const project = new Project2({ skipAddingFilesFromTsConfig: true });
2108
2237
  for (const file of walk(targetDir)) {
2109
2238
  const sf = project.addSourceFileAtPath(file);
2110
- const calls = sf.getDescendantsOfKind(SyntaxKind11.CallExpression);
2239
+ const calls = sf.getDescendantsOfKind(SyntaxKind14.CallExpression);
2111
2240
  for (const ce of calls) {
2112
2241
  const expr = ce.getExpression();
2113
2242
  const text = expr.getText();
@@ -2135,22 +2264,22 @@ function detectAccountFlows(targetDir) {
2135
2264
  const importedModules = new Set(
2136
2265
  sf.getImportDeclarations().map((d) => d.getModuleSpecifierValue())
2137
2266
  );
2138
- for (const ce of sf.getDescendantsOfKind(SyntaxKind11.CallExpression)) {
2267
+ for (const ce of sf.getDescendantsOfKind(SyntaxKind14.CallExpression)) {
2139
2268
  const expr = ce.getExpression();
2140
- let callName6 = null;
2141
- if (expr.getKind() === SyntaxKind11.Identifier) {
2142
- callName6 = expr.getText();
2143
- } else if (expr.getKind() === SyntaxKind11.PropertyAccessExpression) {
2144
- callName6 = expr.asKind(SyntaxKind11.PropertyAccessExpression).getName();
2269
+ let callName8 = null;
2270
+ if (expr.getKind() === SyntaxKind14.Identifier) {
2271
+ callName8 = expr.getText();
2272
+ } else if (expr.getKind() === SyntaxKind14.PropertyAccessExpression) {
2273
+ callName8 = expr.asKind(SyntaxKind14.PropertyAccessExpression).getName();
2145
2274
  }
2146
- if (!callName6) continue;
2147
- const known = callIndex.get(callName6);
2275
+ if (!callName8) continue;
2276
+ const known = callIndex.get(callName8);
2148
2277
  if (!known) continue;
2149
2278
  if (!importedModules.has(known.module)) continue;
2150
2279
  const lamports = rentExemptMinimum(known.dataLen);
2151
2280
  const { scalable, note } = isInsideLoop(ce);
2152
2281
  flows.push({
2153
- call: callName6,
2282
+ call: callName8,
2154
2283
  module: known.module,
2155
2284
  accountType: known.accountType,
2156
2285
  file,
package/dist/cli.js CHANGED
@@ -20,7 +20,7 @@ import {
20
20
  submitTelemetry,
21
21
  telemetryFilePath,
22
22
  validatePack
23
- } from "./chunk-LOPYKIXE.js";
23
+ } from "./chunk-LHP6HFMS.js";
24
24
 
25
25
  // src/cli.ts
26
26
  import { writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
package/dist/index.js CHANGED
@@ -50,7 +50,7 @@ import {
50
50
  testKinds,
51
51
  validatePack,
52
52
  validatePackManifest
53
- } from "./chunk-LOPYKIXE.js";
53
+ } from "./chunk-LHP6HFMS.js";
54
54
 
55
55
  // src/generate.ts
56
56
  import { writeFileSync, mkdirSync } from "fs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brainblast",
3
- "version": "0.5.1",
3
+ "version": "0.5.3",
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
6
  "keywords": [