brainblast 0.5.0 → 0.5.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/{chunk-5LJXC66F.js → chunk-LOPYKIXE.js} +75 -36
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -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
|
|
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(
|
|
954
|
+
const calls = c.fn.getDescendantsOfKind(SyntaxKind9.CallExpression).filter((call) => {
|
|
916
955
|
const exp = call.getExpression();
|
|
917
|
-
return exp.getKind() ===
|
|
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() ===
|
|
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
|
|
943
|
-
function
|
|
981
|
+
import { SyntaxKind as SyntaxKind10 } from "ts-morph";
|
|
982
|
+
function callName5(call) {
|
|
944
983
|
const exp = call.getExpression();
|
|
945
|
-
if (exp.getKind() ===
|
|
946
|
-
if (exp.getKind() ===
|
|
947
|
-
return exp.asKind(
|
|
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(
|
|
966
|
-
const verify = calls.filter((x) => p.verifyCalls.includes(
|
|
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(
|
|
1010
|
+
const obj = lastArg?.asKind(SyntaxKind10.ObjectLiteralExpression);
|
|
972
1011
|
const presentNames = obj ? obj.getProperties().map((pr) => {
|
|
973
|
-
const pa = pr.asKind(
|
|
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 ${
|
|
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 ${
|
|
1032
|
+
suggestion: `Add an options object ({ ${newProps} }) as an argument to ${callName5(call)}.`
|
|
994
1033
|
};
|
|
995
1034
|
}
|
|
996
1035
|
return {
|
|
@@ -1956,7 +1995,7 @@ function renderTrustGraphMd(g) {
|
|
|
1956
1995
|
}
|
|
1957
1996
|
|
|
1958
1997
|
// src/costAnalysis.ts
|
|
1959
|
-
import { Project as Project2, SyntaxKind as
|
|
1998
|
+
import { Project as Project2, SyntaxKind as SyntaxKind11 } from "ts-morph";
|
|
1960
1999
|
var LAMPORTS_PER_BYTE_YEAR = 3480;
|
|
1961
2000
|
var EXEMPTION_THRESHOLD = 2;
|
|
1962
2001
|
var OVERHEAD_BYTES = 128;
|
|
@@ -2037,11 +2076,11 @@ var KNOWN_FLOWS = [
|
|
|
2037
2076
|
}
|
|
2038
2077
|
];
|
|
2039
2078
|
var LOOP_NODE_KINDS = /* @__PURE__ */ new Set([
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2079
|
+
SyntaxKind11.ForStatement,
|
|
2080
|
+
SyntaxKind11.ForOfStatement,
|
|
2081
|
+
SyntaxKind11.ForInStatement,
|
|
2082
|
+
SyntaxKind11.WhileStatement,
|
|
2083
|
+
SyntaxKind11.DoStatement
|
|
2045
2084
|
]);
|
|
2046
2085
|
var ARRAY_METHOD_LOOPS = /* @__PURE__ */ new Set(["map", "forEach", "flatMap", "reduce", "filter"]);
|
|
2047
2086
|
function isInsideLoop(node) {
|
|
@@ -2049,12 +2088,12 @@ function isInsideLoop(node) {
|
|
|
2049
2088
|
while (cur) {
|
|
2050
2089
|
const k = cur.getKind?.();
|
|
2051
2090
|
if (k !== void 0 && LOOP_NODE_KINDS.has(k)) {
|
|
2052
|
-
return { scalable: true, note: `call is inside a ${
|
|
2091
|
+
return { scalable: true, note: `call is inside a ${SyntaxKind11[k]} \u2014 cost scales with loop iterations` };
|
|
2053
2092
|
}
|
|
2054
|
-
if (k ===
|
|
2093
|
+
if (k === SyntaxKind11.CallExpression) {
|
|
2055
2094
|
const expr = cur.getExpression?.();
|
|
2056
|
-
if (expr?.getKind?.() ===
|
|
2057
|
-
const name = expr.asKind?.(
|
|
2095
|
+
if (expr?.getKind?.() === SyntaxKind11.PropertyAccessExpression) {
|
|
2096
|
+
const name = expr.asKind?.(SyntaxKind11.PropertyAccessExpression)?.getName?.();
|
|
2058
2097
|
if (name && ARRAY_METHOD_LOOPS.has(name)) {
|
|
2059
2098
|
return { scalable: true, note: `call is inside .${name}() \u2014 cost scales with array length` };
|
|
2060
2099
|
}
|
|
@@ -2068,7 +2107,7 @@ function detectPriorityFee(targetDir) {
|
|
|
2068
2107
|
const project = new Project2({ skipAddingFilesFromTsConfig: true });
|
|
2069
2108
|
for (const file of walk(targetDir)) {
|
|
2070
2109
|
const sf = project.addSourceFileAtPath(file);
|
|
2071
|
-
const calls = sf.getDescendantsOfKind(
|
|
2110
|
+
const calls = sf.getDescendantsOfKind(SyntaxKind11.CallExpression);
|
|
2072
2111
|
for (const ce of calls) {
|
|
2073
2112
|
const expr = ce.getExpression();
|
|
2074
2113
|
const text = expr.getText();
|
|
@@ -2096,22 +2135,22 @@ function detectAccountFlows(targetDir) {
|
|
|
2096
2135
|
const importedModules = new Set(
|
|
2097
2136
|
sf.getImportDeclarations().map((d) => d.getModuleSpecifierValue())
|
|
2098
2137
|
);
|
|
2099
|
-
for (const ce of sf.getDescendantsOfKind(
|
|
2138
|
+
for (const ce of sf.getDescendantsOfKind(SyntaxKind11.CallExpression)) {
|
|
2100
2139
|
const expr = ce.getExpression();
|
|
2101
|
-
let
|
|
2102
|
-
if (expr.getKind() ===
|
|
2103
|
-
|
|
2104
|
-
} else if (expr.getKind() ===
|
|
2105
|
-
|
|
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();
|
|
2106
2145
|
}
|
|
2107
|
-
if (!
|
|
2108
|
-
const known = callIndex.get(
|
|
2146
|
+
if (!callName6) continue;
|
|
2147
|
+
const known = callIndex.get(callName6);
|
|
2109
2148
|
if (!known) continue;
|
|
2110
2149
|
if (!importedModules.has(known.module)) continue;
|
|
2111
2150
|
const lamports = rentExemptMinimum(known.dataLen);
|
|
2112
2151
|
const { scalable, note } = isInsideLoop(ce);
|
|
2113
2152
|
flows.push({
|
|
2114
|
-
call:
|
|
2153
|
+
call: callName6,
|
|
2115
2154
|
module: known.module,
|
|
2116
2155
|
accountType: known.accountType,
|
|
2117
2156
|
file,
|
package/dist/cli.js
CHANGED
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "brainblast",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
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": [
|