blueprint-tsa 1.0.4 → 1.1.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.
Files changed (44) hide show
  1. package/README.md +31 -0
  2. package/dist/cli.js +8 -16
  3. package/dist/commands/audit.d.ts +50 -17
  4. package/dist/commands/audit.js +113 -67
  5. package/dist/commands/bounce-check.d.ts +51 -7
  6. package/dist/commands/bounce-check.js +49 -74
  7. package/dist/commands/clean.d.ts +2 -1
  8. package/dist/commands/clean.js +7 -7
  9. package/dist/commands/command-utils.d.ts +34 -0
  10. package/dist/commands/command-utils.js +165 -0
  11. package/dist/commands/common-analyzer-args.d.ts +100 -0
  12. package/dist/commands/common-analyzer-args.js +89 -0
  13. package/dist/commands/create-checker.d.ts +11 -0
  14. package/dist/commands/create-checker.js +70 -0
  15. package/dist/commands/drain-check.d.ts +51 -21
  16. package/dist/commands/drain-check.js +63 -86
  17. package/dist/commands/opcode-info.d.ts +45 -15
  18. package/dist/commands/opcode-info.js +71 -46
  19. package/dist/commands/owner-hijack-check.d.ts +61 -11
  20. package/dist/commands/owner-hijack-check.js +76 -149
  21. package/dist/commands/replay-attack-check.d.ts +58 -6
  22. package/dist/commands/replay-attack-check.js +61 -74
  23. package/dist/commands/reproduce.d.ts +12 -2
  24. package/dist/commands/reproduce.js +20 -13
  25. package/dist/common/analyzer-wrapper.d.ts +20 -5
  26. package/dist/common/analyzer-wrapper.js +235 -22
  27. package/dist/common/analyzer.js +1 -1
  28. package/dist/common/build-utils.d.ts +1 -1
  29. package/dist/common/build-utils.js +10 -5
  30. package/dist/common/constants.d.ts +5 -3
  31. package/dist/common/constants.js +4 -2
  32. package/dist/common/opcode-extractor.d.ts +1 -0
  33. package/dist/common/opcode-extractor.js +2 -0
  34. package/dist/common/paths.d.ts +5 -0
  35. package/dist/common/paths.js +31 -8
  36. package/dist/common/result-parsing.d.ts +2 -0
  37. package/dist/common/result-parsing.js +22 -10
  38. package/dist/reproduce/build-config.d.ts +1 -1
  39. package/dist/reproduce/build-config.js +3 -1
  40. package/dist/reproduce/concrete-analysis.d.ts +3 -2
  41. package/dist/reproduce/concrete-analysis.js +3 -4
  42. package/dist/reproduce/reproduce-config.d.ts +2 -0
  43. package/dist/reproduce/reproduce-config.js +2 -0
  44. package/package.json +2 -2
package/README.md CHANGED
@@ -0,0 +1,31 @@
1
+ # blueprint-tsa
2
+
3
+ A plugin for the [Blueprint Framework](https://github.com/ton-org/blueprint/) that simplifies your workflow with the [TON Symbolic Analyzer](https://tonsec.dev/).
4
+
5
+ ## Getting Started
6
+
7
+ 1. Add this plugin as a dependency of your Blueprint project:
8
+
9
+ ```bash
10
+ yarn add blueprint-tsa
11
+ ```
12
+
13
+ 2. Add this configuration to `blueprint.config.ts`:
14
+
15
+ ```ts
16
+ import { TsaPlugin } from "blueprint-tsa";
17
+
18
+ export const config = {
19
+ plugins: [new TsaPlugin()],
20
+ };
21
+ ```
22
+
23
+ ## Usage
24
+
25
+ Run the following command:
26
+
27
+ ```bash
28
+ yarn blueprint tsa help
29
+ ```
30
+
31
+ More more information, visit [documentation](https://tonsec.dev/docs).
package/dist/cli.js CHANGED
@@ -19,24 +19,16 @@ const bounce_check_js_1 = require("./commands/bounce-check.js");
19
19
  const createCLI = (context) => {
20
20
  const { args, ui } = context;
21
21
  const argv = args._.slice(1);
22
- const drainCheckConfig = (0, drain_check_js_1.configureDrainCheckCommand)(context);
23
- const replayAttackCheckConfig = (0, replay_attack_check_js_1.configureReplayAttackCheckCommand)(context);
24
- const cleanConfig = (0, clean_js_1.configureCleanCommand)();
25
- const reproduceCommand = (0, reproduce_js_1.configureReproduceCommand)(context);
26
- const ownerHijackConfig = (0, owner_hijack_check_js_1.configureOwnerHijackCommand)(context);
27
- const opcodeInfoConfig = (0, opcode_info_js_1.configureOpcodeInfoCommand)(context);
28
- const auditConfig = (0, audit_js_1.configureAuditCommand)(context);
29
- const bounceCheckConfig = (0, bounce_check_js_1.configureBounceCheckCommand)(context);
30
22
  return (0, yargs_1.default)(argv)
31
23
  .scriptName("tsa")
32
- .command(drainCheckConfig.command, drainCheckConfig.description, drainCheckConfig.builder, drainCheckConfig.handler)
33
- .command(replayAttackCheckConfig.command, replayAttackCheckConfig.description, replayAttackCheckConfig.builder, replayAttackCheckConfig.handler)
34
- .command(cleanConfig.command, cleanConfig.description, cleanConfig.builder, cleanConfig.handler)
35
- .command(ownerHijackConfig.command, ownerHijackConfig.description, ownerHijackConfig.builder, ownerHijackConfig.handler)
36
- .command(reproduceCommand.command, reproduceCommand.description, reproduceCommand.builder, reproduceCommand.handler)
37
- .command(opcodeInfoConfig.command, opcodeInfoConfig.description, opcodeInfoConfig.builder, opcodeInfoConfig.handler)
38
- .command(auditConfig.command, auditConfig.description, auditConfig.builder, auditConfig.handler)
39
- .command(bounceCheckConfig.command, bounceCheckConfig.description, bounceCheckConfig.builder, bounceCheckConfig.handler)
24
+ .command((0, drain_check_js_1.createDrainCheckCommand)(context))
25
+ .command((0, owner_hijack_check_js_1.createOwnerHijackCheckCommand)(context))
26
+ .command((0, replay_attack_check_js_1.createReplayAttackCheckCommand)(context))
27
+ .command((0, bounce_check_js_1.createBounceCheckCommand)(context))
28
+ .command((0, clean_js_1.createCleanCommand)())
29
+ .command((0, reproduce_js_1.createReproduceCommand)(context))
30
+ .command((0, opcode_info_js_1.createOpcodeInfoCommand)(context))
31
+ .command((0, audit_js_1.createAuditCommand)(context))
40
32
  .demandCommand(1, "Please specify a subcommand")
41
33
  .help()
42
34
  .alias("help", "h")
@@ -1,19 +1,52 @@
1
- import { Argv } from "yargs";
2
- import yargs from "yargs";
1
+ import { CommandModule, InferredOptionTypes } from "yargs";
3
2
  import { CommandContext } from "../cli.js";
4
- export declare const configureAuditCommand: (context: CommandContext) => {
5
- command: string;
6
- description: string;
7
- builder: (yargs: Argv) => Argv<{
8
- contract: string;
9
- } & {
10
- timeout: number | undefined;
11
- } & {
12
- "owner-method": string | undefined;
13
- } & {
14
- "disable-opcode-extraction": boolean | undefined;
15
- } & {
16
- verbose: boolean | undefined;
17
- }>;
18
- handler: (argv: yargs.ArgumentsCamelCase) => Promise<void>;
3
+ declare const auditOptions: {
4
+ readonly "disable-opcode-extraction": {
5
+ readonly type: "boolean";
6
+ readonly description: "Disable opcode extraction. This affects path selection strategy and default timeout.";
7
+ };
8
+ readonly timeout: {
9
+ readonly alias: "t";
10
+ readonly type: "number";
11
+ readonly description: "Overall analysis timeout in seconds";
12
+ };
13
+ readonly verbose: {
14
+ readonly alias: "v";
15
+ readonly type: "boolean";
16
+ readonly description: "Use debug output in TSA log";
17
+ };
18
+ readonly contract: {
19
+ readonly alias: "c";
20
+ readonly type: "string";
21
+ readonly description: "Contract name";
22
+ readonly demandOption: true;
23
+ };
24
+ readonly "iteration-limit": {
25
+ readonly type: "number";
26
+ readonly description: "Iteration limit";
27
+ readonly default: 2;
28
+ };
29
+ readonly "max-recursion-depth": {
30
+ readonly type: "number";
31
+ readonly description: "Recursion limit";
32
+ readonly default: 1;
33
+ };
34
+ readonly interactive: {
35
+ readonly type: "boolean";
36
+ readonly default: true;
37
+ readonly description: "Enable interactive confirmations";
38
+ };
39
+ readonly "verbose-analysis-artifacts": {
40
+ readonly type: "boolean";
41
+ readonly default: false;
42
+ readonly description: "Keep TSA exported inputs in the verbose multi-file directory layout";
43
+ };
44
+ readonly "owner-method": {
45
+ readonly alias: "m";
46
+ readonly type: "string";
47
+ readonly description: "The method name of get_owner getter (optional, enables owner hijack check)";
48
+ };
19
49
  };
50
+ type AuditSchema = InferredOptionTypes<typeof auditOptions>;
51
+ export declare const createAuditCommand: (context: CommandContext) => CommandModule<object, AuditSchema>;
52
+ export {};
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.configureAuditCommand = void 0;
6
+ exports.createAuditCommand = void 0;
7
7
  const fs_1 = require("fs");
8
8
  const path_1 = __importDefault(require("path"));
9
9
  const constants_js_1 = require("../common/constants.js");
@@ -18,7 +18,16 @@ const replay_attack_check_js_1 = require("./replay-attack-check.js");
18
18
  const owner_hijack_check_js_1 = require("./owner-hijack-check.js");
19
19
  const bounce_check_js_1 = require("./bounce-check.js");
20
20
  const opcode_info_js_1 = require("./opcode-info.js");
21
- const ONE_MINUTE_SECONDS = 60;
21
+ const common_analyzer_args_js_1 = require("./common-analyzer-args.js");
22
+ const command_utils_js_1 = require("./command-utils.js");
23
+ const auditOptions = {
24
+ "owner-method": {
25
+ alias: "m",
26
+ type: "string",
27
+ description: "The method name of get_owner getter (optional, enables owner hijack check)",
28
+ },
29
+ ...common_analyzer_args_js_1.commonAnalyzerRecvInternalCliOptions,
30
+ };
22
31
  function getCheckCommand(checkName, contractName, timeout, ownerMethod) {
23
32
  let commandId;
24
33
  if (checkName === constants_js_1.DRAIN_CHECK_NAME) {
@@ -69,16 +78,18 @@ function buildCheckResult(checkName, analyzer, passedMessage, failedMessage, con
69
78
  checkCommand: getCheckCommand(checkName, contractName, timeout, ownerMethod),
70
79
  };
71
80
  if (vulnerability) {
72
- const vulnDesc = analyzer.getVulnerability();
81
+ const vulnDesc = analyzer.getVulnerabilityFromReport();
73
82
  if (vulnDesc) {
74
- result.vulnerabilityPath = (0, paths_js_1.getInputsPath)(analyzer.id, vulnDesc.executionIndex);
83
+ result.vulnerabilityPath = analyzer.usesVerboseAnalysisArtifacts()
84
+ ? (0, paths_js_1.getInputsPath)(analyzer.id, vulnDesc.executionIndex)
85
+ : (0, paths_js_1.getCompactTypedInputPath)(analyzer.id);
75
86
  result.analyzerId = analyzer.id;
76
87
  }
77
88
  result.descriptionUrl = getCheckDescriptionUrl(checkName);
78
89
  }
79
90
  return result;
80
91
  }
81
- async function runOpcodeInfoCheck(contractName, contractPath, ui, timeout, opcodes, verbose) {
92
+ async function runOpcodeInfoCheck(contractName, contractPath, ui, timeout, opcodes, verbose, legacyAnalysisArtifacts, iterationLimit, recursionLimit, interactive) {
82
93
  // Calculate timeout per opcode
83
94
  let opcodeTimeout = null;
84
95
  if (timeout !== null && opcodes.length > 0) {
@@ -86,31 +97,48 @@ async function runOpcodeInfoCheck(contractName, contractPath, ui, timeout, opcod
86
97
  ui.write("");
87
98
  ui.write(`Opcode authorization check: using timeout of ${opcodeTimeout} seconds per opcode (${timeout} / ${opcodes.length})`);
88
99
  }
100
+ const commonArgs = {
101
+ timeout: opcodeTimeout,
102
+ verbose,
103
+ contract: contractName,
104
+ iterationLimit,
105
+ recursionLimit,
106
+ interactive,
107
+ legacyAnalysisArtifacts,
108
+ };
89
109
  const results = [];
90
110
  for (const opcode of opcodes) {
91
- const info = await (0, opcode_info_js_1.runOpcodeAuthorizationCheckAnalysis)(opcode, contractName, contractPath, ui, opcodeTimeout, `Authorization check for ${(0, format_utils_js_1.formatOpcodeHex)(opcode)} completed.`, verbose);
111
+ const info = await (0, opcode_info_js_1.runOpcodeAuthorizationCheckAnalysis)(opcode, contractPath, ui, commonArgs, `Authorization check for ${(0, format_utils_js_1.formatOpcodeHex)(opcode)} completed.`);
92
112
  if (info !== null) {
93
113
  results.push(info);
94
114
  }
95
115
  }
96
116
  return results;
97
117
  }
98
- async function runDrainCheck(contractName, contractPath, ui, timeout, opcodes, verbose) {
99
- const analyzer = await (0, drain_check_js_1.runDrainCheckAnalysis)(contractName, contractPath, ui, timeout, opcodes, verbose, `${constants_js_1.DRAIN_CHECK_NAME} completed.`);
100
- return buildCheckResult(constants_js_1.DRAIN_CHECK_NAME, analyzer, "No drain vulnerabilities detected", "Vulnerability found - contract may be vulnerable to drain attacks", contractName, timeout);
118
+ async function runDrainCheck(contractName, contractPath, ui, commonArgs) {
119
+ const analyzer = await (0, drain_check_js_1.runDrainCheckAnalysis)(ui, contractPath, commonArgs, `${constants_js_1.DRAIN_CHECK_NAME} completed.`);
120
+ return buildCheckResult(constants_js_1.DRAIN_CHECK_NAME, analyzer, "No drain vulnerabilities detected", "Vulnerability found - contract may be vulnerable to drain attacks", contractName, commonArgs.timeout);
101
121
  }
102
- async function runReplayAttackCheck(contractName, contractPath, ui, timeout, verbose) {
103
- const analyzer = await (0, replay_attack_check_js_1.runReplayAttackCheckAnalysis)(contractName, contractPath, ui, timeout, verbose, `${constants_js_1.REPLAY_ATTACK_CHECK_NAME} completed.`);
122
+ async function runReplayAttackCheck(contractName, contractPath, ui, timeout, verbose, iterationLimit, recursionLimit, legacyAnalysisArtifacts, interactive) {
123
+ const analyzer = await (0, replay_attack_check_js_1.runReplayAttackCheckAnalysis)(ui, contractPath, {
124
+ timeout,
125
+ verbose,
126
+ contract: contractName,
127
+ iterationLimit,
128
+ recursionLimit,
129
+ interactive,
130
+ legacyAnalysisArtifacts,
131
+ }, null, `${constants_js_1.REPLAY_ATTACK_CHECK_NAME} completed.`);
104
132
  return buildCheckResult(constants_js_1.REPLAY_ATTACK_CHECK_NAME, analyzer, "No replay attack vulnerabilities detected", "Vulnerability found - contract may be vulnerable to replay attacks", contractName, timeout);
105
133
  }
106
- async function runOwnerHijackCheck(contractName, contractPath, ui, timeout, methodName, opcodes, verbose) {
134
+ async function runOwnerHijackCheck(contractName, contractPath, ui, methodName, commonArgs) {
107
135
  const methodId = BigInt((0, core_1.getMethodId)(methodName));
108
- const analyzer = await (0, owner_hijack_check_js_1.runOwnerHijackCheckAnalysis)(contractName, contractPath, ui, timeout, methodId, opcodes, verbose, `${constants_js_1.OWNER_HIJACK_CHECK_NAME} completed.`);
109
- return buildCheckResult(constants_js_1.OWNER_HIJACK_CHECK_NAME, analyzer, "No owner hijack vulnerabilities detected", "Vulnerability found - contract owner may be hijackable", contractName, timeout, methodName);
136
+ const analyzer = await (0, owner_hijack_check_js_1.runOwnerHijackCheckAnalysis)(ui, contractPath, methodId, commonArgs, `${constants_js_1.OWNER_HIJACK_CHECK_NAME} completed.`);
137
+ return buildCheckResult(constants_js_1.OWNER_HIJACK_CHECK_NAME, analyzer, "No owner hijack vulnerabilities detected", "Vulnerability found - contract owner may be hijackable", contractName, commonArgs.timeout, methodName);
110
138
  }
111
- async function runBounceCheck(contractName, contractPath, ui, timeout, opcodes, verbose) {
112
- const analyzer = await (0, bounce_check_js_1.runBounceCheckAnalysis)(contractName, contractPath, ui, timeout, opcodes, verbose, `${constants_js_1.BOUNCE_CHECK_NAME} completed.`);
113
- return buildCheckResult(constants_js_1.BOUNCE_CHECK_NAME, analyzer, "No bounce message handling vulnerabilities detected", "Vulnerability found - contract may not handle bounced messages correctly", contractName, timeout);
139
+ async function runBounceCheck(contractName, contractPath, ui, commonArgs) {
140
+ const analyzer = await (0, bounce_check_js_1.runBounceCheckAnalysis)(ui, contractPath, commonArgs, `${constants_js_1.BOUNCE_CHECK_NAME} completed.`);
141
+ return buildCheckResult(constants_js_1.BOUNCE_CHECK_NAME, analyzer, "No bounce message handling vulnerabilities detected", "Vulnerability found - contract may not handle bounced messages correctly", contractName, commonArgs.timeout);
114
142
  }
115
143
  function buildAuditReport(summary) {
116
144
  const lines = [];
@@ -191,61 +219,104 @@ function saveAuditReport(summary, ui) {
191
219
  ui.write(`${constants_js_1.Sym.OK} Report saved to: ${filePath}`);
192
220
  return filePath;
193
221
  }
194
- const auditHandler = async (context, args) => {
195
- const { ui } = context;
196
- const { timeout, contract, ownerMethod, disableOpcodeExtraction, verbose } = args;
197
- await (0, build_utils_js_1.buildContracts)(ui);
198
- const contractPath = (0, paths_js_1.findCompiledContract)(contract);
222
+ const auditCommand = async (ui, parsedArgs) => {
223
+ const contractName = parsedArgs.contract;
224
+ const ownerMethod = parsedArgs["owner-method"];
225
+ const disableOpcodeExtraction = parsedArgs["disable-opcode-extraction"];
226
+ const verbose = parsedArgs.verbose;
227
+ await (0, build_utils_js_1.buildAllContracts)(ui, parsedArgs.interactive);
228
+ const contractPath = (0, paths_js_1.findCompiledContract)(contractName);
199
229
  if (!(0, fs_1.existsSync)(contractPath)) {
200
- ui.write(`\n${constants_js_1.Sym.ERR} Contract ${contract} not found`);
230
+ ui.write(`\n${constants_js_1.Sym.ERR} Contract ${contractName} not found`);
201
231
  process.exit(1);
202
232
  }
233
+ const checkCount = ownerMethod ? 5 : 4;
234
+ const explicitTimeout = parsedArgs.timeout ?? null;
235
+ const hasUserProvidedTimeout = (0, command_utils_js_1.hasExplicitTimeout)(explicitTimeout);
236
+ if (hasUserProvidedTimeout && explicitTimeout !== null) {
237
+ await (0, command_utils_js_1.confirmLongRunningAnalysis)(ui, {
238
+ commandLabel: constants_js_1.AUDIT_ID,
239
+ contractName,
240
+ timeoutSeconds: explicitTimeout * checkCount,
241
+ checkCount,
242
+ interactive: parsedArgs.interactive,
243
+ });
244
+ }
203
245
  // Extract opcodes if not disabled
204
246
  let opcodes = [];
205
247
  if (!disableOpcodeExtraction) {
248
+ if (!hasUserProvidedTimeout) {
249
+ await (0, command_utils_js_1.confirmOpcodeExtractionWait)(ui, {
250
+ commandLabel: constants_js_1.AUDIT_ID,
251
+ contractName,
252
+ interactive: parsedArgs.interactive,
253
+ });
254
+ }
206
255
  opcodes = await (0, opcode_extractor_js_1.extractOpcodes)({
207
256
  ui,
208
257
  codePath: contractPath,
209
- contractName: contract,
258
+ contractName,
259
+ interactive: parsedArgs.interactive,
210
260
  });
211
261
  }
212
262
  // Calculate timeout if not provided
213
263
  // Timeout is per analyzer run (except for opcode-info where it's divided by number of opcodes)
214
- let effectiveTimeout = timeout ?? null;
264
+ let effectiveTimeout = parsedArgs.timeout ?? null;
215
265
  if (effectiveTimeout === null && opcodes.length > 0) {
216
- effectiveTimeout = ONE_MINUTE_SECONDS * (opcodes.length + 1);
266
+ effectiveTimeout = command_utils_js_1.ONE_MINUTE_SECONDS * (opcodes.length + 1);
217
267
  ui.write("");
218
268
  ui.write(`Timeout was calculated automatically: ${effectiveTimeout} seconds per analyzer run (based on ${opcodes.length} opcodes)`);
219
269
  }
270
+ const totalEstimatedTimeout = effectiveTimeout === null ? null : effectiveTimeout * checkCount;
271
+ if (!hasUserProvidedTimeout) {
272
+ await (0, command_utils_js_1.confirmLongRunningAnalysis)(ui, {
273
+ commandLabel: constants_js_1.AUDIT_ID,
274
+ contractName,
275
+ timeoutSeconds: totalEstimatedTimeout,
276
+ opcodeCount: opcodes.length,
277
+ checkCount,
278
+ interactive: parsedArgs.interactive,
279
+ });
280
+ }
220
281
  const summary = {
221
- contract: contract,
282
+ contract: contractName,
222
283
  checks: [],
223
284
  opcodeInfo: [],
224
285
  };
225
286
  // Run opcode-info check
226
287
  ui.write("");
227
- ui.write(`${constants_js_1.Sym.WAIT} Running opcode authorization analysis...`);
228
- summary.opcodeInfo = await runOpcodeInfoCheck(contract, contractPath, ui, effectiveTimeout, opcodes, verbose);
288
+ ui.write(`${constants_js_1.Sym.WAIT} Step 1/${checkCount}: running opcode authorization analysis...`);
289
+ summary.opcodeInfo = await runOpcodeInfoCheck(contractName, contractPath, ui, effectiveTimeout, opcodes, verbose ?? false, parsedArgs[common_analyzer_args_js_1.VERBOSE_ANALYSIS_ARTIFACTS_OPTION], parsedArgs[common_analyzer_args_js_1.ITERATION_LIMIT_OPTION], parsedArgs[common_analyzer_args_js_1.RECURSION_LIMIT_OPTION], parsedArgs.interactive);
229
290
  // Run drain-check
230
291
  ui.write("");
231
- ui.write(`${constants_js_1.Sym.WAIT} Running drain check...`);
232
- const drainResult = await runDrainCheck(contract, contractPath, ui, effectiveTimeout, opcodes, verbose);
292
+ ui.write(`${constants_js_1.Sym.WAIT} Step 2/${checkCount}: running drain check...`);
293
+ const commonArgs = {
294
+ timeout: effectiveTimeout,
295
+ opcodes,
296
+ verbose,
297
+ contract: contractName,
298
+ iterationLimit: parsedArgs[common_analyzer_args_js_1.ITERATION_LIMIT_OPTION],
299
+ recursionLimit: parsedArgs[common_analyzer_args_js_1.RECURSION_LIMIT_OPTION],
300
+ interactive: parsedArgs.interactive,
301
+ legacyAnalysisArtifacts: parsedArgs[common_analyzer_args_js_1.VERBOSE_ANALYSIS_ARTIFACTS_OPTION],
302
+ };
303
+ const drainResult = await runDrainCheck(contractName, contractPath, ui, commonArgs);
233
304
  summary.checks.push(drainResult);
234
305
  // Run replay-attack-check
235
306
  ui.write("");
236
- ui.write(`${constants_js_1.Sym.WAIT} Running replay attack check...`);
237
- const replayResult = await runReplayAttackCheck(contract, contractPath, ui, effectiveTimeout, verbose);
307
+ ui.write(`${constants_js_1.Sym.WAIT} Step 3/${checkCount}: running replay attack check...`);
308
+ const replayResult = await runReplayAttackCheck(contractName, contractPath, ui, effectiveTimeout, verbose ?? false, commonArgs.iterationLimit, commonArgs.recursionLimit, commonArgs.legacyAnalysisArtifacts ?? false, parsedArgs.interactive);
238
309
  summary.checks.push(replayResult);
239
310
  // Run bounce-check
240
311
  ui.write("");
241
- ui.write(`${constants_js_1.Sym.WAIT} Running bounce check...`);
242
- const bounceResult = await runBounceCheck(contract, contractPath, ui, effectiveTimeout, opcodes, verbose);
312
+ ui.write(`${constants_js_1.Sym.WAIT} Step 4/${checkCount}: running bounce check...`);
313
+ const bounceResult = await runBounceCheck(contractName, contractPath, ui, commonArgs);
243
314
  summary.checks.push(bounceResult);
244
315
  // Run owner-hijack-check if owner method is provided
245
316
  if (ownerMethod) {
246
317
  ui.write("");
247
- ui.write(`${constants_js_1.Sym.WAIT} Running owner hijack check...`);
248
- const ownerResult = await runOwnerHijackCheck(contract, contractPath, ui, effectiveTimeout, ownerMethod, opcodes, verbose);
318
+ ui.write(`${constants_js_1.Sym.WAIT} Step 5/${checkCount}: running owner hijack check...`);
319
+ const ownerResult = await runOwnerHijackCheck(contractName, contractPath, ui, ownerMethod, commonArgs);
249
320
  summary.checks.push(ownerResult);
250
321
  }
251
322
  else {
@@ -258,39 +329,14 @@ const auditHandler = async (context, args) => {
258
329
  saveAuditReport(summary, ui);
259
330
  (0, utils_js_1.printCleanupInstructions)(ui);
260
331
  };
261
- const configureAuditCommand = (context) => {
332
+ const createAuditCommand = (context) => {
262
333
  return {
263
334
  command: constants_js_1.AUDIT_ID,
264
- description: "Run all available security checks and print a summary",
265
- builder: (yargs) => yargs
266
- .option("contract", {
267
- alias: "c",
268
- type: "string",
269
- description: "Contract name",
270
- demandOption: true,
271
- })
272
- .option("timeout", {
273
- alias: "t",
274
- type: "number",
275
- description: "Analysis timeout in seconds per analyzer run (for opcode-info, divided by number of opcodes)",
276
- })
277
- .option("owner-method", {
278
- alias: "m",
279
- type: "string",
280
- description: "The method name of get_owner getter (optional, enables owner hijack check)",
281
- })
282
- .option("disable-opcode-extraction", {
283
- type: "boolean",
284
- description: "Disable opcode extraction. This affects path selection strategy and default timeout.",
285
- })
286
- .option("verbose", {
287
- alias: "v",
288
- type: "boolean",
289
- description: "Use debug output in TSA log",
290
- }),
335
+ describe: "Run all available security checks and print a summary",
336
+ builder: auditOptions,
291
337
  handler: async (argv) => {
292
- await auditHandler(context, argv);
338
+ await auditCommand(context.ui, argv);
293
339
  },
294
340
  };
295
341
  };
296
- exports.configureAuditCommand = configureAuditCommand;
342
+ exports.createAuditCommand = createAuditCommand;
@@ -1,15 +1,59 @@
1
+ import { CommandModule, InferredOptionTypes } from "yargs";
1
2
  import { UIProvider } from "@ton/blueprint";
2
3
  import { CommandContext } from "../cli.js";
3
4
  import { AnalyzerWrapper } from "../common/analyzer-wrapper.js";
4
- export declare const configureBounceCheckCommand: (context: CommandContext) => any;
5
+ import { CommonAnalyzerRecvInternalArgs } from "./common-analyzer-args.js";
6
+ declare const bounceCheckOptions: {
7
+ readonly "disable-opcode-extraction": {
8
+ readonly type: "boolean";
9
+ readonly description: "Disable opcode extraction. This affects path selection strategy and default timeout.";
10
+ };
11
+ readonly timeout: {
12
+ readonly alias: "t";
13
+ readonly type: "number";
14
+ readonly description: "Overall analysis timeout in seconds";
15
+ };
16
+ readonly verbose: {
17
+ readonly alias: "v";
18
+ readonly type: "boolean";
19
+ readonly description: "Use debug output in TSA log";
20
+ };
21
+ readonly contract: {
22
+ readonly alias: "c";
23
+ readonly type: "string";
24
+ readonly description: "Contract name";
25
+ readonly demandOption: true;
26
+ };
27
+ readonly "iteration-limit": {
28
+ readonly type: "number";
29
+ readonly description: "Iteration limit";
30
+ readonly default: 2;
31
+ };
32
+ readonly "max-recursion-depth": {
33
+ readonly type: "number";
34
+ readonly description: "Recursion limit";
35
+ readonly default: 1;
36
+ };
37
+ readonly interactive: {
38
+ readonly type: "boolean";
39
+ readonly default: true;
40
+ readonly description: "Enable interactive confirmations";
41
+ };
42
+ readonly "verbose-analysis-artifacts": {
43
+ readonly type: "boolean";
44
+ readonly default: false;
45
+ readonly description: "Keep TSA exported inputs in the verbose multi-file directory layout";
46
+ };
47
+ };
48
+ type BounceCheckSchema = InferredOptionTypes<typeof bounceCheckOptions>;
49
+ export declare const createBounceCheckCommand: (context: CommandContext) => CommandModule<object, BounceCheckSchema>;
5
50
  /**
6
51
  * Runs bounce check analysis and returns the analyzer wrapper
7
- * @param contractName - Name of the contract
8
- * @param contractPath - Path to the compiled contract
9
52
  * @param ui - UI provider
10
- * @param timeout - Analysis timeout in seconds
11
- * @param opcodes - List of opcodes to analyze
12
- * @param verbose - Enable verbose output
53
+ * @param contractPath - Path to the compiled contract
54
+ * @param commonArgs
55
+ * @param completionMessage
13
56
  * @returns AnalyzerWrapper instance
14
57
  */
15
- export declare const runBounceCheckAnalysis: (contractName: string, contractPath: string, ui: UIProvider, timeout: number | null, opcodes: number[], verbose?: boolean, completionMessage?: string) => Promise<AnalyzerWrapper>;
58
+ export declare const runBounceCheckAnalysis: (ui: UIProvider, contractPath: string, commonArgs: CommonAnalyzerRecvInternalArgs, completionMessage?: string) => Promise<AnalyzerWrapper>;
59
+ export {};