brainblast 0.4.0 → 0.4.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.
package/dist/cli.js CHANGED
@@ -1,16 +1,20 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  analyzeCosts,
4
+ applyDiffToFile,
4
5
  audit,
5
6
  buildTrustGraph,
6
7
  cacheSize,
7
8
  defaultCachePath,
9
+ getChangedRanges,
8
10
  isValidSolanaAddress,
9
11
  loadProgramCache,
12
+ parseDiff,
10
13
  renderCostReportMd,
11
14
  renderTrustGraphMd,
12
- resolveRules
13
- } from "./chunk-WVHGN2HR.js";
15
+ resolveRules,
16
+ startWatch
17
+ } from "./chunk-Q72MTJXQ.js";
14
18
 
15
19
  // src/cli.ts
16
20
  import { writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
@@ -37,16 +41,16 @@ function loadMemory(targetDir2) {
37
41
  return { ...EMPTY_MEMORY, lastRun: [], fixHistory: [] };
38
42
  }
39
43
  }
40
- function saveMemory(targetDir2, memory2) {
44
+ function saveMemory(targetDir2, memory) {
41
45
  mkdirSync(join(targetDir2, ".agent-research"), { recursive: true });
42
- writeFileSync(memoryPath(targetDir2), JSON.stringify(memory2, null, 2));
46
+ writeFileSync(memoryPath(targetDir2), JSON.stringify(memory, null, 2));
43
47
  }
44
48
  var snapshotKey = (e) => `${e.ruleId}::${e.file}::${e.exportName}`;
45
49
  function precedentKey(c) {
46
50
  return `${c.ruleId}::${c.file}`;
47
51
  }
48
- function updateMemory(memory2, checks2, now = /* @__PURE__ */ new Date()) {
49
- const prevByKey = new Map(memory2.lastRun.map((e) => [snapshotKey(e), e]));
52
+ function updateMemory(memory, checks2, now = /* @__PURE__ */ new Date()) {
53
+ const prevByKey = new Map(memory.lastRun.map((e) => [snapshotKey(e), e]));
50
54
  const fixedAt = now.toISOString().slice(0, 10);
51
55
  const newFixEvents = [];
52
56
  for (const c of checks2) {
@@ -61,15 +65,15 @@ function updateMemory(memory2, checks2, now = /* @__PURE__ */ new Date()) {
61
65
  });
62
66
  }
63
67
  }
64
- const fixHistory = [...memory2.fixHistory, ...newFixEvents];
65
- const precedents2 = /* @__PURE__ */ new Map();
68
+ const fixHistory = [...memory.fixHistory, ...newFixEvents];
69
+ const precedents = /* @__PURE__ */ new Map();
66
70
  for (const c of checks2) {
67
71
  if (c.result !== "fail") continue;
68
72
  const pk = precedentKey(c);
69
- if (precedents2.has(pk)) continue;
73
+ if (precedents.has(pk)) continue;
70
74
  const matches = fixHistory.filter((e) => e.ruleId === c.ruleId && e.file !== c.file).sort((a, b) => a.fixedAt < b.fixedAt ? 1 : a.fixedAt > b.fixedAt ? -1 : 0);
71
75
  if (matches[0]) {
72
- precedents2.set(pk, {
76
+ precedents.set(pk, {
73
77
  file: matches[0].file,
74
78
  exportName: matches[0].exportName,
75
79
  fixedAt: matches[0].fixedAt,
@@ -84,31 +88,72 @@ function updateMemory(memory2, checks2, now = /* @__PURE__ */ new Date()) {
84
88
  result: c.result,
85
89
  detail: c.detail
86
90
  }));
87
- return { memory: { schemaVersion: "1.0", lastRun, fixHistory }, precedents: precedents2 };
91
+ return { memory: { schemaVersion: "1.0", lastRun, fixHistory }, precedents };
88
92
  }
89
93
 
90
94
  // src/cli.ts
95
+ import { execFileSync } from "child_process";
91
96
  var args = process.argv.slice(2);
92
97
  if (args[0] === "trust-graph") {
93
98
  await runTrustGraph(args.slice(1));
94
99
  process.exit(0);
95
100
  }
101
+ if (args[0] === "watch") {
102
+ const watchDir = args.find((a, i) => i > 0 && !a.startsWith("--")) ?? process.cwd();
103
+ startWatch(watchDir);
104
+ process.on("SIGINT", () => process.exit(0));
105
+ process.on("SIGTERM", () => process.exit(0));
106
+ await new Promise(() => {
107
+ });
108
+ }
109
+ if (args[0] === "fix") {
110
+ await runFix(args.slice(1));
111
+ process.exit(0);
112
+ }
96
113
  var ci = args.includes("--ci");
97
114
  var strict = args.includes("--strict");
98
- var targetDir = args.find((a) => !a.startsWith("--")) ?? process.cwd();
115
+ var sinceIdx = args.indexOf("--since");
116
+ var since = sinceIdx >= 0 ? args[sinceIdx + 1] : void 0;
117
+ var targetDir = args.find((a, i) => !a.startsWith("--") && args[i - 1] !== "--since") ?? process.cwd();
118
+ if (sinceIdx >= 0 && !since) {
119
+ console.error("error: --since requires a <ref> argument, e.g. --since origin/main");
120
+ process.exit(2);
121
+ }
99
122
  var rules = resolveRules(targetDir);
100
- var { checks, report } = audit(targetDir, rules);
101
- var memory = loadMemory(targetDir);
102
- var { memory: nextMemory, precedents } = updateMemory(memory, checks);
103
- for (const c of checks) {
104
- const p = precedents.get(precedentKey(c));
105
- if (p) c.precedent = p;
123
+ var changedRanges;
124
+ if (since) {
125
+ try {
126
+ changedRanges = getChangedRanges(targetDir, since);
127
+ } catch (e) {
128
+ console.error(e.message ?? String(e));
129
+ process.exit(2);
130
+ }
106
131
  }
107
- for (const rc of report.checks) {
108
- const p = precedents.get(precedentKey(rc));
109
- if (p) rc.precedent = p;
132
+ var { checks, report } = audit(targetDir, rules, changedRanges);
133
+ if (!changedRanges) {
134
+ const memory = loadMemory(targetDir);
135
+ const { memory: nextMemory, precedents } = updateMemory(memory, checks);
136
+ for (const c of checks) {
137
+ const p = precedents.get(precedentKey(c));
138
+ if (p) c.precedent = p;
139
+ }
140
+ for (const rc of report.checks) {
141
+ const p = precedents.get(precedentKey(rc));
142
+ if (p) rc.precedent = p;
143
+ }
144
+ saveMemory(targetDir, nextMemory);
145
+ } else {
146
+ const memory = loadMemory(targetDir);
147
+ const { precedents } = updateMemory(memory, checks);
148
+ for (const c of checks) {
149
+ const p = precedents.get(precedentKey(c));
150
+ if (p) c.precedent = p;
151
+ }
152
+ for (const rc of report.checks) {
153
+ const p = precedents.get(precedentKey(rc));
154
+ if (p) rc.precedent = p;
155
+ }
110
156
  }
111
- saveMemory(targetDir, nextMemory);
112
157
  var costReport = analyzeCosts(targetDir);
113
158
  report.costAnalysis = costReport;
114
159
  var outDir = join2(targetDir, ".agent-research");
@@ -199,3 +244,78 @@ async function runTrustGraph(argv) {
199
244
  console.error(` program-cache: ${count} entries (${cp})`);
200
245
  }
201
246
  }
247
+ async function runFix(argv) {
248
+ const apply = argv.includes("--apply");
249
+ const branch = argv.includes("--branch");
250
+ const targetDir2 = argv.find((a) => !a.startsWith("--")) ?? process.cwd();
251
+ const rules2 = resolveRules(targetDir2);
252
+ const { checks: before } = audit(targetDir2, rules2);
253
+ const fixable = before.filter((c) => c.result === "fail" && c.fix?.diff);
254
+ if (fixable.length === 0) {
255
+ console.log("brainblast fix: no mechanical fixes available (no FAIL ships a fix.diff).");
256
+ const others = before.filter((c) => c.result === "fail" && c.fix?.suggestion);
257
+ for (const c of others) {
258
+ console.log(` [GUIDANCE] ${c.ruleId} ${c.file}:${c.line}`);
259
+ console.log(` ${c.fix.summary}`);
260
+ }
261
+ return;
262
+ }
263
+ console.log(`brainblast fix: ${fixable.length} mechanical fix(es) found.`);
264
+ for (const c of fixable) {
265
+ console.log(` [${apply ? "APPLY" : "DRY-RUN"}] ${c.ruleId} ${c.file}:${c.line} \u2014 ${c.fix.summary}`);
266
+ }
267
+ if (!apply) {
268
+ console.log("\nRe-run with --apply to write these changes to disk.");
269
+ return;
270
+ }
271
+ const byFile = /* @__PURE__ */ new Map();
272
+ for (const c of fixable) {
273
+ const file = parseDiff(c.fix.diff).filePath;
274
+ byFile.set(file, [...byFile.get(file) ?? [], c]);
275
+ }
276
+ let applied = 0;
277
+ let skipped = 0;
278
+ for (const [, group] of byFile) {
279
+ const sorted = [...group].sort((a, b) => parseDiff(b.fix.diff).oldStart - parseDiff(a.fix.diff).oldStart);
280
+ for (const c of sorted) {
281
+ const ok = applyDiffToFile(c.fix.diff);
282
+ if (ok) applied++;
283
+ else {
284
+ skipped++;
285
+ console.log(` [SKIP] ${c.ruleId} ${c.file}:${c.line} \u2014 file no longer matches the fix's expected range`);
286
+ }
287
+ }
288
+ }
289
+ console.log(`
290
+ Applied ${applied} fix(es)${skipped ? `, skipped ${skipped}` : ""}.`);
291
+ const { checks: after } = audit(targetDir2, rules2);
292
+ const stillFailing = fixable.filter((c) => {
293
+ const a = after.find((x) => x.ruleId === c.ruleId && x.file === c.file && x.exportName === c.exportName);
294
+ return a?.result === "fail";
295
+ });
296
+ if (stillFailing.length > 0) {
297
+ console.log(`
298
+ Warning: ${stillFailing.length} fix(es) applied but the rule still fails:`);
299
+ for (const c of stillFailing) console.log(` ${c.ruleId} ${c.file}:${c.line}`);
300
+ } else if (applied > 0) {
301
+ console.log("All applied fixes now pass (or cant_tell) on re-audit. \u2713");
302
+ }
303
+ if (branch && applied > 0) {
304
+ const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
305
+ const branchName = `brainblast/auto-fix-${ts}`;
306
+ try {
307
+ execFileSync("git", ["checkout", "-b", branchName], { cwd: targetDir2, stdio: "ignore" });
308
+ execFileSync("git", ["add", "-A"], { cwd: targetDir2, stdio: "ignore" });
309
+ execFileSync(
310
+ "git",
311
+ ["commit", "-q", "-m", `brainblast fix: apply ${applied} mechanical fix(es)`],
312
+ { cwd: targetDir2, stdio: "ignore" }
313
+ );
314
+ console.log(`
315
+ Committed to new branch '${branchName}'.`);
316
+ } catch (e) {
317
+ console.error(`
318
+ Warning: could not create branch/commit: ${e.message ?? e}`);
319
+ }
320
+ }
321
+ }
package/dist/index.d.ts CHANGED
@@ -74,6 +74,14 @@ interface RustCandidate {
74
74
  /** tree-sitter SyntaxNode for the function body — available for precise queries */
75
75
  fnBodyNode: any;
76
76
  }
77
+ interface ConfigCandidate {
78
+ /** Source file (absolute path) */
79
+ filePath: string;
80
+ /** Raw file contents */
81
+ content: string;
82
+ /** Whether this file is tracked by git / not covered by .gitignore */
83
+ tracked: boolean;
84
+ }
77
85
  interface CheckOutcome {
78
86
  result: CheckResultKind;
79
87
  detail: string;
@@ -115,8 +123,17 @@ interface Rule {
115
123
  modules: string[];
116
124
  nameRegex: string;
117
125
  triggerCalls: string[];
118
- /** Defaults to "typescript". Set to "rust" for Anchor/Rust checker kinds. */
119
- lang?: "typescript" | "rust";
126
+ /**
127
+ * Defaults to "typescript". Set to "rust" for Anchor/Rust checker kinds,
128
+ * or "config" for whole-file config/env audits (see `filePatterns`).
129
+ */
130
+ lang?: "typescript" | "rust" | "config";
131
+ /**
132
+ * Required when `lang: "config"`. Regexes (matched against the file path
133
+ * relative to the scan root) selecting which files this rule audits,
134
+ * e.g. `["(^|/)\\.env(\\.[^/]+)?$"]`. Ignored for "typescript"/"rust".
135
+ */
136
+ filePatterns?: string[];
120
137
  /**
121
138
  * When true, a module import from `modules` is a REQUIRED condition for
122
139
  * detection: a candidate must be in a file that imports one of the listed
@@ -140,9 +157,18 @@ interface Rule {
140
157
  params?: Record<string, any>;
141
158
  };
142
159
  }
160
+ type Checker = (candidate: Candidate, params: any) => CheckOutcome;
161
+ type RustChecker = (candidate: RustCandidate, params: any) => CheckOutcome;
162
+ type ConfigChecker = (candidate: ConfigCandidate, params: any) => CheckOutcome;
143
163
 
144
- declare function auditWithRule(targetDir: string, rule: Rule): CheckResult[];
145
- declare function audit(targetDir: string, rules: Rule[]): {
164
+ type ChangedRanges = Map<string, Array<[number, number]>>;
165
+ declare function getChangedRanges(targetDir: string, ref: string): ChangedRanges;
166
+ declare function getWorkingTreeChanges(targetDir: string): ChangedRanges;
167
+ declare function fileChanged(ranges: ChangedRanges, file: string): boolean;
168
+ declare function rangeChanged(ranges: ChangedRanges, file: string, startLine: number, endLine: number): boolean;
169
+
170
+ declare function auditWithRule(targetDir: string, rule: Rule, changedRanges?: ChangedRanges): CheckResult[];
171
+ declare function audit(targetDir: string, rules: Rule[], changedRanges?: ChangedRanges): {
146
172
  checks: CheckResult[];
147
173
  report: {
148
174
  schemaVersion: string;
@@ -213,11 +239,52 @@ declare function renderTest(kind: string, opts: {
213
239
  }): string;
214
240
  declare const testKinds: string[];
215
241
 
216
- declare function runChecker(kind: string, c: Candidate | RustCandidate, params: any): CheckOutcome;
242
+ declare function runChecker(kind: string, c: Candidate | RustCandidate | ConfigCandidate, params: any): CheckOutcome;
217
243
  declare const checkerKinds: string[];
218
244
 
219
245
  declare function findCandidates(targetDir: string, rule: Rule): Candidate[];
220
246
 
247
+ declare function findConfigCandidates(targetDir: string, rule: Rule): ConfigCandidate[];
248
+
249
+ type WatchEvent = {
250
+ type: "watch_started";
251
+ targetDir: string;
252
+ } | {
253
+ type: "scan_error";
254
+ message: string;
255
+ } | {
256
+ type: "finding";
257
+ ruleId: string;
258
+ severity: string;
259
+ result: "fail" | "cant_tell";
260
+ file: string;
261
+ line: number;
262
+ detail: string;
263
+ fix?: unknown;
264
+ } | {
265
+ type: "scan_complete";
266
+ filesChanged: number;
267
+ findings: number;
268
+ durationMs: number;
269
+ };
270
+ interface WatchOptions {
271
+ debounceMs?: number;
272
+ emit?: (event: WatchEvent) => void;
273
+ }
274
+ declare function runIncrementalScan(targetDir: string, rules: Rule[], emit: (e: WatchEvent) => void): void;
275
+ declare function startWatch(targetDir: string, opts?: WatchOptions): {
276
+ close: () => void;
277
+ };
278
+
279
+ interface ParsedDiff {
280
+ filePath: string;
281
+ oldStart: number;
282
+ oldCount: number;
283
+ newLines: string[];
284
+ }
285
+ declare function parseDiff(diff: string): ParsedDiff;
286
+ declare function applyDiffToFile(diff: string): boolean;
287
+
221
288
  type UpgradeAuthorityKind = "renounced" | "single-key" | "multisig" | "dao" | "unknown";
222
289
  type UpgradeAuthoritySource = "directory" | "rpc" | "research";
223
290
  interface UpgradeAuthority {
@@ -347,4 +414,4 @@ declare function isEntryExpired(entry: ProgramCacheEntry, ttlHoursOverride?: num
347
414
  */
348
415
  declare function cacheSize(cache: ProgramCache, ttlHoursOverride?: number): number;
349
416
 
350
- export { type AccountFlow, type AuditRef, type BuildOpts, type Candidate, type CheckOutcome, type CheckResult, type CheckResultKind, type CostReport, DEFAULT_TTL_HOURS, type OnChainProgram, type ParityNote, type PriorityFeePosture, type ProgramCache, type ProgramCacheEntry, type Recoverability, type Rule, type RustAccountField, type RustCandidate, type Severity, type TrustGraph, type UpgradeAuthority, type UpgradeAuthorityKind, type UpgradeAuthoritySource, type VerifiedBuildState, analyzeCosts, audit, auditWithRule, base58Decode, base58Encode, buildTrustGraph, rules as bundledRules, cacheSize, checkerKinds, defaultCachePath, findCandidates, generateTestForResult, getCacheEntry, getCacheEntryMeta, isEntryExpired, isValidSolanaAddress, lamportsToSol, loadDirectory, loadProgramCache, loadRules, putCacheEntry, renderCostReportMd, renderTest, renderTrustGraphMd, rentExemptMinimum, resolveRules, runChecker, saveProgramCache, testKinds };
417
+ export { type AccountFlow, type AuditRef, type BuildOpts, type Candidate, type ChangedRanges, type CheckOutcome, type CheckResult, type CheckResultKind, type Checker, type ConfigCandidate, type ConfigChecker, type CostReport, DEFAULT_TTL_HOURS, type OnChainProgram, type ParityNote, type ParsedDiff, type PriorityFeePosture, type ProgramCache, type ProgramCacheEntry, type Recoverability, type Rule, type RustAccountField, type RustCandidate, type RustChecker, type Severity, type TrustGraph, type UpgradeAuthority, type UpgradeAuthorityKind, type UpgradeAuthoritySource, type VerifiedBuildState, type WatchEvent, type WatchOptions, analyzeCosts, applyDiffToFile, audit, auditWithRule, base58Decode, base58Encode, buildTrustGraph, rules as bundledRules, cacheSize, checkerKinds, defaultCachePath, fileChanged, findCandidates, findConfigCandidates, generateTestForResult, getCacheEntry, getCacheEntryMeta, getChangedRanges, getWorkingTreeChanges, isEntryExpired, isValidSolanaAddress, lamportsToSol, loadDirectory, loadProgramCache, loadRules, parseDiff, putCacheEntry, rangeChanged, renderCostReportMd, renderTest, renderTrustGraphMd, rentExemptMinimum, resolveRules, runChecker, runIncrementalScan, saveProgramCache, startWatch, testKinds };
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  DEFAULT_TTL_HOURS,
3
3
  analyzeCosts,
4
+ applyDiffToFile,
4
5
  audit,
5
6
  auditWithRule,
6
7
  base58Decode,
@@ -9,16 +10,22 @@ import {
9
10
  cacheSize,
10
11
  checkerKinds,
11
12
  defaultCachePath,
13
+ fileChanged,
12
14
  findCandidates,
15
+ findConfigCandidates,
13
16
  getCacheEntry,
14
17
  getCacheEntryMeta,
18
+ getChangedRanges,
19
+ getWorkingTreeChanges,
15
20
  isEntryExpired,
16
21
  isValidSolanaAddress,
17
22
  lamportsToSol,
18
23
  loadDirectory,
19
24
  loadProgramCache,
20
25
  loadRules,
26
+ parseDiff,
21
27
  putCacheEntry,
28
+ rangeChanged,
22
29
  renderCostReportMd,
23
30
  renderTest,
24
31
  renderTrustGraphMd,
@@ -26,9 +33,11 @@ import {
26
33
  resolveRules,
27
34
  rules,
28
35
  runChecker,
36
+ runIncrementalScan,
29
37
  saveProgramCache,
38
+ startWatch,
30
39
  testKinds
31
- } from "./chunk-WVHGN2HR.js";
40
+ } from "./chunk-Q72MTJXQ.js";
32
41
 
33
42
  // src/generate.ts
34
43
  import { writeFileSync, mkdirSync } from "fs";
@@ -46,6 +55,7 @@ function generateTestForResult(result, rule, outPath) {
46
55
  export {
47
56
  DEFAULT_TTL_HOURS,
48
57
  analyzeCosts,
58
+ applyDiffToFile,
49
59
  audit,
50
60
  auditWithRule,
51
61
  base58Decode,
@@ -55,23 +65,31 @@ export {
55
65
  cacheSize,
56
66
  checkerKinds,
57
67
  defaultCachePath,
68
+ fileChanged,
58
69
  findCandidates,
70
+ findConfigCandidates,
59
71
  generateTestForResult,
60
72
  getCacheEntry,
61
73
  getCacheEntryMeta,
74
+ getChangedRanges,
75
+ getWorkingTreeChanges,
62
76
  isEntryExpired,
63
77
  isValidSolanaAddress,
64
78
  lamportsToSol,
65
79
  loadDirectory,
66
80
  loadProgramCache,
67
81
  loadRules,
82
+ parseDiff,
68
83
  putCacheEntry,
84
+ rangeChanged,
69
85
  renderCostReportMd,
70
86
  renderTest,
71
87
  renderTrustGraphMd,
72
88
  rentExemptMinimum,
73
89
  resolveRules,
74
90
  runChecker,
91
+ runIncrementalScan,
75
92
  saveProgramCache,
93
+ startWatch,
76
94
  testKinds
77
95
  };
@@ -0,0 +1,43 @@
1
+ # Pure-data rule (facts). Binds to vetted templates by `check.kind`/`test.kind`.
2
+ id: env-secret-leaked-to-sink
3
+ severity: high
4
+ title: Secret-shaped environment value flows to a logging/response sink
5
+ component:
6
+ name: Environment configuration
7
+ type: Config
8
+ version: unversioned
9
+ sourceUrl: https://12factor.net/config
10
+ detect:
11
+ lang: typescript
12
+ modules: []
13
+ # Never matches by name alone — only the triggerCalls (sink calls) below
14
+ # select candidates. Keeps this from firing on every function in a repo.
15
+ nameRegex: "(?!)"
16
+ triggerCalls:
17
+ - log
18
+ - error
19
+ - warn
20
+ - info
21
+ - debug
22
+ - json
23
+ - send
24
+ - write
25
+ - end
26
+ requiresImport: false
27
+ check:
28
+ kind: env-taint-to-sink
29
+ params:
30
+ sinkCalls:
31
+ - log
32
+ - error
33
+ - warn
34
+ - info
35
+ - debug
36
+ - json
37
+ - send
38
+ - write
39
+ - end
40
+ # Key names that typically hold credentials/secrets.
41
+ secretKeyPattern: "(SECRET|PRIVATE_KEY|API_KEY|ACCESS_KEY|TOKEN|PASSWORD|CREDENTIAL)"
42
+ test:
43
+ kind: none
@@ -0,0 +1,32 @@
1
+ # Pure-data rule (facts). Binds to vetted templates by `check.kind`/`test.kind`.
2
+ id: env-secrets-committed
3
+ severity: critical
4
+ title: Secret-looking values are not committed to source control
5
+ component:
6
+ name: Environment configuration
7
+ type: Config
8
+ version: unversioned
9
+ sourceUrl: https://12factor.net/config
10
+ detect:
11
+ lang: config
12
+ # .env, .env.local, .env.production, etc. — but not .env.example/.sample/.template,
13
+ # which are meant to be committed and document expected keys with placeholders.
14
+ filePatterns:
15
+ - "(^|/)\\.env(\\.(?!example$|sample$|template$)[^/]+)?$"
16
+ check:
17
+ kind: env-secrets-committed
18
+ params:
19
+ # Key names that typically hold credentials/secrets.
20
+ secretKeyPattern: "(SECRET|PRIVATE_KEY|API_KEY|ACCESS_KEY|TOKEN|PASSWORD|CREDENTIAL)"
21
+ # Values that look like placeholders, not real secrets — these are fine to commit.
22
+ placeholderPattern: "^(your[_-]|xxx|changeme|change[_-]?me|replace|example|<|sk_test_|pk_test_|test[_-]|dummy|placeholder|\\*+$|\\.\\.\\.$)"
23
+ ignoredDetail: >-
24
+ File is git-ignored and not committed to source control.
25
+ passDetail: >-
26
+ File is tracked but contains no secret-looking values (placeholders only).
27
+ failDetailPrefix: >-
28
+ This file is committed to source control and contains what look like real
29
+ secret values. Anyone with read access to the repo (including forks) can
30
+ read these credentials
31
+ test:
32
+ kind: none
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brainblast",
3
- "version": "0.4.0",
3
+ "version": "0.4.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": [