brainblast 0.5.0 → 0.5.2

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.
@@ -662,6 +662,44 @@ var taintToSink = (c, p) => {
662
662
  return { result: "pass", detail: "No tracked source value flows to a sink within the analyzed call graph." };
663
663
  };
664
664
 
665
+ // src/checkers/literalMultiplierWrongConstant.ts
666
+ import { SyntaxKind as SyntaxKind8 } from "ts-morph";
667
+ function callName4(call) {
668
+ 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();
672
+ }
673
+ return "";
674
+ }
675
+ 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);
678
+ }
679
+ var literalMultiplierWrongConstant = (c, p) => {
680
+ const calls = c.fn.getDescendantsOfKind(SyntaxKind8.CallExpression).filter((x) => callName4(x) === p.call);
681
+ if (calls.length === 0) {
682
+ return { result: "cant_tell", detail: p.absentCallDetail };
683
+ }
684
+ const arg = calls[0].getArguments()[p.argIndex];
685
+ if (!arg) {
686
+ return { result: "cant_tell", detail: p.absentCallDetail };
687
+ }
688
+ const forbidden = Array.isArray(p.forbiddenIdentifiers) ? p.forbiddenIdentifiers : [];
689
+ for (const name of forbidden) {
690
+ if (containsIdentifier(arg, name)) {
691
+ return { result: "fail", detail: String(p.failDetail).replace("{got}", name) };
692
+ }
693
+ }
694
+ const expected = Array.isArray(p.expectedIdentifiers) ? p.expectedIdentifiers : [];
695
+ for (const name of expected) {
696
+ if (containsIdentifier(arg, name)) {
697
+ return { result: "pass", detail: String(p.passDetail) };
698
+ }
699
+ }
700
+ return { result: "cant_tell", detail: p.cantTellDetail };
701
+ };
702
+
665
703
  // src/checkers/index.ts
666
704
  var registry = {
667
705
  "positional-arg-identity": positionalArgIdentity,
@@ -671,7 +709,8 @@ var registry = {
671
709
  "object-arg-property-literal-equals": objectArgPropertyLiteralEquals,
672
710
  "anchor-init-if-needed-guarded": anchorInitIfNeededGuarded,
673
711
  "env-secrets-committed": envSecretsCommitted,
674
- "taint-to-sink": taintToSink
712
+ "taint-to-sink": taintToSink,
713
+ "literal-multiplier-wrong-constant": literalMultiplierWrongConstant
675
714
  };
676
715
  function runChecker(kind, c, params) {
677
716
  const fn = registry[kind];
@@ -887,7 +926,7 @@ function findRustCandidates(targetDir, rule) {
887
926
  }
888
927
 
889
928
  // src/fixers/positionalArgIdentity.ts
890
- import { SyntaxKind as SyntaxKind8 } from "ts-morph";
929
+ import { SyntaxKind as SyntaxKind9 } from "ts-morph";
891
930
 
892
931
  // src/fixers/diffUtil.ts
893
932
  function buildDiff(node, replacement) {
@@ -912,9 +951,9 @@ function buildDiff(node, replacement) {
912
951
  // src/fixers/positionalArgIdentity.ts
913
952
  var fixPositionalArgIdentity = (c, p, outcome) => {
914
953
  if (outcome.result !== "fail") return void 0;
915
- const calls = c.fn.getDescendantsOfKind(SyntaxKind8.CallExpression).filter((call) => {
954
+ const calls = c.fn.getDescendantsOfKind(SyntaxKind9.CallExpression).filter((call) => {
916
955
  const exp = call.getExpression();
917
- return exp.getKind() === SyntaxKind8.PropertyAccessExpression && exp.asKind(SyntaxKind8.PropertyAccessExpression).getName() === p.call;
956
+ return exp.getKind() === SyntaxKind9.PropertyAccessExpression && exp.asKind(SyntaxKind9.PropertyAccessExpression).getName() === p.call;
918
957
  });
919
958
  if (calls.length === 0) {
920
959
  const wantParam2 = c.params[p.paramIndex] ?? "<rawBodyParam>";
@@ -929,7 +968,7 @@ Do not call JSON.parse() on the body before this verification step.`
929
968
  }
930
969
  const arg = calls[0].getArguments()[p.argIndex];
931
970
  const wantParam = c.params[p.paramIndex];
932
- if (arg && wantParam && arg.getKind() === SyntaxKind8.CallExpression) {
971
+ if (arg && wantParam && arg.getKind() === SyntaxKind9.CallExpression) {
933
972
  return {
934
973
  summary: `Pass the raw body parameter '${wantParam}' to ${p.call} instead of a parsed value`,
935
974
  diff: buildDiff(arg, wantParam)
@@ -939,12 +978,12 @@ Do not call JSON.parse() on the body before this verification step.`
939
978
  };
940
979
 
941
980
  // src/fixers/requiredCallWithOptions.ts
942
- import { SyntaxKind as SyntaxKind9 } from "ts-morph";
943
- function callName4(call) {
981
+ import { SyntaxKind as SyntaxKind10 } from "ts-morph";
982
+ function callName5(call) {
944
983
  const exp = call.getExpression();
945
- if (exp.getKind() === SyntaxKind9.Identifier) return exp.getText();
946
- if (exp.getKind() === SyntaxKind9.PropertyAccessExpression) {
947
- return exp.asKind(SyntaxKind9.PropertyAccessExpression).getName();
984
+ if (exp.getKind() === SyntaxKind10.Identifier) return exp.getText();
985
+ if (exp.getKind() === SyntaxKind10.PropertyAccessExpression) {
986
+ return exp.asKind(SyntaxKind10.PropertyAccessExpression).getName();
948
987
  }
949
988
  return "";
950
989
  }
@@ -962,15 +1001,15 @@ function placeholderFor(propName) {
962
1001
  }
963
1002
  var fixRequiredCallWithOptions = (c, p, outcome) => {
964
1003
  if (outcome.result !== "fail") return void 0;
965
- const calls = c.fn.getDescendantsOfKind(SyntaxKind9.CallExpression);
966
- const verify = calls.filter((x) => p.verifyCalls.includes(callName4(x)));
1004
+ const calls = c.fn.getDescendantsOfKind(SyntaxKind10.CallExpression);
1005
+ const verify = calls.filter((x) => p.verifyCalls.includes(callName5(x)));
967
1006
  if (verify.length > 0) {
968
1007
  const call = verify[0];
969
1008
  const args = call.getArguments();
970
1009
  const lastArg = args[args.length - 1];
971
- const obj = lastArg?.asKind(SyntaxKind9.ObjectLiteralExpression);
1010
+ const obj = lastArg?.asKind(SyntaxKind10.ObjectLiteralExpression);
972
1011
  const presentNames = obj ? obj.getProperties().map((pr) => {
973
- const pa = pr.asKind(SyntaxKind9.PropertyAssignment) ?? pr.asKind(SyntaxKind9.ShorthandPropertyAssignment);
1012
+ const pa = pr.asKind(SyntaxKind10.PropertyAssignment) ?? pr.asKind(SyntaxKind10.ShorthandPropertyAssignment);
974
1013
  return pa?.getName() ?? "";
975
1014
  }) : [];
976
1015
  const missingGroups = p.requiredProps.filter(
@@ -978,7 +1017,7 @@ var fixRequiredCallWithOptions = (c, p, outcome) => {
978
1017
  );
979
1018
  if (missingGroups.length === 0) return void 0;
980
1019
  const newProps = missingGroups.map((g) => placeholderFor(g[0])).join(", ");
981
- const summary = `Add ${missingGroups.map((g) => g[0]).join(" and ")} to the ${callName4(call)} call`;
1020
+ const summary = `Add ${missingGroups.map((g) => g[0]).join(" and ")} to the ${callName5(call)} call`;
982
1021
  if (obj) {
983
1022
  const inner = obj.getText().slice(1, -1).trim();
984
1023
  const newText = inner.length > 0 ? `{ ${inner}, ${newProps} }` : `{ ${newProps} }`;
@@ -990,7 +1029,7 @@ var fixRequiredCallWithOptions = (c, p, outcome) => {
990
1029
  }
991
1030
  return {
992
1031
  summary,
993
- suggestion: `Add an options object ({ ${newProps} }) as an argument to ${callName4(call)}.`
1032
+ suggestion: `Add an options object ({ ${newProps} }) as an argument to ${callName5(call)}.`
994
1033
  };
995
1034
  }
996
1035
  return {
@@ -1003,10 +1042,48 @@ JWKS must come from Privy's published JWKS endpoint for your app.`
1003
1042
  };
1004
1043
  };
1005
1044
 
1045
+ // src/fixers/literalMultiplierWrongConstant.ts
1046
+ import { SyntaxKind as SyntaxKind11 } from "ts-morph";
1047
+ function callName6(call) {
1048
+ const exp = call.getExpression();
1049
+ if (exp.getKind() === SyntaxKind11.Identifier) return exp.getText();
1050
+ if (exp.getKind() === SyntaxKind11.PropertyAccessExpression) {
1051
+ return exp.asKind(SyntaxKind11.PropertyAccessExpression).getName();
1052
+ }
1053
+ return "";
1054
+ }
1055
+ function findIdentifier(node, name) {
1056
+ if (node.getKind() === SyntaxKind11.Identifier && node.getText() === name) return node;
1057
+ return node.getDescendantsOfKind(SyntaxKind11.Identifier).find((id) => id.getText() === name);
1058
+ }
1059
+ var fixLiteralMultiplierWrongConstant = (c, p, outcome) => {
1060
+ if (outcome.result !== "fail") return void 0;
1061
+ const calls = c.fn.getDescendantsOfKind(SyntaxKind11.CallExpression).filter((call) => callName6(call) === p.call);
1062
+ if (calls.length === 0) return void 0;
1063
+ const arg = calls[0].getArguments()[p.argIndex];
1064
+ if (!arg) return void 0;
1065
+ const forbidden = Array.isArray(p.forbiddenIdentifiers) ? p.forbiddenIdentifiers : [];
1066
+ const forbiddenNode = forbidden.map((name) => findIdentifier(arg, name)).find((n) => n);
1067
+ if (!forbiddenNode) return void 0;
1068
+ const expected = Array.isArray(p.expectedIdentifiers) ? p.expectedIdentifiers : [];
1069
+ const expectedName = expected.find((name) => c.params.includes(name));
1070
+ if (!expectedName) {
1071
+ return {
1072
+ summary: `Scale by the mint's decimals instead of ${forbiddenNode.getText()}`,
1073
+ 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()}'.`
1074
+ };
1075
+ }
1076
+ return {
1077
+ summary: `Scale by 10 ** ${expectedName} instead of ${forbiddenNode.getText()}`,
1078
+ diff: buildDiff(forbiddenNode, `10 ** ${expectedName}`)
1079
+ };
1080
+ };
1081
+
1006
1082
  // src/fixers/index.ts
1007
1083
  var registry2 = {
1008
1084
  "positional-arg-identity": fixPositionalArgIdentity,
1009
- "required-call-with-options": fixRequiredCallWithOptions
1085
+ "required-call-with-options": fixRequiredCallWithOptions,
1086
+ "literal-multiplier-wrong-constant": fixLiteralMultiplierWrongConstant
1010
1087
  };
1011
1088
  function runFixer(kind, c, params, outcome) {
1012
1089
  if (outcome.result !== "fail") return void 0;
@@ -1956,7 +2033,7 @@ function renderTrustGraphMd(g) {
1956
2033
  }
1957
2034
 
1958
2035
  // src/costAnalysis.ts
1959
- import { Project as Project2, SyntaxKind as SyntaxKind10 } from "ts-morph";
2036
+ import { Project as Project2, SyntaxKind as SyntaxKind12 } from "ts-morph";
1960
2037
  var LAMPORTS_PER_BYTE_YEAR = 3480;
1961
2038
  var EXEMPTION_THRESHOLD = 2;
1962
2039
  var OVERHEAD_BYTES = 128;
@@ -2037,11 +2114,11 @@ var KNOWN_FLOWS = [
2037
2114
  }
2038
2115
  ];
2039
2116
  var LOOP_NODE_KINDS = /* @__PURE__ */ new Set([
2040
- SyntaxKind10.ForStatement,
2041
- SyntaxKind10.ForOfStatement,
2042
- SyntaxKind10.ForInStatement,
2043
- SyntaxKind10.WhileStatement,
2044
- SyntaxKind10.DoStatement
2117
+ SyntaxKind12.ForStatement,
2118
+ SyntaxKind12.ForOfStatement,
2119
+ SyntaxKind12.ForInStatement,
2120
+ SyntaxKind12.WhileStatement,
2121
+ SyntaxKind12.DoStatement
2045
2122
  ]);
2046
2123
  var ARRAY_METHOD_LOOPS = /* @__PURE__ */ new Set(["map", "forEach", "flatMap", "reduce", "filter"]);
2047
2124
  function isInsideLoop(node) {
@@ -2049,12 +2126,12 @@ function isInsideLoop(node) {
2049
2126
  while (cur) {
2050
2127
  const k = cur.getKind?.();
2051
2128
  if (k !== void 0 && LOOP_NODE_KINDS.has(k)) {
2052
- return { scalable: true, note: `call is inside a ${SyntaxKind10[k]} \u2014 cost scales with loop iterations` };
2129
+ return { scalable: true, note: `call is inside a ${SyntaxKind12[k]} \u2014 cost scales with loop iterations` };
2053
2130
  }
2054
- if (k === SyntaxKind10.CallExpression) {
2131
+ if (k === SyntaxKind12.CallExpression) {
2055
2132
  const expr = cur.getExpression?.();
2056
- if (expr?.getKind?.() === SyntaxKind10.PropertyAccessExpression) {
2057
- const name = expr.asKind?.(SyntaxKind10.PropertyAccessExpression)?.getName?.();
2133
+ if (expr?.getKind?.() === SyntaxKind12.PropertyAccessExpression) {
2134
+ const name = expr.asKind?.(SyntaxKind12.PropertyAccessExpression)?.getName?.();
2058
2135
  if (name && ARRAY_METHOD_LOOPS.has(name)) {
2059
2136
  return { scalable: true, note: `call is inside .${name}() \u2014 cost scales with array length` };
2060
2137
  }
@@ -2068,7 +2145,7 @@ function detectPriorityFee(targetDir) {
2068
2145
  const project = new Project2({ skipAddingFilesFromTsConfig: true });
2069
2146
  for (const file of walk(targetDir)) {
2070
2147
  const sf = project.addSourceFileAtPath(file);
2071
- const calls = sf.getDescendantsOfKind(SyntaxKind10.CallExpression);
2148
+ const calls = sf.getDescendantsOfKind(SyntaxKind12.CallExpression);
2072
2149
  for (const ce of calls) {
2073
2150
  const expr = ce.getExpression();
2074
2151
  const text = expr.getText();
@@ -2096,22 +2173,22 @@ function detectAccountFlows(targetDir) {
2096
2173
  const importedModules = new Set(
2097
2174
  sf.getImportDeclarations().map((d) => d.getModuleSpecifierValue())
2098
2175
  );
2099
- for (const ce of sf.getDescendantsOfKind(SyntaxKind10.CallExpression)) {
2176
+ for (const ce of sf.getDescendantsOfKind(SyntaxKind12.CallExpression)) {
2100
2177
  const expr = ce.getExpression();
2101
- let callName5 = null;
2102
- if (expr.getKind() === SyntaxKind10.Identifier) {
2103
- callName5 = expr.getText();
2104
- } else if (expr.getKind() === SyntaxKind10.PropertyAccessExpression) {
2105
- callName5 = expr.asKind(SyntaxKind10.PropertyAccessExpression).getName();
2178
+ let callName7 = null;
2179
+ if (expr.getKind() === SyntaxKind12.Identifier) {
2180
+ callName7 = expr.getText();
2181
+ } else if (expr.getKind() === SyntaxKind12.PropertyAccessExpression) {
2182
+ callName7 = expr.asKind(SyntaxKind12.PropertyAccessExpression).getName();
2106
2183
  }
2107
- if (!callName5) continue;
2108
- const known = callIndex.get(callName5);
2184
+ if (!callName7) continue;
2185
+ const known = callIndex.get(callName7);
2109
2186
  if (!known) continue;
2110
2187
  if (!importedModules.has(known.module)) continue;
2111
2188
  const lamports = rentExemptMinimum(known.dataLen);
2112
2189
  const { scalable, note } = isInsideLoop(ce);
2113
2190
  flows.push({
2114
- call: callName5,
2191
+ call: callName7,
2115
2192
  module: known.module,
2116
2193
  accountType: known.accountType,
2117
2194
  file,
package/dist/cli.js CHANGED
@@ -20,7 +20,7 @@ import {
20
20
  submitTelemetry,
21
21
  telemetryFilePath,
22
22
  validatePack
23
- } from "./chunk-5LJXC66F.js";
23
+ } from "./chunk-EYFKA33G.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-5LJXC66F.js";
53
+ } from "./chunk-EYFKA33G.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.0",
3
+ "version": "0.5.2",
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": [